From ca23166e6eb946830c46172657100975a03a19dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Thu, 19 Sep 2019 09:32:51 +0200 Subject: [PATCH] Feature/ocisprovider use backend (#249) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * TODOS Signed-off-by: Jörn Friedrich Dreyer * oidcprovider: allow auth and user backend config * add todos and comments Signed-off-by: Jörn Friedrich Dreyer --- cmd/revad/svcs/httpsvcs/oidcprovider/auth.go | 37 +++-- cmd/revad/svcs/httpsvcs/oidcprovider/home.go | 3 + .../svcs/httpsvcs/oidcprovider/introspect.go | 6 +- .../httpsvcs/oidcprovider/oidcprovider.go | 142 ++++++++++-------- .../svcs/httpsvcs/oidcprovider/revoke.go | 10 +- .../svcs/httpsvcs/oidcprovider/sessions.go | 38 ++--- cmd/revad/svcs/httpsvcs/oidcprovider/token.go | 10 +- .../svcs/httpsvcs/oidcprovider/userinfo.go | 50 +++--- 8 files changed, 155 insertions(+), 141 deletions(-) diff --git a/cmd/revad/svcs/httpsvcs/oidcprovider/auth.go b/cmd/revad/svcs/httpsvcs/oidcprovider/auth.go index c0178b8a2d..fbafb284dd 100644 --- a/cmd/revad/svcs/httpsvcs/oidcprovider/auth.go +++ b/cmd/revad/svcs/httpsvcs/oidcprovider/auth.go @@ -22,7 +22,9 @@ import ( "fmt" "net/http" + typespb "github.com/cs3org/go-cs3apis/cs3/types" "github.com/cs3org/reva/pkg/appctx" + "github.com/cs3org/reva/pkg/user" ) func (s *svc) doAuth(w http.ResponseWriter, r *http.Request) { @@ -31,10 +33,10 @@ func (s *svc) doAuth(w http.ResponseWriter, r *http.Request) { // Let's create an AuthorizeRequest object! // It will analyze the request and extract important information like scopes, response type and others. - ar, err := oauth2.NewAuthorizeRequest(ctx, r) + ar, err := s.oauth2.NewAuthorizeRequest(ctx, r) if err != nil { log.Error().Err(err).Msg("Error occurred in NewAuthorizeRequest") - oauth2.WriteAuthorizeError(w, ar, err) + s.oauth2.WriteAuthorizeError(w, ar, err) return } // You have now access to authorizeRequest, Code ResponseTypes, Scopes ... @@ -48,11 +50,11 @@ func (s *svc) doAuth(w http.ResponseWriter, r *http.Request) { // We're simplifying things and just checking if the request includes a valid username and password if err := r.ParseForm(); err != nil { log.Error().Err(err).Msg("Error occurred parsing the form data") - oauth2.WriteAuthorizeError(w, ar, err) + s.oauth2.WriteAuthorizeError(w, ar, err) return } username := r.PostForm.Get("username") - password := "secret" + password := r.PostForm.Get("password") if username == "" { w.Header().Set("Content-Type", "text/html; charset=utf-8") _, err := w.Write([]byte(fmt.Sprintf(` @@ -63,19 +65,28 @@ func (s *svc) doAuth(w http.ResponseWriter, r *http.Request) { By logging in, you consent to grant these scopes:
    %s

- try aaliyah_abernathy, aaliyah_adams or aaliyah_anderson
+
+
`, requestedScopes))) if err != nil { log.Error().Err(err).Msg("Error writing response") - oauth2.WriteAuthorizeError(w, ar, err) + s.oauth2.WriteAuthorizeError(w, ar, err) } return } - // use reva sepcific implementation that uses existing auth managers - if err := store.Authenticate(ctx, username, password); err != nil { - oauth2.WriteAuthorizeError(w, ar, err) + actx, err := s.authmgr.Authenticate(ctx, username, password) + if err != nil { + s.oauth2.WriteAuthorizeError(w, ar, err) + } + uid, ok := user.ContextGetUserID(actx) + if !ok { + // try to look up user by username + // TODO log warning or should we fail? + uid = &typespb.UserId{ + OpaqueId: username, + } } // let's see what scopes the user gave consent to @@ -84,7 +95,7 @@ func (s *svc) doAuth(w http.ResponseWriter, r *http.Request) { } // Now that the user is authorized, we set up a session: - mySessionData := newSession(username, getSub(ctx, username)) + mySessionData := newSession(username, uid) // When using the HMACSHA strategy you must use something that implements the HMACSessionContainer. // It brings you the power of overriding the default values. @@ -110,7 +121,7 @@ func (s *svc) doAuth(w http.ResponseWriter, r *http.Request) { // Now we need to get a response. This is the place where the AuthorizeEndpointHandlers kick in and start processing the request. // NewAuthorizeResponse is capable of running multiple response type handlers which in turn enables this library // to support open id connect. - response, err := oauth2.NewAuthorizeResponse(ctx, ar, mySessionData) + response, err := s.oauth2.NewAuthorizeResponse(ctx, ar, mySessionData) // Catch any errors, e.g.: // * unknown client @@ -118,10 +129,10 @@ func (s *svc) doAuth(w http.ResponseWriter, r *http.Request) { // * ... if err != nil { log.Error().Err(err).Msg("Error occurred in NewAuthorizeResponse") - oauth2.WriteAuthorizeError(w, ar, err) + s.oauth2.WriteAuthorizeError(w, ar, err) return } // Last but not least, send the response! - oauth2.WriteAuthorizeResponse(w, ar, response) + s.oauth2.WriteAuthorizeResponse(w, ar, response) } diff --git a/cmd/revad/svcs/httpsvcs/oidcprovider/home.go b/cmd/revad/svcs/httpsvcs/oidcprovider/home.go index 47ba1d9591..5d5febf183 100644 --- a/cmd/revad/svcs/httpsvcs/oidcprovider/home.go +++ b/cmd/revad/svcs/httpsvcs/oidcprovider/home.go @@ -62,6 +62,9 @@ func (s *svc) doHome(w http.ResponseWriter, r *http.Request) { Make an invalid request `, + // TODO(jfd): make sure phoenix uses random state and nonce, see https://tools.ietf.org/html/rfc6819#section-4.4.1.8 + // - nonce vs jti https://security.stackexchange.com/a/188171 + // - state vs nonce https://stackoverflow.com/a/46859861 clientConf.AuthCodeURL("some-random-state-foobar")+"&nonce=some-random-nonce", "http://localhost:9998/oauth2/auth?client_id=my-client&redirect_uri=http%3A%2F%2Flocalhost%3A9998%2Fcallback&response_type=token%20id_token&scope=fosite%20openid&state=some-random-state-foobar&nonce=some-random-nonce", clientConf.AuthCodeURL("some-random-state-foobar")+"&nonce=some-random-nonce", diff --git a/cmd/revad/svcs/httpsvcs/oidcprovider/introspect.go b/cmd/revad/svcs/httpsvcs/oidcprovider/introspect.go index 2fa67ecb36..5ced6e5045 100644 --- a/cmd/revad/svcs/httpsvcs/oidcprovider/introspect.go +++ b/cmd/revad/svcs/httpsvcs/oidcprovider/introspect.go @@ -27,12 +27,12 @@ import ( func (s *svc) doIntrospect(w http.ResponseWriter, r *http.Request) { ctx := r.Context() log := appctx.GetLogger(ctx) - ir, err := oauth2.NewIntrospectionRequest(ctx, r, emptySession()) + ir, err := s.oauth2.NewIntrospectionRequest(ctx, r, emptySession()) if err != nil { log.Error().Err(err).Msg("Error occurred in NewIntrospectionRequest") - oauth2.WriteIntrospectionError(w, err) + s.oauth2.WriteIntrospectionError(w, err) return } - oauth2.WriteIntrospectionResponse(w, ir) + s.oauth2.WriteIntrospectionResponse(w, ir) } diff --git a/cmd/revad/svcs/httpsvcs/oidcprovider/oidcprovider.go b/cmd/revad/svcs/httpsvcs/oidcprovider/oidcprovider.go index 707ff9e1de..bfa9dcf13e 100644 --- a/cmd/revad/svcs/httpsvcs/oidcprovider/oidcprovider.go +++ b/cmd/revad/svcs/httpsvcs/oidcprovider/oidcprovider.go @@ -19,11 +19,9 @@ package oidcprovider import ( - "context" - "crypto/md5" "crypto/rand" "crypto/rsa" - "encoding/hex" + "fmt" "net/http" "time" @@ -33,9 +31,14 @@ import ( "github.com/ory/fosite/storage" "github.com/ory/fosite/token/jwt" + typespb "github.com/cs3org/go-cs3apis/cs3/types" "github.com/cs3org/reva/cmd/revad/httpserver" "github.com/cs3org/reva/cmd/revad/svcs/httpsvcs" "github.com/cs3org/reva/pkg/appctx" + "github.com/cs3org/reva/pkg/auth" + authmgr "github.com/cs3org/reva/pkg/auth/manager/registry" + "github.com/cs3org/reva/pkg/user" + usermgr "github.com/cs3org/reva/pkg/user/manager/registry" "github.com/mitchellh/mapstructure" ) @@ -44,20 +47,26 @@ func init() { } type config struct { - Prefix string `mapstructure:"prefix"` + Prefix string `mapstructure:"prefix"` + AuthManager string `mapstructure:"auth_manager"` + AuthManagers map[string]map[string]interface{} `mapstructure:"auth_managers"` + UserManager string `mapstructure:"user_manager"` + UserManagers map[string]map[string]interface{} `mapstructure:"user_managers"` } type svc struct { prefix string handler http.Handler + authmgr auth.Manager + usermgr user.Manager + store *storage.MemoryStore + oauth2 fosite.OAuth2Provider } -// This is an exemplary storage instance. We will add a client and a user to it so we can use these later on. -var store = newExampleStore() - func newExampleStore() *storage.MemoryStore { return &storage.MemoryStore{ IDSessions: make(map[string]fosite.Requester), + // TODO(jfd): read clients from a json file Clients: map[string]fosite.Client{ "phoenix": &fosite.DefaultClient{ ID: "phoenix", @@ -75,21 +84,6 @@ func newExampleStore() *storage.MemoryStore { Scopes: []string{"openid", "profile", "email", "offline"}, }, }, - // TODO implement reva specific user store that uses existing user managers - Users: map[string]storage.MemoryUserRelation{ - "aaliyah_abernathy": { - Username: "aaliyah_abernathy", - Password: "secret", - }, - "aaliyah_adams": { - Username: "aaliyah_adams", - Password: "secret", - }, - "aaliyah_anderson": { - Username: "aaliyah_anderson", - Password: "secret", - }, - }, AuthorizeCodes: map[string]storage.StoreAuthorizeCode{}, Implicit: map[string]fosite.Requester{}, AccessTokens: map[string]fosite.Requester{}, @@ -107,35 +101,13 @@ var fconfig = new(compose.Config) var start = compose.CommonStrategy{ // alternatively you could use: // OAuth2Strategy: compose.NewOAuth2JWTStrategy(mustRSAKey()) + // TODO(jfd): generate / read proper secret from config CoreStrategy: compose.NewOAuth2HMACStrategy(fconfig, []byte("some-super-cool-secret-that-nobody-knows"), nil), // open id connect strategy OpenIDConnectTokenStrategy: compose.NewOpenIDConnectStrategy(fconfig, mustRSAKey()), } -var oauth2 = compose.Compose( - fconfig, - store, - start, - nil, - - // enabled handlers - compose.OAuth2AuthorizeExplicitFactory, - compose.OAuth2AuthorizeImplicitFactory, - compose.OAuth2ClientCredentialsGrantFactory, - compose.OAuth2RefreshTokenGrantFactory, - compose.OAuth2ResourceOwnerPasswordCredentialsFactory, - - compose.OAuth2TokenRevocationFactory, - compose.OAuth2TokenIntrospectionFactory, - - // be aware that open id connect factories need to be added after oauth2 factories to work properly. - compose.OpenIDConnectExplicitFactory, - compose.OpenIDConnectImplicitFactory, - compose.OpenIDConnectHybridFactory, - compose.OpenIDConnectRefreshFactory, -) - // A session is passed from the `/auth` to the `/token` endpoint. You probably want to store data like: "Who made the request", // "What organization does that person belong to" and so on. // For our use case, the session will meet the requirements imposed by JWT access tokens, HMAC access tokens and OpenID Connect @@ -146,11 +118,11 @@ var oauth2 = compose.Compose( // Usually, you could do: // // session = new(fosite.DefaultSession) -func newSession(username string, sub string) *openid.DefaultSession { +func newSession(username string, uid *typespb.UserId) *openid.DefaultSession { return &openid.DefaultSession{ Claims: &jwt.IDTokenClaims{ - Issuer: "http://localhost:9998", - Subject: sub, + Issuer: uid.Idp, + Subject: uid.OpaqueId, //Audience: []string{"https://my-client.my-application.com"}, ExpiresAt: time.Now().Add(time.Hour * 6), IssuedAt: time.Now(), @@ -160,45 +132,87 @@ func newSession(username string, sub string) *openid.DefaultSession { Headers: &jwt.Headers{ Extra: make(map[string]interface{}), }, - Subject: sub, + Subject: uid.OpaqueId, Username: username, } } // emptySession creates a session object and fills it with safe defaults func emptySession() *openid.DefaultSession { - return newSession("", "") + return newSession("", &typespb.UserId{}) } func mustRSAKey() *rsa.PrivateKey { key, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { - // TODO really panic? + // TODO(jfd): don't panic! panic(err) } return key } -// TODO currently we fake a sub. it would change when tha username changes ... -func getSub(ctx context.Context, username string) string { - hasher := md5.New() - _, err := hasher.Write([]byte(username)) - if err != nil { - appctx.GetLogger(ctx).Error().Err(err).Msg("Error occurred in getSub") - return "" +func getAuthManager(manager string, m map[string]map[string]interface{}) (auth.Manager, error) { + if f, ok := authmgr.NewFuncs[manager]; ok { + return f(m[manager]) + } + + return nil, fmt.Errorf("driver %s not found for auth manager", manager) +} + +func getUserManager(manager string, m map[string]map[string]interface{}) (user.Manager, error) { + if f, ok := usermgr.NewFuncs[manager]; ok { + return f(m[manager]) } - return hex.EncodeToString(hasher.Sum(nil)) + + return nil, fmt.Errorf("driver %s not found for user manager", manager) } -// New returns a new webuisvc +// New returns a new oidcprovidersvc func New(m map[string]interface{}) (httpsvcs.Service, error) { - conf := &config{} - if err := mapstructure.Decode(m, conf); err != nil { + c := &config{} + if err := mapstructure.Decode(m, c); err != nil { + return nil, err + } + + authManager, err := getAuthManager(c.AuthManager, c.AuthManagers) + if err != nil { + return nil, err + } + + userManager, err := getUserManager(c.UserManager, c.UserManagers) + if err != nil { return nil, err } + store := newExampleStore() s := &svc{ - prefix: conf.Prefix, + prefix: c.Prefix, + authmgr: authManager, + usermgr: userManager, + // This is an exemplary storage instance. We will add a client and a user to it so we can use these later on. + store: store, + oauth2: compose.Compose( + fconfig, + store, + start, + nil, + + // enabled handlers + compose.OAuth2AuthorizeExplicitFactory, + compose.OAuth2AuthorizeImplicitFactory, + compose.OAuth2ClientCredentialsGrantFactory, + compose.OAuth2RefreshTokenGrantFactory, + compose.OAuth2ResourceOwnerPasswordCredentialsFactory, + + compose.OAuth2TokenRevocationFactory, + compose.OAuth2TokenIntrospectionFactory, + + // be aware that open id connect factories need to be added after oauth2 factories to work properly. + compose.OpenIDConnectExplicitFactory, + compose.OpenIDConnectImplicitFactory, + compose.OpenIDConnectHybridFactory, + compose.OpenIDConnectRefreshFactory, + ), } s.setHandler() return s, nil @@ -240,7 +254,7 @@ func (s *svc) setHandler() { case "userinfo": s.doUserinfo(w, r) case "sessions": - // TODO only for development + // TODO(jfd) make session lookup configurable? only for development? s.doSessions(w, r) default: w.WriteHeader(http.StatusNotFound) diff --git a/cmd/revad/svcs/httpsvcs/oidcprovider/revoke.go b/cmd/revad/svcs/httpsvcs/oidcprovider/revoke.go index 6f683ab3cd..65f9321009 100644 --- a/cmd/revad/svcs/httpsvcs/oidcprovider/revoke.go +++ b/cmd/revad/svcs/httpsvcs/oidcprovider/revoke.go @@ -31,7 +31,7 @@ func (s *svc) doRevoke(w http.ResponseWriter, r *http.Request) { log := appctx.GetLogger(ctx) // This will create an access request object and iterate through the registered TokenEndpointHandlers to validate the request. - accessRequest, err := oauth2.NewAccessRequest(ctx, r, emptySession()) + accessRequest, err := s.oauth2.NewAccessRequest(ctx, r, emptySession()) // Catch any errors, e.g.: // * unknown client @@ -39,7 +39,7 @@ func (s *svc) doRevoke(w http.ResponseWriter, r *http.Request) { // * ... if err != nil { log.Error().Err(err).Msg("Error occurred in NewAccessRequest") - oauth2.WriteAccessError(w, accessRequest, err) + s.oauth2.WriteAccessError(w, accessRequest, err) return } @@ -54,15 +54,15 @@ func (s *svc) doRevoke(w http.ResponseWriter, r *http.Request) { // Next we create a response for the access request. Again, we iterate through the TokenEndpointHandlers // and aggregate the result in response. - response, err := oauth2.NewAccessResponse(ctx, accessRequest) + response, err := s.oauth2.NewAccessResponse(ctx, accessRequest) if err != nil { log.Error().Err(err).Msg("Error occurred in NewAccessResponse") - oauth2.WriteAccessError(w, accessRequest, err) + s.oauth2.WriteAccessError(w, accessRequest, err) return } // All done, send the response. - oauth2.WriteAccessResponse(w, accessRequest, response) + s.oauth2.WriteAccessResponse(w, accessRequest, response) // The client now has a valid access token } diff --git a/cmd/revad/svcs/httpsvcs/oidcprovider/sessions.go b/cmd/revad/svcs/httpsvcs/oidcprovider/sessions.go index a691a45c5c..0ee08a847b 100644 --- a/cmd/revad/svcs/httpsvcs/oidcprovider/sessions.go +++ b/cmd/revad/svcs/httpsvcs/oidcprovider/sessions.go @@ -34,7 +34,7 @@ func (s *svc) doSessions(w http.ResponseWriter, r *http.Request) { log.Error().Err(err).Msg("Error writing response") return } - for id, c := range store.Clients { + for id, c := range s.store.Clients { c := c _, err := w.Write([]byte(fmt.Sprintf(`
  • @@ -53,7 +53,7 @@ func (s *svc) doSessions(w http.ResponseWriter, r *http.Request) { log.Error().Err(err).Msg("Error writing response") return } - for id, c := range store.AuthorizeCodes { + for id, c := range s.store.AuthorizeCodes { c := c _, err := w.Write([]byte(fmt.Sprintf(`
  • @@ -73,7 +73,7 @@ func (s *svc) doSessions(w http.ResponseWriter, r *http.Request) { log.Error().Err(err).Msg("Error writing response") return } - for id, c := range store.IDSessions { + for id, c := range s.store.IDSessions { c := c _, err := w.Write([]byte(fmt.Sprintf(`
  • @@ -93,7 +93,7 @@ func (s *svc) doSessions(w http.ResponseWriter, r *http.Request) { log.Error().Err(err).Msg("Error writing response") return } - for id, c := range store.AccessTokens { + for id, c := range s.store.AccessTokens { _, err := w.Write([]byte(fmt.Sprintf(`
  • %s: %#v @@ -112,7 +112,7 @@ func (s *svc) doSessions(w http.ResponseWriter, r *http.Request) { log.Error().Err(err).Msg("Error writing response") return } - for id, c := range store.Implicit { + for id, c := range s.store.Implicit { c := c _, err := w.Write([]byte(fmt.Sprintf(`
  • @@ -132,7 +132,7 @@ func (s *svc) doSessions(w http.ResponseWriter, r *http.Request) { log.Error().Err(err).Msg("Error writing response") return } - for id, c := range store.RefreshTokens { + for id, c := range s.store.RefreshTokens { c := c _, err := w.Write([]byte(fmt.Sprintf(`
  • @@ -152,27 +152,7 @@ func (s *svc) doSessions(w http.ResponseWriter, r *http.Request) { log.Error().Err(err).Msg("Error writing response") return } - for id, c := range store.PKCES { - c := c - _, err := w.Write([]byte(fmt.Sprintf(` -
  • - %s: %#v -
  • `, - - id, c, - ))) - if err != nil { - log.Error().Err(err).Msg("Error writing response") - return - } - } - // Users - _, err = w.Write([]byte(`

    Users

      `)) - if err != nil { - log.Error().Err(err).Msg("Error writing response") - return - } - for id, c := range store.Users { + for id, c := range s.store.PKCES { c := c _, err := w.Write([]byte(fmt.Sprintf(`
    • @@ -192,7 +172,7 @@ func (s *svc) doSessions(w http.ResponseWriter, r *http.Request) { log.Error().Err(err).Msg("Error writing response") return } - for id, c := range store.AccessTokenRequestIDs { + for id, c := range s.store.AccessTokenRequestIDs { c := c _, err := w.Write([]byte(fmt.Sprintf(`
    • @@ -212,7 +192,7 @@ func (s *svc) doSessions(w http.ResponseWriter, r *http.Request) { log.Error().Err(err).Msg("Error writing response") return } - for id, c := range store.RefreshTokenRequestIDs { + for id, c := range s.store.RefreshTokenRequestIDs { c := c _, err := w.Write([]byte(fmt.Sprintf(`
    • diff --git a/cmd/revad/svcs/httpsvcs/oidcprovider/token.go b/cmd/revad/svcs/httpsvcs/oidcprovider/token.go index ea00ae8f2f..75a0b38c38 100644 --- a/cmd/revad/svcs/httpsvcs/oidcprovider/token.go +++ b/cmd/revad/svcs/httpsvcs/oidcprovider/token.go @@ -31,7 +31,7 @@ func (s *svc) doToken(w http.ResponseWriter, r *http.Request) { log := appctx.GetLogger(ctx) // This will create an access request object and iterate through the registered TokenEndpointHandlers to validate the request. - accessRequest, err := oauth2.NewAccessRequest(ctx, r, emptySession()) + accessRequest, err := s.oauth2.NewAccessRequest(ctx, r, emptySession()) // Catch any errors, e.g.: // * unknown client @@ -39,7 +39,7 @@ func (s *svc) doToken(w http.ResponseWriter, r *http.Request) { // * ... if err != nil { log.Error().Err(err).Msg("Error occurred in NewAccessRequest") - oauth2.WriteAccessError(w, accessRequest, err) + s.oauth2.WriteAccessError(w, accessRequest, err) return } @@ -54,15 +54,15 @@ func (s *svc) doToken(w http.ResponseWriter, r *http.Request) { // Next we create a response for the access request. Again, we iterate through the TokenEndpointHandlers // and aggregate the result in response. - response, err := oauth2.NewAccessResponse(ctx, accessRequest) + response, err := s.oauth2.NewAccessResponse(ctx, accessRequest) if err != nil { log.Error().Err(err).Msg("Error occurred in NewAccessResponse") - oauth2.WriteAccessError(w, accessRequest, err) + s.oauth2.WriteAccessError(w, accessRequest, err) return } // All done, send the response. - oauth2.WriteAccessResponse(w, accessRequest, response) + s.oauth2.WriteAccessResponse(w, accessRequest, response) // The client now has a valid access token } diff --git a/cmd/revad/svcs/httpsvcs/oidcprovider/userinfo.go b/cmd/revad/svcs/httpsvcs/oidcprovider/userinfo.go index b033d902b3..579ee24c7e 100644 --- a/cmd/revad/svcs/httpsvcs/oidcprovider/userinfo.go +++ b/cmd/revad/svcs/httpsvcs/oidcprovider/userinfo.go @@ -25,6 +25,7 @@ import ( "github.com/ory/fosite" + typespb "github.com/cs3org/go-cs3apis/cs3/types" "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/auth/manager/oidc" ) @@ -35,7 +36,7 @@ func (s *svc) doUserinfo(w http.ResponseWriter, r *http.Request) { requiredScope := "openid" - _, ar, err := oauth2.IntrospectToken(ctx, fosite.AccessTokenFromRequest(r), fosite.AccessToken, emptySession(), requiredScope) + _, ar, err := s.oauth2.IntrospectToken(ctx, fosite.AccessTokenFromRequest(r), fosite.AccessToken, emptySession(), requiredScope) if err != nil { fmt.Fprintf(w, "

      An error occurred!

      Could not perform introspection: %v

      ", err) return @@ -43,30 +44,35 @@ func (s *svc) doUserinfo(w http.ResponseWriter, r *http.Request) { log.Debug().Interface("ar", ar).Msg("introspected") - var sc *oidc.StandardClaims - switch ar.GetSession().GetUsername() { - //TODO use reva specific implementation that uses existing user managers - case "aaliyah_abernathy": - sc = &oidc.StandardClaims{ - Name: "Aaliyah Abernathy", - } - case "aaliyah_adams": - sc = &oidc.StandardClaims{ - Name: "Aaliyah Adams", - } - case "aaliyah_anderson": - sc = &oidc.StandardClaims{ - Name: "Aaliyah Anderson", - } - default: - log.Error().Err(err).Msg("unknown user") + sub := ar.GetSession().GetSubject() + + uid := &typespb.UserId{ + // TODO(jfd): also fill the idp, if possible. + // well .. that might be hard, because in this case we are the idp + // we should put oar hostname into the Iss field + // - only an oidc provider would be able to provide an iss + // - maybe for ldap the uidNumber attribute makes more sense as sub? + // - this is still a tricky question. ms eg uses sid - security identifiers + // but they change when a usename changes or he moves to a new node in the tree + // to mitigate this they keep track of past ids in the sidHistory attribute + // so the filesystem might use an outdated sid in the permissions but the system + // can still resolve the user using the sidhistory attribute + OpaqueId: sub, + } + user, err := s.usermgr.GetUser(ctx, uid) + if err != nil { + log.Error().Err(err).Str("sub", sub).Msg("unknown user") w.WriteHeader(http.StatusNotFound) return } - sc.Sub = ar.GetSession().GetSubject() - sc.PreferredUsername = ar.GetSession().GetUsername() - sc.EmailVerified = true - sc.Email = sc.PreferredUsername + "@owncloudqa.com" + + sc := &oidc.StandardClaims{ + Sub: user.Id.OpaqueId, + Iss: user.Id.Idp, + PreferredUsername: user.Username, + Name: user.DisplayName, + Email: user.Mail, + } b, err := json.Marshal(sc) if err != nil {