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(`
-
`))
- 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 {