Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AUTH-8: Add group synchronization from OIDC providers #87

Merged
merged 3 commits into from
Oct 18, 2021

Conversation

stlaz
Copy link
Contributor

@stlaz stlaz commented Sep 24, 2021

This PR adds group synchronization for OIDC providers as described in the Synchronizing User Groups on Login enhancement.

The usermappers normally used to create users are wrapped by a delegator that first attempts to perform the usermapping strategy and only after its success it attempts to add/remove the user in appropriate groups.

@stlaz
Copy link
Contributor Author

stlaz commented Sep 24, 2021

/cc @s-urbaniak @ibihim

@openshift-ci openshift-ci bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Sep 24, 2021
@stlaz
Copy link
Contributor Author

stlaz commented Sep 24, 2021

/hold
for some reason the bot did not mark the PR as WIP, adding hold manually

@openshift-ci openshift-ci bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Sep 24, 2021
@stlaz stlaz changed the title WIP: AUTH-8: Add group synchronization from OIDC providers [WIP] AUTH-8: Add group synchronization from OIDC providers Sep 24, 2021
@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Sep 24, 2021
@stlaz
Copy link
Contributor Author

stlaz commented Sep 24, 2021

/hold cancel
there we go

@openshift-ci openshift-ci bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Sep 24, 2021
@stlaz stlaz force-pushed the groupsync_oidc branch 6 times, most recently from e4c60b0 to a8b4672 Compare September 27, 2021 15:13
Copy link
Contributor

@ibihim ibihim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great PR. I can't yet understand everything, but I hope my comments help you nevertheless.

@@ -33,7 +33,7 @@ func RunOsinServer(osinConfig *osinv1.OsinServerConfig, stopCh <-chan struct{})
return errors.New("osin server requires non-empty oauthConfig")
}

oauthServerConfig, err := newOAuthServerConfig(osinConfig)
oauthServerConfig, postStartHooks, err := newOAuthServerConfig(osinConfig)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be better, if postStartHooks would be returned from a dedicated function or being part of the oauthServerConfig (if they config something)?

I wouldn't anticipate to retrieve hooks, when calling for a config.

The main point, as far as I can tell (and this is not much, as I am a newbie and I am looking from an abstract and not holistic view), we are handing the definition of the hooks down to reach the informer. So I would assume that we could create the informer on this level and hand it down to the config and a dedicated make-hooks function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same how the postStartHooks are being passed through the other apiservers (oauth-, openshift- and kube-apiservers).

I don't think return of postStartHooks is actually that surprising here. You're generating configuration for the apiserver, and you want the apiserver to run certain actions that make the configuration work properly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is idiomatic, let keep doing it that way 😄

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to understand the idiomatic attempt to handling postStartHooks I looked into kubernetes/apiserver and openshift/openshift-apiserver, but couldn't find a similar pattern. In the former it is part of the Server and injected and in the later it is part of the Config.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I think my point was more about delivering the postStartHooks to the inner GenericAPIServer of the server object,

I can hide the hooks in the config structure if you want, and use it while setting up the OAuthServer struct in the completedOAuthConfig.New method if you want.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved the postStartHooks to the config

