Manage a single identity provider for an organization
Closes #28272 Signed-off-by: vramik <vramik@redhat.com>
This commit is contained in:
parent
0327787645
commit
00ce3e34bd
12 changed files with 501 additions and 71 deletions
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.admin.client.resource;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.DELETE;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.PUT;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
|
|
||||||
|
public interface OrganizationIdentityProviderResource {
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
Response create(IdentityProviderRepresentation idpRepresentation);
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
IdentityProviderRepresentation toRepresentation();
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
Response update(IdentityProviderRepresentation idpRepresentation);
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
Response delete();
|
||||||
|
}
|
|
@ -42,4 +42,7 @@ public interface OrganizationResource {
|
||||||
|
|
||||||
@Path("members")
|
@Path("members")
|
||||||
OrganizationMembersResource members();
|
OrganizationMembersResource members();
|
||||||
|
|
||||||
|
@Path("identity-provider")
|
||||||
|
OrganizationIdentityProviderResource identityProvider();
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,6 @@ import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.MapKeyColumn;
|
import jakarta.persistence.MapKeyColumn;
|
||||||
import jakarta.persistence.NamedQueries;
|
|
||||||
import jakarta.persistence.NamedQuery;
|
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -38,9 +36,6 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name="IDENTITY_PROVIDER")
|
@Table(name="IDENTITY_PROVIDER")
|
||||||
@NamedQueries({
|
|
||||||
@NamedQuery(name="findIdentityProviderByAlias", query="select identityProvider from IdentityProviderEntity identityProvider where identityProvider.alias = :alias")
|
|
||||||
})
|
|
||||||
public class IdentityProviderEntity {
|
public class IdentityProviderEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
|
|
@ -34,9 +34,12 @@ import jakarta.persistence.Table;
|
||||||
public class OrganizationEntity {
|
public class OrganizationEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@Column(name="ID", length = 36)
|
@Column(name = "ID", length = 36)
|
||||||
@Access(AccessType.PROPERTY)
|
@Access(AccessType.PROPERTY)
|
||||||
protected String id;
|
private String id;
|
||||||
|
|
||||||
|
@Column(name = "NAME")
|
||||||
|
private String name;
|
||||||
|
|
||||||
@Column(name = "REALM_ID")
|
@Column(name = "REALM_ID")
|
||||||
private String realmId;
|
private String realmId;
|
||||||
|
@ -44,8 +47,8 @@ public class OrganizationEntity {
|
||||||
@Column(name = "GROUP_ID")
|
@Column(name = "GROUP_ID")
|
||||||
private String groupId;
|
private String groupId;
|
||||||
|
|
||||||
@Column(name="NAME")
|
@Column(name = "IPD_ALIAS")
|
||||||
protected String name;
|
private String idpAlias;
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
|
@ -55,6 +58,10 @@ public class OrganizationEntity {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
public String getRealmId() {
|
public String getRealmId() {
|
||||||
return realmId;
|
return realmId;
|
||||||
}
|
}
|
||||||
|
@ -75,8 +82,12 @@ public class OrganizationEntity {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
public String getIdpAlias() {
|
||||||
this.name = name;
|
return idpAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIdpAlias(String idpAlias) {
|
||||||
|
this.idpAlias = idpAlias;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -27,7 +27,9 @@ import jakarta.persistence.TypedQuery;
|
||||||
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.GroupProvider;
|
import org.keycloak.models.GroupProvider;
|
||||||
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
import org.keycloak.models.ModelException;
|
import org.keycloak.models.ModelException;
|
||||||
import org.keycloak.models.OrganizationModel;
|
import org.keycloak.models.OrganizationModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
@ -71,15 +73,17 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean remove(OrganizationModel organization) {
|
public boolean remove(OrganizationModel organization) {
|
||||||
|
OrganizationEntity entity = getEntity(organization.getId());
|
||||||
|
|
||||||
GroupModel group = getOrganizationGroup(organization);
|
GroupModel group = getOrganizationGroup(organization);
|
||||||
|
|
||||||
//TODO: won't scale, requires a better mechanism for bulk deleting users
|
//TODO: won't scale, requires a better mechanism for bulk deleting users
|
||||||
userProvider.getGroupMembersStream(realm, group).forEach(userModel -> userProvider.removeUser(realm, userModel));
|
userProvider.getGroupMembersStream(realm, group).forEach(userModel -> userProvider.removeUser(realm, userModel));
|
||||||
groupProvider.removeGroup(realm, group);
|
groupProvider.removeGroup(realm, group);
|
||||||
|
|
||||||
OrganizationAdapter adapter = getAdapter(organization.getId());
|
realm.removeIdentityProviderByAlias(entity.getIdpAlias());
|
||||||
|
|
||||||
em.remove(adapter.getEntity());
|
em.remove(entity);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -92,12 +96,11 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addMember(OrganizationModel organization, UserModel user) {
|
public boolean addMember(OrganizationModel organization, UserModel user) {
|
||||||
throwExceptionIfOrganizationIsNull(organization);
|
throwExceptionIfObjectIsNull(organization, "Organization");
|
||||||
if (user == null) {
|
throwExceptionIfObjectIsNull(user, "User");
|
||||||
throw new ModelException("User can not be null");
|
|
||||||
}
|
OrganizationEntity entity = getEntity(organization.getId());
|
||||||
OrganizationAdapter adapter = getAdapter(organization.getId());
|
GroupModel group = groupProvider.getGroupById(realm, entity.getGroupId());
|
||||||
GroupModel group = groupProvider.getGroupById(realm, adapter.getGroupId());
|
|
||||||
|
|
||||||
if (user.isMemberOf(group)) {
|
if (user.isMemberOf(group)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -108,14 +111,15 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
user.joinGroup(group);
|
user.joinGroup(group);
|
||||||
user.setSingleAttribute(USER_ORGANIZATION_ATTRIBUTE, adapter.getId());
|
user.setSingleAttribute(USER_ORGANIZATION_ATTRIBUTE, entity.getId());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OrganizationModel getById(String id) {
|
public OrganizationModel getById(String id) {
|
||||||
return getAdapter(id, false);
|
OrganizationEntity entity = getEntity(id, false);
|
||||||
|
return entity == null ? null : new OrganizationAdapter(realm, entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -129,16 +133,15 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<UserModel> getMembersStream(OrganizationModel organization) {
|
public Stream<UserModel> getMembersStream(OrganizationModel organization) {
|
||||||
throwExceptionIfOrganizationIsNull(organization);
|
throwExceptionIfObjectIsNull(organization, "Organization");
|
||||||
OrganizationAdapter adapter = getAdapter(organization.getId());
|
GroupModel group = getOrganizationGroup(organization);
|
||||||
GroupModel group = getOrganizationGroup(adapter);
|
|
||||||
|
|
||||||
return userProvider.getGroupMembersStream(realm, group);
|
return userProvider.getGroupMembersStream(realm, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel getMemberById(OrganizationModel organization, String id) {
|
public UserModel getMemberById(OrganizationModel organization, String id) {
|
||||||
throwExceptionIfOrganizationIsNull(organization);
|
throwExceptionIfObjectIsNull(organization, "Organization");
|
||||||
UserModel user = userProvider.getUserById(realm, id);
|
UserModel user = userProvider.getUserById(realm, id);
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
|
@ -156,9 +159,7 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OrganizationModel getByMember(UserModel member) {
|
public OrganizationModel getByMember(UserModel member) {
|
||||||
if (member == null) {
|
throwExceptionIfObjectIsNull(member, "User");
|
||||||
throw new ModelException("User can not be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
String orgId = member.getFirstAttribute(USER_ORGANIZATION_ATTRIBUTE);
|
String orgId = member.getFirstAttribute(USER_ORGANIZATION_ATTRIBUTE);
|
||||||
|
|
||||||
|
@ -169,16 +170,47 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
return getById(orgId);
|
return getById(orgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addIdentityProvider(OrganizationModel organization, IdentityProviderModel identityProvider) {
|
||||||
|
throwExceptionIfObjectIsNull(organization, "Organization");
|
||||||
|
throwExceptionIfObjectIsNull(identityProvider, "Identity provider");
|
||||||
|
|
||||||
|
OrganizationEntity organizationEntity = getEntity(organization.getId());
|
||||||
|
organizationEntity.setIdpAlias(identityProvider.getAlias());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentityProviderModel getIdentityProvider(OrganizationModel organization) {
|
||||||
|
throwExceptionIfObjectIsNull(organization, "Organization");
|
||||||
|
throwExceptionIfObjectIsNull(organization.getId(), "Organization ID");
|
||||||
|
|
||||||
|
OrganizationEntity organizationEntity = getEntity(organization.getId());
|
||||||
|
// realm and its IDPs are cached
|
||||||
|
return realm.getIdentityProviderByAlias(organizationEntity.getIdpAlias());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeIdentityProvider(OrganizationModel organization) {
|
||||||
|
throwExceptionIfObjectIsNull(organization, "Organization");
|
||||||
|
|
||||||
|
OrganizationEntity organizationEntity = getEntity(organization.getId());
|
||||||
|
organizationEntity.setIdpAlias(null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private OrganizationAdapter getAdapter(String id) {
|
/**
|
||||||
return getAdapter(id, true);
|
* @throws ModelException if there is no entity with given {@code id}
|
||||||
|
*/
|
||||||
|
private OrganizationEntity getEntity(String id) {
|
||||||
|
return getEntity(id, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private OrganizationAdapter getAdapter(String id, boolean failIfNotFound) {
|
private OrganizationEntity getEntity(String id, boolean failIfNotFound) {
|
||||||
OrganizationEntity entity = em.find(OrganizationEntity.class, id);
|
OrganizationEntity entity = em.find(OrganizationEntity.class, id);
|
||||||
|
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
|
@ -192,19 +224,17 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
throw new ModelException("Organization [" + entity.getId() + " does not belong to realm [" + realm.getId() + "]");
|
throw new ModelException("Organization [" + entity.getId() + " does not belong to realm [" + realm.getId() + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OrganizationAdapter(realm, entity);
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private GroupModel createOrganizationGroup(String name) {
|
private GroupModel createOrganizationGroup(String name) {
|
||||||
if (name == null) {
|
throwExceptionIfObjectIsNull(name, "Name of the group");
|
||||||
throw new ModelException("name can not be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
String groupName = getCanonicalGroupName(name);
|
String groupName = getCanonicalGroupName(name);
|
||||||
GroupModel group = groupProvider.getGroupByName(realm, null, name);
|
GroupModel group = groupProvider.getGroupByName(realm, null, name);
|
||||||
|
|
||||||
if (group != null) {
|
if (group != null) {
|
||||||
throw new ModelException("A group with the same name already exist and it is bound to different organization");
|
throw new ModelDuplicateException("A group with the same name already exist and it is bound to different organization");
|
||||||
}
|
}
|
||||||
|
|
||||||
return groupProvider.createGroup(realm, groupName);
|
return groupProvider.createGroup(realm, groupName);
|
||||||
|
@ -215,21 +245,21 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private GroupModel getOrganizationGroup(OrganizationModel organization) {
|
private GroupModel getOrganizationGroup(OrganizationModel organization) {
|
||||||
throwExceptionIfOrganizationIsNull(organization);
|
throwExceptionIfObjectIsNull(organization, "Organization");
|
||||||
OrganizationAdapter adapter = getAdapter(organization.getId());
|
OrganizationEntity entity = getEntity(organization.getId());
|
||||||
|
|
||||||
GroupModel group = groupProvider.getGroupById(realm, adapter.getGroupId());
|
GroupModel group = groupProvider.getGroupById(realm, entity.getGroupId());
|
||||||
|
|
||||||
if (group == null) {
|
if (group == null) {
|
||||||
throw new ModelException("Organization group " + adapter.getGroupId() + " not found");
|
throw new ModelException("Organization group " + entity.getGroupId() + " not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void throwExceptionIfOrganizationIsNull(OrganizationModel organization) {
|
private void throwExceptionIfObjectIsNull(Object object, String objectName) {
|
||||||
if (organization == null) {
|
if (object == null) {
|
||||||
throw new ModelException("organization can not be null");
|
throw new ModelException(String.format("%s cannot be null", objectName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,7 @@
|
||||||
<column name="GROUP_ID" type="VARCHAR(255)">
|
<column name="GROUP_ID" type="VARCHAR(255)">
|
||||||
<constraints nullable="false"/>
|
<constraints nullable="false"/>
|
||||||
</column>
|
</column>
|
||||||
|
<column name="IPD_ALIAS" type="VARCHAR(255)" />
|
||||||
<column name="NAME" type="VARCHAR(255)">
|
<column name="NAME" type="VARCHAR(255)">
|
||||||
<constraints nullable="false"/>
|
<constraints nullable="false"/>
|
||||||
</column>
|
</column>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package org.keycloak.organization;
|
package org.keycloak.organization;
|
||||||
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
import org.keycloak.models.OrganizationModel;
|
import org.keycloak.models.OrganizationModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
@ -29,7 +29,7 @@ import org.keycloak.provider.Provider;
|
||||||
public interface OrganizationProvider extends Provider {
|
public interface OrganizationProvider extends Provider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new organization with given {@code name} to the given realm.
|
* Creates a new organization with given {@code name} to the realm.
|
||||||
* The internal ID of the organization will be created automatically.
|
* The internal ID of the organization will be created automatically.
|
||||||
* @param name String name of the organization.
|
* @param name String name of the organization.
|
||||||
* @throws ModelDuplicateException If there is already an organization with the given name
|
* @throws ModelDuplicateException If there is already an organization with the given name
|
||||||
|
@ -41,60 +41,85 @@ public interface OrganizationProvider extends Provider {
|
||||||
* Returns a {@link OrganizationModel} by its {@code id};
|
* Returns a {@link OrganizationModel} by its {@code id};
|
||||||
*
|
*
|
||||||
* @param id the id of an organization
|
* @param id the id of an organization
|
||||||
* @return the organization with the given {@code id}
|
* @return the organization with the given {@code id} or {@code null} if there is no such an organization.
|
||||||
*/
|
*/
|
||||||
OrganizationModel getById(String id);
|
OrganizationModel getById(String id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the given organization from the given realm.
|
* Removes the given organization from the realm together with the data associated with it, e.g. its members etc.
|
||||||
*
|
*
|
||||||
* @param organization Organization to be removed.
|
* @param organization Organization to be removed.
|
||||||
* @return true if the organization was removed, false if group doesn't exist or doesn't belong to the given realm
|
* @throws ModelException if the organization doesn't exist or doesn't belong to the realm.
|
||||||
|
* @return {@code true} if the organization was removed, {@code false} otherwise
|
||||||
*/
|
*/
|
||||||
boolean remove(OrganizationModel organization);
|
boolean remove(OrganizationModel organization);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes all organizations from the given realm.
|
* Removes all organizations from the realm.
|
||||||
*/
|
*/
|
||||||
void removeAll();
|
void removeAll();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the give {@link UserModel} as a member of the given {@link OrganizationModel}.
|
* Adds the given {@link UserModel} as a member of the given {@link OrganizationModel}.
|
||||||
*
|
*
|
||||||
* @param organization the organization
|
* @param organization the organization
|
||||||
* @param user the user
|
* @param user the user
|
||||||
|
* @throws ModelException if the {@link UserModel} is member of different organization
|
||||||
* @return {@code true} if the user was added as a member. Otherwise, returns {@code false}
|
* @return {@code true} if the user was added as a member. Otherwise, returns {@code false}
|
||||||
*/
|
*/
|
||||||
boolean addMember(OrganizationModel organization, UserModel user);
|
boolean addMember(OrganizationModel organization, UserModel user);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the organizations of the given realm as a stream.
|
* Returns the organizations of the realm as a stream.
|
||||||
* @return Stream of the organizations. Never returns {@code null}.
|
* @return Stream of the organizations. Never returns {@code null}.
|
||||||
*/
|
*/
|
||||||
Stream<OrganizationModel> getAllStream();
|
Stream<OrganizationModel> getAllStream();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the members of a given {@code organization}.
|
* Returns the members of a given {@link OrganizationModel}.
|
||||||
*
|
*
|
||||||
* @param organization the organization
|
* @param organization the organization
|
||||||
* @return the organization with the given {@code id}
|
* @return Stream of the members. Never returns {@code null}.
|
||||||
*/
|
*/
|
||||||
Stream<UserModel> getMembersStream(OrganizationModel organization);
|
Stream<UserModel> getMembersStream(OrganizationModel organization);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the member of an {@code organization} by its {@code id}.
|
* Returns the member of the {@link OrganizationModel} by its {@code id}.
|
||||||
*
|
*
|
||||||
* @param organization the organization
|
* @param organization the organization
|
||||||
* @param id the member id
|
* @param id the member id
|
||||||
* @return the organization with the given {@code id}
|
* @return the member of the {@link OrganizationModel} with the given {@code id}
|
||||||
*/
|
*/
|
||||||
UserModel getMemberById(OrganizationModel organization, String id);
|
UserModel getMemberById(OrganizationModel organization, String id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link OrganizationModel} that a {@code member} belongs to.
|
* Returns the {@link OrganizationModel} that the {@code member} belongs to.
|
||||||
*
|
*
|
||||||
* @param member the member of a organization
|
* @param member the member of a organization
|
||||||
* @return the organization the {@code member} belongs to
|
* @return the organization the {@code member} belongs to or {@code null} if the user doesn't belong to any.
|
||||||
*/
|
*/
|
||||||
OrganizationModel getByMember(UserModel member);
|
OrganizationModel getByMember(UserModel member);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associate the given {@link IdentityProviderModel} with the given {@link OrganizationModel}.
|
||||||
|
*
|
||||||
|
* @param organization the organization
|
||||||
|
* @param identityProvider the identityProvider
|
||||||
|
* @return {@code true} if the identityProvider was associated with the organization. Otherwise, returns {@code false}
|
||||||
|
*/
|
||||||
|
boolean addIdentityProvider(OrganizationModel organization, IdentityProviderModel identityProvider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param organization the organization
|
||||||
|
* @return The identityProvider associated with a given {@code organization} or {@code null} if there is none.
|
||||||
|
*/
|
||||||
|
IdentityProviderModel getIdentityProvider(OrganizationModel organization);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the link between the given {@link OrganizationModel} and identity provider associated with it if such a link exists.
|
||||||
|
*
|
||||||
|
* @param organization the organization
|
||||||
|
* @return {@code true} if the link was removed, {@code false} otherwise
|
||||||
|
*/
|
||||||
|
boolean removeIdentityProvider(OrganizationModel organization);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.organization.admin.resource;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.DELETE;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.PUT;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import jakarta.ws.rs.core.Response.Status;
|
||||||
|
import jakarta.ws.rs.ext.Provider;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.ModelException;
|
||||||
|
import org.keycloak.models.OrganizationModel;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
|
import org.keycloak.organization.OrganizationProvider;
|
||||||
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
|
import org.keycloak.services.ErrorResponse;
|
||||||
|
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||||
|
import org.keycloak.services.resources.admin.IdentityProviderResource;
|
||||||
|
import org.keycloak.services.resources.admin.IdentityProvidersResource;
|
||||||
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||||
|
|
||||||
|
@Provider
|
||||||
|
public class OrganizationIdentityProviderResource {
|
||||||
|
|
||||||
|
private final KeycloakSession session;
|
||||||
|
private final RealmModel realm;
|
||||||
|
private final OrganizationProvider organizationProvider;
|
||||||
|
private final OrganizationModel organization;
|
||||||
|
private final AdminPermissionEvaluator auth;
|
||||||
|
private final AdminEventBuilder adminEvent;
|
||||||
|
|
||||||
|
public OrganizationIdentityProviderResource() {
|
||||||
|
// needed for registering to the JAX-RS stack
|
||||||
|
this(null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrganizationIdentityProviderResource(KeycloakSession session, OrganizationModel organization, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
|
||||||
|
this.session = session;
|
||||||
|
this.realm = session == null ? null : session.getContext().getRealm();
|
||||||
|
this.organizationProvider = session == null ? null : session.getProvider(OrganizationProvider.class);
|
||||||
|
this.organization = organization;
|
||||||
|
this.auth = auth;
|
||||||
|
this.adminEvent = adminEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public Response addIdentityProvider(IdentityProviderRepresentation providerRep) {
|
||||||
|
|
||||||
|
IdentityProviderModel identityProvider = organizationProvider.getIdentityProvider(organization);
|
||||||
|
if (identityProvider != null) {
|
||||||
|
throw ErrorResponse.error("Organization already assigned with an identity provider.", Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
//create IdP within the realm
|
||||||
|
Response response = new IdentityProvidersResource(realm, session, auth, adminEvent).create(providerRep);
|
||||||
|
|
||||||
|
if (Status.CREATED.getStatusCode() == response.getStatus()) {
|
||||||
|
|
||||||
|
//get the created IdP from session
|
||||||
|
identityProvider = realm.getIdentityProviderByAlias(providerRep.getAlias());
|
||||||
|
|
||||||
|
String errorMessage;
|
||||||
|
try {
|
||||||
|
if (organizationProvider.addIdentityProvider(organization, identityProvider)) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
errorMessage = "Assigning the Identity provider with the organization was not succesful.";
|
||||||
|
} catch (ModelException me) {
|
||||||
|
errorMessage = me.getMessage();
|
||||||
|
}
|
||||||
|
throw ErrorResponse.error(errorMessage, Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public IdentityProviderRepresentation getIdentityProvider() {
|
||||||
|
IdentityProviderModel identityProvider = organizationProvider.getIdentityProvider(organization);
|
||||||
|
return identityProvider == null ? null : toRepresentation(identityProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
public Response delete() {
|
||||||
|
IdentityProviderModel identityProvider = getIdentityProviderModel();
|
||||||
|
|
||||||
|
Response response = getIdentityProviderResource(identityProvider).delete();
|
||||||
|
|
||||||
|
// remove link between IdP and the organization if the IdP deletetion was successful
|
||||||
|
if (Status.NO_CONTENT.getStatusCode() == response.getStatus()) {
|
||||||
|
String errorMessage;
|
||||||
|
try {
|
||||||
|
if (organizationProvider.removeIdentityProvider(organization)) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
errorMessage = "Removing the Identity provider from the organization was not succesful.";
|
||||||
|
} catch (ModelException me) {
|
||||||
|
errorMessage = me.getMessage();
|
||||||
|
}
|
||||||
|
throw ErrorResponse.error(errorMessage, Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public Response update(IdentityProviderRepresentation providerRep) {
|
||||||
|
IdentityProviderModel identityProvider = getIdentityProviderModel();
|
||||||
|
|
||||||
|
Response response = getIdentityProviderResource(identityProvider).update(providerRep);
|
||||||
|
|
||||||
|
//update link between IdP and the organization if the update of IdP was successful and the IdP alias differs
|
||||||
|
if (Status.NO_CONTENT.getStatusCode() == response.getStatus() &&
|
||||||
|
! Objects.equals(identityProvider.getAlias(), providerRep.getAlias())) {
|
||||||
|
|
||||||
|
//get the updated IdP from session
|
||||||
|
identityProvider = realm.getIdentityProviderByAlias(providerRep.getAlias());
|
||||||
|
|
||||||
|
String errorMessage;
|
||||||
|
try {
|
||||||
|
if (organizationProvider.removeIdentityProvider(organization) &&
|
||||||
|
organizationProvider.addIdentityProvider(organization, identityProvider)) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
errorMessage = "Updating the Identity provider was not succesful.";
|
||||||
|
} catch (ModelException me) {
|
||||||
|
errorMessage = me.getMessage();
|
||||||
|
}
|
||||||
|
throw ErrorResponse.error(errorMessage, Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IdentityProviderRepresentation toRepresentation(IdentityProviderModel idp) {
|
||||||
|
return ModelToRepresentation.toRepresentation(realm, idp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IdentityProviderResource getIdentityProviderResource(IdentityProviderModel idp) {
|
||||||
|
return new IdentityProviderResource(auth, realm, session, idp, adminEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IdentityProviderModel getIdentityProviderModel() {
|
||||||
|
IdentityProviderModel identityProvider = organizationProvider.getIdentityProvider(organization);
|
||||||
|
if (identityProvider == null) {
|
||||||
|
throw ErrorResponse.error("Organization doesn't have assigned an identity provider.", Status.NOT_FOUND);
|
||||||
|
}
|
||||||
|
return identityProvider;
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,6 @@ package org.keycloak.organization.admin.resource;
|
||||||
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import jakarta.ws.rs.BadRequestException;
|
|
||||||
import jakarta.ws.rs.Consumes;
|
import jakarta.ws.rs.Consumes;
|
||||||
import jakarta.ws.rs.DELETE;
|
import jakarta.ws.rs.DELETE;
|
||||||
import jakarta.ws.rs.GET;
|
import jakarta.ws.rs.GET;
|
||||||
|
@ -39,11 +38,11 @@ import org.keycloak.models.ModelException;
|
||||||
import org.keycloak.models.OrganizationModel;
|
import org.keycloak.models.OrganizationModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.organization.OrganizationProvider;
|
import org.keycloak.organization.OrganizationProvider;
|
||||||
import org.keycloak.representations.idm.OrganizationRepresentation;
|
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.services.ErrorResponse;
|
||||||
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||||
import org.keycloak.services.resources.admin.UserResource;
|
import org.keycloak.services.resources.admin.UserResource;
|
||||||
import org.keycloak.services.resources.admin.UsersResource;
|
import org.keycloak.services.resources.admin.UsersResource;
|
||||||
|
@ -82,26 +81,26 @@ public class OrganizationMemberResource {
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
public Response addMember(UserRepresentation rep) {
|
public Response addMember(UserRepresentation rep) {
|
||||||
if (rep == null || !Objects.equals(rep.getUsername(), rep.getEmail())) {
|
if (rep == null || !Objects.equals(rep.getUsername(), rep.getEmail())) {
|
||||||
throw new BadRequestException("To add a member to the organization it is expected the username and the email is the same.");
|
throw ErrorResponse.error("To add a member to the organization it is expected the username and the email is the same.", Status.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
UsersResource usersResource = new UsersResource(session, auth, adminEvent);
|
UsersResource usersResource = new UsersResource(session, auth, adminEvent);
|
||||||
Response response = usersResource.createUser(rep);
|
Response response = usersResource.createUser(rep);
|
||||||
|
|
||||||
if (Status.CREATED.getStatusCode() == response.getStatus()) {
|
if (Status.CREATED.getStatusCode() == response.getStatus()) {
|
||||||
RealmModel realm = session.getContext().getRealm();
|
|
||||||
UserModel member = session.users().getUserByUsername(realm, rep.getEmail());
|
|
||||||
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
|
|
||||||
|
|
||||||
|
UserModel member = session.users().getUserByUsername(realm, rep.getEmail());
|
||||||
|
|
||||||
|
String errorMessage;
|
||||||
try {
|
try {
|
||||||
if (provider.addMember(organization, member)) {
|
if (provider.addMember(organization, member)) {
|
||||||
return Response.created(session.getContext().getUri().getAbsolutePathBuilder().path(member.getId()).build()).build();
|
return response;
|
||||||
}
|
}
|
||||||
|
errorMessage = "Assigning the User as member of the organization was not succesful.";
|
||||||
} catch (ModelException me) {
|
} catch (ModelException me) {
|
||||||
throw new BadRequestException(me.getMessage());
|
errorMessage = me.getMessage();
|
||||||
}
|
}
|
||||||
|
throw ErrorResponse.error(errorMessage, Status.BAD_REQUEST);
|
||||||
throw new BadRequestException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
@ -118,7 +117,7 @@ public class OrganizationMemberResource {
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public UserRepresentation get(@PathParam("id") String id) {
|
public UserRepresentation get(@PathParam("id") String id) {
|
||||||
if (StringUtil.isBlank(id)) {
|
if (StringUtil.isBlank(id)) {
|
||||||
throw new BadRequestException();
|
throw ErrorResponse.error("id cannot be null", Status.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
return toRepresentation(getMember(id));
|
return toRepresentation(getMember(id));
|
||||||
|
@ -128,7 +127,7 @@ public class OrganizationMemberResource {
|
||||||
@DELETE
|
@DELETE
|
||||||
public Response delete(@PathParam("id") String id) {
|
public Response delete(@PathParam("id") String id) {
|
||||||
if (StringUtil.isBlank(id)) {
|
if (StringUtil.isBlank(id)) {
|
||||||
throw new BadRequestException();
|
throw ErrorResponse.error("id cannot be null", Status.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserModel member = getMember(id);
|
UserModel member = getMember(id);
|
||||||
|
@ -148,7 +147,7 @@ public class OrganizationMemberResource {
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public OrganizationRepresentation getOrganization(@PathParam("id") String id) {
|
public OrganizationRepresentation getOrganization(@PathParam("id") String id) {
|
||||||
if (StringUtil.isBlank(id)) {
|
if (StringUtil.isBlank(id)) {
|
||||||
throw new BadRequestException();
|
throw ErrorResponse.error("id cannot be null", Status.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserModel member = getMember(id);
|
UserModel member = getMember(id);
|
||||||
|
|
|
@ -119,6 +119,11 @@ public class OrganizationResource {
|
||||||
return new OrganizationMemberResource(session, organization, auth, adminEvent);
|
return new OrganizationMemberResource(session, organization, auth, adminEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Path("{id}/identity-provider")
|
||||||
|
public OrganizationIdentityProviderResource identityProvider(@PathParam("id") String id) {
|
||||||
|
return new OrganizationIdentityProviderResource(session, getOrganization(id), auth, adminEvent);
|
||||||
|
}
|
||||||
|
|
||||||
private OrganizationModel getOrganization(String id) {
|
private OrganizationModel getOrganization(String id) {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
throw new BadRequestException();
|
throw new BadRequestException();
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.testsuite.organization.admin;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.admin.client.resource.OrganizationIdentityProviderResource;
|
||||||
|
import org.keycloak.admin.client.resource.OrganizationResource;
|
||||||
|
import org.keycloak.common.Profile.Feature;
|
||||||
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
|
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||||
|
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||||
|
|
||||||
|
@EnableFeature(Feature.ORGANIZATION)
|
||||||
|
public class OrganizationIdentityProviderTest extends AbstractOrganizationTest {
|
||||||
|
|
||||||
|
private final String idpAlias = "org-identity-provider";
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void addCleanups() {
|
||||||
|
addCleanupIdP(idpAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCRUD() {
|
||||||
|
OrganizationIdentityProviderResource orgIdPResource = testRealm().organizations().get(createOrganization().getId()).identityProvider();
|
||||||
|
|
||||||
|
//create, read
|
||||||
|
IdentityProviderRepresentation idpRepresentation = createRep(idpAlias, "oidc");
|
||||||
|
try (Response response = orgIdPResource.create(idpRepresentation)) {
|
||||||
|
assertThat(response.getStatus(), equalTo(Response.Status.CREATED.getStatusCode()));
|
||||||
|
}
|
||||||
|
idpRepresentation = orgIdPResource.toRepresentation();
|
||||||
|
assertThat(idpRepresentation.getAlias(), equalTo(idpAlias));
|
||||||
|
|
||||||
|
String updatedIdpAlias = "updated-org-identity-provider";
|
||||||
|
//update
|
||||||
|
idpRepresentation.setAlias(updatedIdpAlias);
|
||||||
|
try (Response response = orgIdPResource.update(idpRepresentation)) {
|
||||||
|
assertThat(response.getStatus(), equalTo(Response.Status.NO_CONTENT.getStatusCode()));
|
||||||
|
addCleanupIdP(updatedIdpAlias);
|
||||||
|
}
|
||||||
|
assertThat(orgIdPResource.toRepresentation().getAlias(), equalTo(updatedIdpAlias));
|
||||||
|
|
||||||
|
//delete
|
||||||
|
try (Response response = orgIdPResource.delete()) {
|
||||||
|
assertThat(response.getStatus(), equalTo(Response.Status.NO_CONTENT.getStatusCode()));
|
||||||
|
}
|
||||||
|
assertThat(orgIdPResource.toRepresentation(), nullValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void tryCreateSecondIdp() {
|
||||||
|
OrganizationIdentityProviderResource orgIdPResource = testRealm().organizations().get(createOrganization().getId()).identityProvider();
|
||||||
|
|
||||||
|
IdentityProviderRepresentation idpRepresentation = createRep(idpAlias, "oidc");
|
||||||
|
try (Response response = orgIdPResource.create(idpRepresentation)) {
|
||||||
|
assertThat(response.getStatus(), equalTo(Response.Status.CREATED.getStatusCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
idpRepresentation.setAlias("another-idp");
|
||||||
|
try (Response response = orgIdPResource.create(idpRepresentation)) {
|
||||||
|
assertThat(response.getStatus(), equalTo(Response.Status.BAD_REQUEST.getStatusCode()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = jakarta.ws.rs.NotFoundException.class)
|
||||||
|
public void removingOrgShouldRemoveIdP() {
|
||||||
|
OrganizationRepresentation orgRep = createOrganization();
|
||||||
|
OrganizationResource orgResource = testRealm().organizations().get(orgRep.getId());
|
||||||
|
|
||||||
|
OrganizationIdentityProviderResource orgIdPResource = orgResource.identityProvider();
|
||||||
|
|
||||||
|
IdentityProviderRepresentation idpRepresentation = createRep(idpAlias, "oidc");
|
||||||
|
try (Response response = orgIdPResource.create(idpRepresentation)) {
|
||||||
|
assertThat(response.getStatus(), equalTo(Response.Status.CREATED.getStatusCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Response response = orgResource.delete()) {
|
||||||
|
assertThat(response.getStatus(), equalTo(Response.Status.NO_CONTENT.getStatusCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
testRealm().identityProviders().get(idpAlias).toRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void tryUpdateAndRemoveIdPNotAssignedToOrg() {
|
||||||
|
OrganizationRepresentation orgRep = createOrganization();
|
||||||
|
OrganizationResource orgResource = testRealm().organizations().get(orgRep.getId());
|
||||||
|
|
||||||
|
OrganizationIdentityProviderResource orgIdPResource = orgResource.identityProvider();
|
||||||
|
|
||||||
|
IdentityProviderRepresentation idpRepresentation = createRep(idpAlias, "oidc");
|
||||||
|
//create IdP in realm not bound to Org
|
||||||
|
testRealm().identityProviders().create(idpRepresentation).close();
|
||||||
|
|
||||||
|
try (Response response = orgIdPResource.update(idpRepresentation)) {
|
||||||
|
assertThat(response.getStatus(), equalTo(Response.Status.NOT_FOUND.getStatusCode()));
|
||||||
|
}
|
||||||
|
try (Response response = orgIdPResource.delete()) {
|
||||||
|
assertThat(response.getStatus(), equalTo(Response.Status.NOT_FOUND.getStatusCode()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IdentityProviderRepresentation createRep(String alias, String providerId) {
|
||||||
|
IdentityProviderRepresentation idp = new IdentityProviderRepresentation();
|
||||||
|
|
||||||
|
idp.setAlias(alias);
|
||||||
|
idp.setDisplayName(alias);
|
||||||
|
idp.setProviderId(providerId);
|
||||||
|
idp.setEnabled(true);
|
||||||
|
return idp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCleanupIdP(String alias) {
|
||||||
|
getCleanup().addCleanup(() -> testRealm().identityProviders().get(alias).remove());
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,3 +17,4 @@ TransactionsTest
|
||||||
UserProfileTest
|
UserProfileTest
|
||||||
org.keycloak.testsuite.admin.**
|
org.keycloak.testsuite.admin.**
|
||||||
org.keycloak.testsuite.authz.**ManagementTest
|
org.keycloak.testsuite.authz.**ManagementTest
|
||||||
|
org.keycloak.testsuite.organization.admin.**
|
||||||
|
|
Loading…
Reference in a new issue