Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@pedroigor
Copy link
Contributor

@pedroigor pedroigor commented May 16, 2024

Closes #30747

  • Removes the constraints we have today to allow users to join multiple organizations
  • Most of the changes are related to changing methods and places to support users/members associated with multiple organizations
  • A member of different organizations is a managed member in only one of the organizations. By that, we ensure members always have a single source of truth for their identities. This PR depends on Make sure users created through a registration link are managed members #30743 and should be updated accordingly.
  • The OrganizationAuthenticator was refactored to make it simpler and to support members of multiple organizations
  • Removing kc.org user attribute
  • Introducing cache to some query methods that were previously relying on the kc.org user attribute

Copy link

@keycloak-github-bot keycloak-github-bot bot left a comment

Choose a reason for hiding this comment

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

Unreported flaky test detected, please review

@keycloak-github-bot
Copy link

Unreported flaky test detected

If the flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR.

org.keycloak.testsuite.model.user.UserModelTest#testAddRemoveUserConcurrent

Keycloak CI - Store Model Tests

java.util.ConcurrentModificationException: java.util.ConcurrentModificationException
	at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
	at java.base/java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:540)
...

Report flaky test

org.keycloak.testsuite.model.user.UserModelTest#testAddRemoveUserConcurrent

Keycloak CI - Store Model Tests

java.util.ConcurrentModificationException: java.util.ConcurrentModificationException
	at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
	at java.base/java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:540)
...

Report flaky test

org.keycloak.testsuite.model.user.UserModelTest#testAddRemoveUserConcurrent

Keycloak CI - Store Model Tests

java.util.ConcurrentModificationException: java.util.ConcurrentModificationException
	at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
	at java.base/java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:540)
...

Report flaky test

@pedroigor pedroigor force-pushed the org-members-multiple-orgs branch from 1379d88 to 4bb0fdb Compare June 12, 2024 13:13
@pedroigor pedroigor force-pushed the org-members-multiple-orgs branch 4 times, most recently from 7d9d70d to 95cc95a Compare July 10, 2024 12:57
@pedroigor pedroigor force-pushed the org-members-multiple-orgs branch 6 times, most recently from cc92c0d to 16d784f Compare July 16, 2024 19:48
@pedroigor pedroigor changed the title wip: support for joining multiple organizations Allow members joining multiple organizations Jul 16, 2024
@pedroigor pedroigor changed the title Allow members joining multiple organizations Allow members joining multiple organizations Jul 16, 2024
@pedroigor pedroigor marked this pull request as ready for review July 16, 2024 19:49
@pedroigor pedroigor requested review from a team as code owners July 16, 2024 19:49
ahus1
ahus1 previously approved these changes Jul 23, 2024
Copy link
Contributor

@ahus1 ahus1 left a comment

Choose a reason for hiding this comment

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

Approving based on @sguilhen's approval. Not merging yet in case a review from another person is necessary.

@ahus1
Copy link
Contributor

ahus1 commented Jul 23, 2024

BTW, Adapter IT failed, rerunning that job.

Copy link

@keycloak-github-bot keycloak-github-bot bot left a comment

Choose a reason for hiding this comment

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

Unreported flaky test detected, please review

@keycloak-github-bot
Copy link

Unreported flaky test detected

If the flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR.

org.keycloak.testsuite.forms.LoginHotpTest#loginWithHotpSuccess

Keycloak CI - Forms IT (chrome)

java.lang.IllegalStateException: Could not start mail server smtp:localhost:3025, try to set server startup timeout > 2000 via ServerSetup.setServerStartupTimeout(timeoutInMs) or -Dgreenmail.startup.timeout
	at com.icegreen.greenmail.util.GreenMail.start(GreenMail.java:118)
	at org.keycloak.testsuite.util.GreenMailRule.before(GreenMailRule.java:54)
	at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:50)
	at org.jboss.arquillian.junit.Arquillian$7$1.invoke(Arquillian.java:273)
...

Report flaky test