pkg/oauthserver/auth.go Show resolved Hide resolved
pkg/api/types.go Outdated Show resolved Hide resolved
pkg/groupmapper/groupmapper.go Show resolved Hide resolved
pkg/groupmapper/groupmapper.go Show resolved Hide resolved
// FIXME: add retries
// FIXME: use idpName
func (m *UserGroupsMapper) removeUserFromGroupWithRetries(idpName, username, group string) error {
updatedGroup, err := m.groupsClient.Get(context.TODO(), group, metav1.GetOptions{})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we bubble up the ctx?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the context is unfortunately not wired through the oauth-server, and I fear that to really do it properly we would have to also implement openshift/osin#82

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on Sep 18, 2015 👀

Copy link
Contributor

@ibihim ibihim Oct 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I would mark the suggestion as too big for the issue.

Do we have refactoring opportunities?

pkg/groupmapper/groupmapper.go Show resolved Hide resolved
pkg/groupmapper/groupmapper.go Show resolved Hide resolved
pkg/groupmapper/groupmapper.go Outdated Show resolved Hide resolved
@stlaz stlaz force-pushed the groupsync_oidc branch 2 times, most recently from 74983be to 81db104 Compare October 5, 2021 11:33
@stlaz
Copy link
Contributor Author

stlaz commented Oct 5, 2021

/test verify
looks suspiciously like failed infra

@stlaz stlaz changed the title [WIP] AUTH-8: Add group synchronization from OIDC providers AUTH-8: Add group synchronization from OIDC providers Oct 5, 2021
@openshift-ci openshift-ci bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Oct 5, 2021
@stlaz
Copy link
Contributor Author

stlaz commented Oct 6, 2021

/hold
contains fake bumps

@openshift-ci openshift-ci bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Oct 6, 2021
userIdentityMapping userclient.UserIdentityMappingInterface,
method identitymapper.MappingMethodType,
) (api.UserIdentityMapper, error) {
userMapper, err := identitymapper.NewIdentityUserMapper(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as far as i see NewIdentityUserMapper is unused, is there any reason to keep this separate constructor? I think we are instanciating with groups only, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are wrapping the UserIdentityWrapper result of the NewIdentityUserMapper with a groups mapper. I think it's worth having it separate to keep the logical wrapper -> wrapped relationship as each of these mappers are serving a slightly different purpose.

@stlaz
Copy link
Contributor Author

stlaz commented Oct 13, 2021

now with real bumps!

groups userclient.GroupInterface,
groupsLister userlisterv1.GroupLister,
userIdentityMapping userclient.UserIdentityMappingInterface,
method identitymapper.MappingMethodType,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: quite some amount of params. At some point we should consider switching to a config / functional options.

@@ -33,7 +33,7 @@ func RunOsinServer(osinConfig *osinv1.OsinServerConfig, stopCh <-chan struct{})
return errors.New("osin server requires non-empty oauthConfig")
}

oauthServerConfig, err := newOAuthServerConfig(osinConfig)
oauthServerConfig, postStartHooks, err := newOAuthServerConfig(osinConfig)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to understand the idiomatic attempt to handling postStartHooks I looked into kubernetes/apiserver and openshift/openshift-apiserver, but couldn't find a similar pattern. In the former it is part of the Server and injected and in the later it is part of the Config.

pkg/groupmapper/groupmapper.go Show resolved Hide resolved
pkg/groupmapper/groupmapper.go Show resolved Hide resolved
@@ -43,7 +49,7 @@ func init() {

// TODO we need to switch the oauth server to an external type, but that can be done after we get our externally facing flag values fixed
// TODO remaining bits involve the session file, LDAP util code, validation, ...
func NewOAuthServerConfig(oauthConfig osinv1.OAuthConfig, userClientConfig *rest.Config, genericConfig *genericapiserver.RecommendedConfig) (*OAuthServerConfig, error) {
func NewOAuthServerConfig(oauthConfig osinv1.OAuthConfig, userClientConfig *rest.Config, genericConfig *genericapiserver.RecommendedConfig) (*OAuthServerConfig, map[string]genericapiserver.PostStartHookFunc, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be cool if we could at some point trim down the function 😄

return ret, nil
postStartHooks := map[string]genericapiserver.PostStartHookFunc{
"openshift.io-StartUserInformer": func(ctx genericapiserver.PostStartHookContext) error {
go userInformer.Start(ctx.StopCh)
Copy link
Contributor

@ibihim ibihim Oct 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to start it as a Go routine? The Start method seems to iterate over the internal informers and start a Go routine too.

https://github.com/kubernetes/apiserver/blob/master/pkg/server/config.go#L664

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be better to make sure the call is non-blocking if we don't own the implementation, but I can change it to not use the goroutine if you want.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be interesting to know if this Go routines will stay alive for as long as the child-Go routines are alive.

If the minimal blocking, while iterating through the for-loop could be more problematic than an additional Go-routine, then I am fine 😄

Wrap usermappers with a groupmapper that attempts to add a user to
groups that they are not a part of or remove them from groups for the
specific provider.
@stlaz
Copy link
Contributor Author

stlaz commented Oct 18, 2021

/hold cancel

@openshift-ci openshift-ci bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Oct 18, 2021
Copy link
Contributor

@ibihim ibihim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

Name: group,
Annotations: map[string]string{
fmt.Sprintf(groupSyncedKeyFmt, idpName): "synced",
groupGeneratedKey: "true",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

Sorry, I just now realized this. Would it make sense to make "synced" and "true" constants?

@openshift-ci openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label Oct 18, 2021
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Oct 18, 2021

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: ibihim, stlaz

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-bot
Copy link
Contributor

/retest-required

Please review the full test history for this PR and help us cut down flakes.

1 similar comment
@openshift-bot
Copy link
Contributor

/retest-required

Please review the full test history for this PR and help us cut down flakes.

@openshift-merge-robot openshift-merge-robot merged commit 6eb7ac3 into openshift:master Oct 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants