Skip to content

Commit

Permalink
Uma (#161)
Browse files Browse the repository at this point in the history
* Implement UMA authorization, only non-interactive (apis) flow

* Solve pat race

* Run only benchmarks not tests in benchmark phase
  • Loading branch information
p53 authored Jun 15, 2022
1 parent fa347d3 commit 83dcaf7
Show file tree
Hide file tree
Showing 12 changed files with 800 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
# Run Go benchmarks
- name: Benchmark
run: |
go test -bench=. -benchmem
go test -run=^$ -bench=. -benchmem
# Sends code coverage report to Coveralls
- name: Coveralls
env:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,4 @@ _testmain.go
*.test
*.prof
__debug_bin
.env
100 changes: 97 additions & 3 deletions common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ type DefaultTestTokenClaims struct {
Item1 []string `json:"item1"`
Item2 []string `json:"item2"`
Item3 []string `json:"item3"`
Authorization Permissions `json:"authorization"`
}

var defTestTokenClaims = DefaultTestTokenClaims{
Expand Down Expand Up @@ -111,6 +112,15 @@ var defTestTokenClaims = DefaultTestTokenClaims{
Item1: []string{"default"},
Item2: []string{"default"},
Item3: []string{"default"},
Authorization: Permissions{
Permissions: []Permission{
{
Scopes: []string{"test"},
ResourceID: "6ef1b62e-0fd4-47f2-81fc-eead97a01c22",
ResourceName: "test-resource",
},
},
},
}

const (
Expand Down Expand Up @@ -140,6 +150,7 @@ type fakeRequest struct {
SkipIssuerCheck bool
RequestCA string
TokenClaims map[string]interface{}
TokenAuthorization *Permissions
URI string
URL string
Username string
Expand Down Expand Up @@ -183,6 +194,12 @@ func newFakeProxy(c *Config, authConfig *fakeAuthConfig) *fakeProxy {
c.DiscoveryURL = auth.getLocation()
c.Verbose = true
c.DisableAllLogging = true
err := c.update()

if err != nil {
panic("failed to create fake proxy service, error: " + err.Error())
}

proxy, err := newProxy(c)

if err != nil {
Expand Down Expand Up @@ -332,6 +349,10 @@ func (f *fakeProxy) RunTests(t *testing.T, requests []fakeRequest) {
token.setExpiration(time.Now().Add(c.Expires))
}

if c.TokenAuthorization != nil {
token.claims.Authorization = *c.TokenAuthorization
}

if c.NotSigned {
authToken, err := token.getUnsignedToken()
assert.NoError(t, err)
Expand Down Expand Up @@ -666,6 +687,11 @@ func newTestProxyService(config *Config) (*oauthProxy, *fakeAuthServer, string)
config.RevocationEndpoint = auth.getRevocationURL()
config.Verbose = false
config.EnableLogging = false
err := config.update()

if err != nil {
panic("failed to create proxy service, error: " + err.Error())
}

proxy, err := newProxy(config)
if err != nil {
Expand Down Expand Up @@ -1095,6 +1121,9 @@ func newFakeAuthServer(config *fakeAuthConfig) *fakeAuthServer {
r.Post("/auth/realms/hod-test/protocol/openid-connect/logout", service.logoutHandler)
r.Post("/auth/realms/hod-test/protocol/openid-connect/revoke", service.revocationHandler)
r.Post("/auth/realms/hod-test/protocol/openid-connect/token", service.tokenHandler)
r.Get("/auth/realms/hod-test/authz/protection/resource_set", service.ResourcesHandler)
r.Get("/auth/realms/hod-test/authz/protection/resource_set/{id}", service.ResourceHandler)
r.Post("/auth/realms/hod-test/authz/protection/permission", service.PermissionTicketHandler)

if config.EnableTLS {
service.server = httptest.NewTLSServer(r)
Expand Down Expand Up @@ -1280,8 +1309,14 @@ func (r *fakeAuthServer) tokenHandler(w http.ResponseWriter, req *http.Request)
clientSecret := req.FormValue("client_secret")

if clientID == "" || clientSecret == "" {
w.WriteHeader(http.StatusBadRequest)
return
u, p, ok := req.BasicAuth()
clientID = u
clientSecret = p

if clientID == "" || clientSecret == "" || !ok {
w.WriteHeader(http.StatusBadRequest)
return
}
}

if clientID == validUsername && clientSecret == validPassword {
Expand All @@ -1296,7 +1331,7 @@ func (r *fakeAuthServer) tokenHandler(w http.ResponseWriter, req *http.Request)

renderJSON(http.StatusUnauthorized, w, req, map[string]string{
"error": "invalid_grant",
"error_description": "invalid user credentials",
"error_description": "invalid client credentials",
})
case GrantTypeRefreshToken:
oldRefreshToken, err := jwt.ParseSigned(req.FormValue("refresh_token"))
Expand Down Expand Up @@ -1354,6 +1389,65 @@ func (r *fakeAuthServer) tokenHandler(w http.ResponseWriter, req *http.Request)
}
}

func (r *fakeAuthServer) ResourcesHandler(w http.ResponseWriter, req *http.Request) {
response := []string{"6ef1b62e-0fd4-47f2-81fc-eead97a01c22"}
renderJSON(http.StatusOK, w, req, response)
}

func (r *fakeAuthServer) ResourceHandler(w http.ResponseWriter, req *http.Request) {
type Resource struct {
Name string `json:"name"`
Type string `json:"type"`
Owner struct{ ID string } `json:"owner"`
OwnerManagedAccess bool `json:"ownerManagedAccess"`
Attributes struct{} `json:"attributes"`
ID string `json:"_id"`
URIS []string `json:"uris"`
ResourceScopes []struct {
Name string `json:"name"`
} `json:"resource_scopes"`
Scopes []struct {
Name string `json:"name"`
} `json:"scopes"`
}

response := Resource{
Name: "Default Resource",
Type: "urn:test-client:resources:default",
Owner: struct{ ID string }{ID: "6ef1b62e-0fd4-47f2-81fc-eead97a01c22"},
OwnerManagedAccess: false,
Attributes: struct{}{},
ID: "6ef1b62e-0fd4-47f2-81fc-eead97a01c22",
URIS: []string{"/*"},
ResourceScopes: []struct {
Name string `json:"name"`
}{{Name: "test"}},
Scopes: []struct {
Name string `json:"name"`
}{{Name: "test"}},
}
renderJSON(http.StatusOK, w, req, response)
}

func (r *fakeAuthServer) PermissionTicketHandler(w http.ResponseWriter, req *http.Request) {
token := newTestToken(r.getLocation())
acc, err := token.getToken()

if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}

type Ticket struct {
Ticket string `json:"ticket"`
}

response := Ticket{
Ticket: acc,
}
renderJSON(http.StatusOK, w, req, response)
}

func getRandomString(n int) (string, error) {
b := make([]rune, n)
for i := range b {
Expand Down
64 changes: 64 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ func newDefaultConfig() *Config {
UpstreamTimeout: 10 * time.Second,
UseLetsEncrypt: false,
ForwardingGrantType: GrantTypeUserCreds,
PatRetryCount: 5,
PatRetryInterval: 10 * time.Second,
PatRefreshInterval: 33 * time.Second,
}
}

Expand All @@ -88,6 +91,21 @@ func (r *Config) WithOAuthURI(uri string) string {
return fmt.Sprintf("%s/%s", r.OAuthURI, uri)
}

func (r *Config) update() error {
updateRegistry := []func() error{
r.updateDiscoveryURI,
r.updateRealm,
}

for _, updateFunc := range updateRegistry {
if err := updateFunc(); err != nil {
return err
}
}

return nil
}

// isValid validates if the config is valid
func (r *Config) isValid() error {
if r.ListenAdmin == r.Listen {
Expand Down Expand Up @@ -323,6 +341,7 @@ func (r *Config) isReverseProxySettingsValid() error {
if !r.EnableForwarding {
validationRegistry := []func() error{
r.isUpstreamValid,
r.isEnableUmaValid,
r.isTokenVerificationSettingsValid,
r.isResourceValid,
r.isMatchClaimValid,
Expand Down Expand Up @@ -535,3 +554,48 @@ func (r *Config) isMatchClaimValid() error {

return nil
}

func (r *Config) isEnableUmaValid() error {
if r.EnableUma {
if r.ClientID == "" || r.ClientSecret == "" {
return errors.New(
"enable uma requires client credentials",
)
}
}
return nil
}

func (r *Config) updateDiscoveryURI() error {
// step: fix up the url if required, the underlining lib will add
// the .well-known/openid-configuration to the discovery url for us.
r.DiscoveryURL = strings.TrimSuffix(
r.DiscoveryURL,
"/.well-known/openid-configuration",
)

u, err := url.ParseRequestURI(r.DiscoveryURL)

if err != nil {
return fmt.Errorf(
"failed to parse discovery url: %w",
err,
)
}

r.DiscoveryURI = u

return nil
}

func (r *Config) updateRealm() error {
path := strings.Split(r.DiscoveryURI.Path, "/")

if len(path) != 4 {
return fmt.Errorf("missing realm in discovery url?")
}

realm := path[len(path)-1]
r.Realm = realm
return nil
}
Loading

0 comments on commit 83dcaf7

Please sign in to comment.