org.keycloak.testsuite.forms.LoginHotpTest#loginWithHotpFailure

Keycloak CI - Forms IT (chrome)

java.lang.IllegalStateException: Could not start mail server smtp:localhost:3025, try to set server startup timeout > 2000 via ServerSetup.setServerStartupTimeout(timeoutInMs) or -Dgreenmail.startup.timeout
	at com.icegreen.greenmail.util.GreenMail.start(GreenMail.java:118)
	at org.keycloak.testsuite.util.GreenMailRule.before(GreenMailRule.java:54)
	at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:50)
	at org.jboss.arquillian.junit.Arquillian$7$1.invoke(Arquillian.java:273)
...

Report flaky test

org.keycloak.testsuite.forms.LoginHotpTest#loginWithHotpInvalidPassword

Keycloak CI - Forms IT (chrome)

java.lang.IllegalStateException: Could not start mail server smtp:localhost:3025, try to set server startup timeout > 2000 via ServerSetup.setServerStartupTimeout(timeoutInMs) or -Dgreenmail.startup.timeout
	at com.icegreen.greenmail.util.GreenMail.start(GreenMail.java:118)
	at org.keycloak.testsuite.util.GreenMailRule.before(GreenMailRule.java:54)
	at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:50)
	at org.jboss.arquillian.junit.Arquillian$7$1.invoke(Arquillian.java:273)
...

Report flaky test

org.keycloak.testsuite.forms.LoginHotpTest#loginWithMissingHotp

Keycloak CI - Forms IT (chrome)

java.lang.IllegalStateException: Could not start mail server smtp:localhost:3025, try to set server startup timeout > 2000 via ServerSetup.setServerStartupTimeout(timeoutInMs) or -Dgreenmail.startup.timeout
	at com.icegreen.greenmail.util.GreenMail.start(GreenMail.java:118)
	at org.keycloak.testsuite.util.GreenMailRule.before(GreenMailRule.java:54)
	at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:50)
	at org.jboss.arquillian.junit.Arquillian$7$1.invoke(Arquillian.java:273)
...

Report flaky test

Copy link
Contributor

@vramik vramik left a comment

Choose a reason for hiding this comment

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

Thank you @pedroigor for the PR, I have only few suggestions about naming and one question about caching. I assume there will be needed rebase after #31471 would be merged.

@GET
@Produces(MediaType.APPLICATION_JSON)
OrganizationRepresentation getOrganization(@PathParam("id") String id);
List<OrganizationRepresentation> getOrganization(@PathParam("id") String id);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
List<OrganizationRepresentation> getOrganization(@PathParam("id") String id);
List<OrganizationRepresentation> getOrganizations(@PathParam("id") String id);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@vramik Just realized ... Do we really need this method? Why not just have member(id).getOrganizations?

Copy link
Contributor

Choose a reason for hiding this comment

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

Probably not, good catch.

@DELETE
Response delete();

@Path("organization")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
@Path("organization")
@Path("organizations")

@QueryParam("max") Integer max
);

@Path("{id}/organization")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
@Path("{id}/organization")
@Path("{id}/organizations")

Comment on lines +200 to +210
UserModel member = getMemberById(organization, user.getId());

if (member == null) {
return false;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we rely on CachedMembership instead of getting the member by id? I am probably missing something ... if CachedMembership is not invalidated then we may be able to get the information whether the users is Managed or not from it, don't we?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If the getMemberById returns null is because the user is not a member.

Copy link
Contributor

@vramik vramik Jul 24, 2024

Choose a reason for hiding this comment

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

I see, thanks, so it means that user might not a member, but could still be cached the information within CachedMembership, right?

@pedroigor pedroigor force-pushed the org-members-multiple-orgs branch from 0061b02 to 770cbe7 Compare July 24, 2024 11:52
@pedroigor pedroigor force-pushed the org-members-multiple-orgs branch from 770cbe7 to 24905da Compare July 24, 2024 13:40
@pedroigor
Copy link
Contributor Author

@vramik Applied your suggestions.

@pedroigor
Copy link
Contributor Author

@vramik It is rebased already. I'll check now the behavior with your latest changes.

@pedroigor pedroigor force-pushed the org-members-multiple-orgs branch 2 times, most recently from 9e597b3 to 9bb1987 Compare July 26, 2024 12:53
@pedroigor pedroigor force-pushed the org-members-multiple-orgs branch from 9bb1987 to 116d685 Compare July 26, 2024 14:20
Copy link
Contributor

@mhajas mhajas left a comment

Choose a reason for hiding this comment

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

Approving based on reviews from @keycloak/core-iam

@mhajas mhajas merged commit 04bd665 into keycloak:main Jul 29, 2024
@jaeaxt
Copy link

jaeaxt commented Sep 23, 2024

Today I tried Keycloak 25.0.6 but when I try to add user to a second Organization gave a error than the user only can have 1 Organization, is that ok ?

@sschu
Copy link
Contributor

sschu commented Sep 23, 2024

@jaeaxt This was merged into main so will be available in the next major release of Keycloak which is Keycloak 26.

@DGuhr
Copy link
Contributor

DGuhr commented Sep 25, 2024

Hey Folks 👋 Great work with orgs so-far. I've just tried out the latest nightly build via

docker run --name kc-orgs -d -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -p 8080:8080 quay.io/keycloak/keycloak:nightly start-dev --features organization

, created 2 orgs foo/foo.com and bar/bar.com, and added a user baz/[email protected] as member in both orgs.

Now I wanted to login using the new identity first - flow and just "baz".

My expectation was that I could somehow "choose" for which org I want to log in.
Instead, the bar.com password/IdP page was shown.

Now I am a bit unsure how, with this PR, it'd be possible to login my [email protected] user under the context of the foo org. It'd be awesome if you had any hints, couldn't find an issue so-far and thought I'd ask here first :)

@sguilhen
Copy link
Contributor

@DGuhr I just tested here and I see what you mean - if the user has an e-mail that matches one of the orgs, we're being a bit too opinionated towards the IDP selection. In this case, we're narrowing the IDP choices based on the e-mail that matched.

@pedroigor we need to discuss how to make this a little less opinionated - if user belongs to more than 1 org, then if none of the orgs has an idp that automatically redirects based on e-mail, we prob should be assembling the IDP list in a way that all orgs the user belongs too are represented (provided they have public IDPs, that is).

@sguilhen
Copy link
Contributor

If you think about it, even with no public IDPs, if the user is a member of multiple orgs then perhaps a better option would be not to select the IDP itself, but the org he wants to sign in to. Then we redirect to the org IDP, which doesn't even have to be public.

@Anderen2
Copy link

Anderen2 commented Aug 5, 2025

If you think about it, even with no public IDPs, if the user is a member of multiple orgs then perhaps a better option would be not to select the IDP itself, but the org he wants to sign in to. Then we redirect to the org IDP, which doesn't even have to be public.

Hi @sguilhen and @pedroigor.
Sorry for commenting on this closed PR, however I was not able to find any issue or documentation around this. Is this already implemented, or is there a separate issue somewhere on allowing a user to select a org if member of multiple?

Would this also set which organization the user is "primarily acting on behalf of"? Eg. if you have a consultant or similar working on the behalf of multiple of your clients (but always only on one at a time)?

@pedroigor
Copy link
Contributor Author

Hey @Anderen2, no problem. Sometimes I find myself doing the same ... Not ideal because comments usually get lost.

There is support for allowing users to select an organization when they are a member of multiple ones. The part of the documentation covering this capability is here [1].

Basically, whenever you send the organization scope, if the user has multiple organizations, he will be prompted to select one. After doing so, the authentication will happen in the context of that organization so that any IdP associated with the organization is considered (including automatic redirect), and the token will have the organization claim mapping to the selected organization.

Is this what you are looking for?

[1] https://www.keycloak.org/docs/latest/server_admin/#_mapping_organization_claims_

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for members joining multiple organizations

10 participants