diff --git a/changelog/unreleased/oidc-ldap-user-groups.md b/changelog/unreleased/oidc-ldap-user-groups.md new file mode 100644 index 0000000000..df9a88bd24 --- /dev/null +++ b/changelog/unreleased/oidc-ldap-user-groups.md @@ -0,0 +1,3 @@ +Enhancement: Fetch user groups in OIDC and LDAP backend + +https://github.com/cs3org/reva/pull/1456 diff --git a/pkg/auth/manager/ldap/ldap.go b/pkg/auth/manager/ldap/ldap.go index 8040b845ab..4a490937c3 100644 --- a/pkg/auth/manager/ldap/ldap.go +++ b/pkg/auth/manager/ldap/ldap.go @@ -25,12 +25,15 @@ import ( "strings" user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/auth" "github.com/cs3org/reva/pkg/auth/manager/registry" "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/logger" + "github.com/cs3org/reva/pkg/rgrpc/todo/pool" + "github.com/cs3org/reva/pkg/sharedconf" "github.com/go-ldap/ldap/v3" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" @@ -53,6 +56,7 @@ type config struct { BindUsername string `mapstructure:"bind_username"` BindPassword string `mapstructure:"bind_password"` Idp string `mapstructure:"idp"` + GatewaySvc string `mapstructure:"gatewaysvc"` Schema attributes `mapstructure:"schema"` } @@ -111,6 +115,8 @@ func New(m map[string]interface{}) (auth.Manager, error) { c.LoginFilter = strings.ReplaceAll(c.LoginFilter, "%s", "{{login}}") } + c.GatewaySvc = sharedconf.GetGatewaySVC(c.GatewaySvc) + return &mgr{ c: c, }, nil @@ -159,15 +165,30 @@ func (am *mgr) Authenticate(ctx context.Context, clientID, clientSecret string) return nil, err } + userID := &user.UserId{ + Idp: am.c.Idp, + OpaqueId: sr.Entries[0].GetEqualFoldAttributeValue(am.c.Schema.UID), + } + gwc, err := pool.GetGatewayServiceClient(am.c.GatewaySvc) + if err != nil { + return nil, errors.Wrap(err, "ldap: error getting gateway grpc client") + } + getGroupsResp, err := gwc.GetUserGroups(ctx, &user.GetUserGroupsRequest{ + UserId: userID, + }) + if err != nil { + return nil, errors.Wrap(err, "ldap: error getting user groups") + } + if getGroupsResp.Status.Code != rpc.Code_CODE_OK { + return nil, errors.Wrap(err, "ldap: grpc getting user groups failed") + } + u := &user.User{ - Id: &user.UserId{ - Idp: am.c.Idp, - OpaqueId: sr.Entries[0].GetEqualFoldAttributeValue(am.c.Schema.UID), - }, + Id: userID, // TODO add more claims from the StandardClaims, eg EmailVerified Username: sr.Entries[0].GetEqualFoldAttributeValue(am.c.Schema.CN), // TODO groups - Groups: []string{}, + Groups: getGroupsResp.Groups, Mail: sr.Entries[0].GetEqualFoldAttributeValue(am.c.Schema.Mail), DisplayName: sr.Entries[0].GetEqualFoldAttributeValue(am.c.Schema.DisplayName), Opaque: &types.Opaque{ diff --git a/pkg/auth/manager/oidc/oidc.go b/pkg/auth/manager/oidc/oidc.go index fd7f37baf1..b5bea8f964 100644 --- a/pkg/auth/manager/oidc/oidc.go +++ b/pkg/auth/manager/oidc/oidc.go @@ -27,10 +27,13 @@ import ( oidc "github.com/coreos/go-oidc" user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" "github.com/cs3org/reva/pkg/auth" "github.com/cs3org/reva/pkg/auth/manager/registry" + "github.com/cs3org/reva/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/pkg/rhttp" + "github.com/cs3org/reva/pkg/sharedconf" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" "github.com/rs/zerolog/log" @@ -47,11 +50,12 @@ type mgr struct { } type config struct { - Insecure bool `mapstructure:"insecure" docs:"false;Whether to skip certificate checks when sending requests."` - Issuer string `mapstructure:"issuer" docs:";The issuer of the OIDC token."` - IDClaim string `mapstructure:"id_claim" docs:"sub;The claim containing the ID of the user."` - UIDClaim string `mapstructure:"uid_claim" docs:";The claim containing the UID of the user."` - GIDClaim string `mapstructure:"gid_claim" docs:";The claim containing the GID of the user."` + Insecure bool `mapstructure:"insecure" docs:"false;Whether to skip certificate checks when sending requests."` + Issuer string `mapstructure:"issuer" docs:";The issuer of the OIDC token."` + IDClaim string `mapstructure:"id_claim" docs:"sub;The claim containing the ID of the user."` + UIDClaim string `mapstructure:"uid_claim" docs:";The claim containing the UID of the user."` + GIDClaim string `mapstructure:"gid_claim" docs:";The claim containing the GID of the user."` + GatewaySvc string `mapstructure:"gatewaysvc" docs:";The endpoint at which the GRPC gateway is exposed."` } func (c *config) init() { @@ -59,6 +63,8 @@ func (c *config) init() { // sub is stable and defined as unique. the user manager needs to take care of the sub to user metadata lookup c.IDClaim = "sub" } + + c.GatewaySvc = sharedconf.GetGatewaySVC(c.GatewaySvc) } func parseConfig(m map[string]interface{}) (*config, error) { @@ -145,17 +151,32 @@ func (am *mgr) Authenticate(ctx context.Context, clientID, clientSecret string) } } + userID := &user.UserId{ + OpaqueId: claims[am.c.IDClaim].(string), // a stable non reassignable id + Idp: claims["issuer"].(string), // in the scope of this issuer + } + gwc, err := pool.GetGatewayServiceClient(am.c.GatewaySvc) + if err != nil { + return nil, errors.Wrap(err, "oidc: error getting gateway grpc client") + } + getGroupsResp, err := gwc.GetUserGroups(ctx, &user.GetUserGroupsRequest{ + UserId: userID, + }) + if err != nil { + return nil, errors.Wrap(err, "oidc: error getting user groups") + } + if getGroupsResp.Status.Code != rpc.Code_CODE_OK { + return nil, errors.Wrap(err, "oidc: grpc getting user groups failed") + } + u := &user.User{ - Id: &user.UserId{ - OpaqueId: claims[am.c.IDClaim].(string), // a stable non reassignable id - Idp: claims["issuer"].(string), // in the scope of this issuer - }, + Id: userID, Username: claims["preferred_username"].(string), // TODO(labkode) if we can get groups from the claim we need to give the possibility // to the admin to choose what claim provides the groups. // TODO(labkode) ... use all claims from oidc? // TODO(labkode): do like K8s does it: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go - Groups: []string{}, + Groups: getGroupsResp.Groups, Mail: claims["email"].(string), MailVerified: claims["email_verified"].(bool), DisplayName: claims["name"].(string),