Merge pull request #1158 from mposolda/master
KEYCLOAK-1070 Persistent grants - step 1
This commit is contained in:
commit
9937be1cdf
49 changed files with 1532 additions and 117 deletions
|
@ -35,10 +35,43 @@
|
|||
<addColumn tableName="CREDENTIAL">
|
||||
<column name="CREATED_DATE" type="BIGINT"/>
|
||||
</addColumn>
|
||||
<createTable tableName="GRANTED_CONSENT">
|
||||
<column name="ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="CLIENT_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="USER_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<createTable tableName="GRANTED_CONSENT_ROLE">
|
||||
<column name="GRANTED_CONSENT_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="ROLE_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<createTable tableName="GRANTED_CONSENT_PROT_MAPPER">
|
||||
<column name="GRANTED_CONSENT_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="PROTOCOL_MAPPER_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_IDPM" tableName="IDENTITY_PROVIDER_MAPPER"/>
|
||||
<addPrimaryKey columnNames="IDP_MAPPER_ID, NAME" constraintName="CONSTRAINT_IDPMConfig" tableName="IDP_MAPPER_CONFIG"/>
|
||||
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_GRNTCSNT_PM" tableName="GRANTED_CONSENT"/>
|
||||
<addPrimaryKey columnNames="GRANTED_CONSENT_ID, ROLE_ID" constraintName="CONSTRAINT_GRNTCSNT_ROLE_PM" tableName="GRANTED_CONSENT_ROLE"/>
|
||||
<addPrimaryKey columnNames="GRANTED_CONSENT_ID, PROTOCOL_MAPPER_ID" constraintName="CONSTRAINT_GRNTCSNT_PRM_PM" tableName="GRANTED_CONSENT_PROT_MAPPER"/>
|
||||
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="IDENTITY_PROVIDER_MAPPER" constraintName="FK_IDPM_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
|
||||
<addForeignKeyConstraint baseColumnNames="IDP_MAPPER_ID" baseTableName="IDP_MAPPER_CONFIG" constraintName="FK_IDPMConfig" referencedColumnNames="ID" referencedTableName="IDENTITY_PROVIDER_MAPPER"/>
|
||||
<addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="GRANTED_CONSENT" constraintName="FK_GRNTCSNT_USER" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
|
||||
<addForeignKeyConstraint baseColumnNames="GRANTED_CONSENT_ID" baseTableName="GRANTED_CONSENT_ROLE" constraintName="FK_GRNTCSNT_ROLE_GR" referencedColumnNames="ID" referencedTableName="GRANTED_CONSENT"/>
|
||||
<addForeignKeyConstraint baseColumnNames="GRANTED_CONSENT_ID" baseTableName="GRANTED_CONSENT_PROT_MAPPER" constraintName="FK_GRNTCSNT_PRM_GR" referencedColumnNames="ID" referencedTableName="GRANTED_CONSENT"/>
|
||||
|
||||
<addColumn tableName="CLIENT">
|
||||
<column name="CONSENT_REQUIRED" type="BOOLEAN" defaultValueBoolean="false">
|
||||
|
@ -68,6 +101,7 @@
|
|||
|
||||
<dropUniqueConstraint tableName="KEYCLOAK_ROLE" constraintName="UK_J3RWUVD56ONTGSUHOGM184WW2"/>
|
||||
<addUniqueConstraint columnNames="NAME,CLIENT_REALM_CONSTRAINT" constraintName="UK_J3RWUVD56ONTGSUHOGM184WW2-2" tableName="KEYCLOAK_ROLE"/>
|
||||
<addUniqueConstraint columnNames="CLIENT_ID, USER_ID" constraintName="UK_JKUWUVD56ONTGSUHOGM8UEWRT" tableName="GRANTED_CONSENT"/>
|
||||
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
<class>org.keycloak.models.jpa.entities.IdentityProviderMapperEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.ClientIdentityProviderMappingEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.ProtocolMapperEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.GrantedConsentEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.GrantedConsentRoleEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.GrantedConsentProtocolMapperEntity</class>
|
||||
|
||||
<!-- JpaUserSessionProvider -->
|
||||
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionEntity</class>
|
||||
|
|
|
@ -23,5 +23,6 @@ public interface Details {
|
|||
String UPDATED_REFRESH_TOKEN_ID = "updated_refresh_token_id";
|
||||
String NODE_HOST = "node_host";
|
||||
String REASON = "reason";
|
||||
String REVOKED_CLIENT = "revoked_client";
|
||||
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ public enum EventType {
|
|||
REMOVE_TOTP(true),
|
||||
REMOVE_TOTP_ERROR(true),
|
||||
|
||||
REVOKE_GRANT(true),
|
||||
|
||||
SEND_VERIFY_EMAIL(true),
|
||||
SEND_VERIFY_EMAIL_ERROR(true),
|
||||
SEND_RESET_PASSWORD(true),
|
||||
|
|
|
@ -5,6 +5,6 @@ package org.keycloak.account;
|
|||
*/
|
||||
public enum AccountPages {
|
||||
|
||||
ACCOUNT, PASSWORD, TOTP, FEDERATED_IDENTITY, LOG, SESSIONS;
|
||||
ACCOUNT, PASSWORD, TOTP, FEDERATED_IDENTITY, LOG, SESSIONS, ACCESS;
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import javax.ws.rs.core.UriInfo;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.account.AccountPages;
|
||||
import org.keycloak.account.AccountProvider;
|
||||
import org.keycloak.account.freemarker.model.AccessBean;
|
||||
import org.keycloak.account.freemarker.model.AccountBean;
|
||||
import org.keycloak.account.freemarker.model.AccountFederatedIdentityBean;
|
||||
import org.keycloak.account.freemarker.model.FeaturesBean;
|
||||
|
@ -37,6 +38,7 @@ import org.keycloak.freemarker.FreeMarkerUtil;
|
|||
import org.keycloak.freemarker.LocaleHelper;
|
||||
import org.keycloak.freemarker.Theme;
|
||||
import org.keycloak.freemarker.ThemeProvider;
|
||||
import org.keycloak.freemarker.beans.AdvancedMessageFormatterMethod;
|
||||
import org.keycloak.freemarker.beans.LocaleBean;
|
||||
import org.keycloak.freemarker.beans.MessageBean;
|
||||
import org.keycloak.freemarker.beans.MessageFormatterMethod;
|
||||
|
@ -183,6 +185,10 @@ public class FreeMarkerAccountProvider implements AccountProvider {
|
|||
case SESSIONS:
|
||||
attributes.put("sessions", new SessionsBean(realm, sessions));
|
||||
break;
|
||||
case ACCESS:
|
||||
attributes.put("access", new AccessBean(realm, user, uriInfo.getBaseUri(), stateChecker));
|
||||
attributes.put("advancedMsg", new AdvancedMessageFormatterMethod(locale, messagesBundle));
|
||||
break;
|
||||
case PASSWORD:
|
||||
attributes.put("password", new PasswordBean(passwordSet));
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ public class Templates {
|
|||
return "log.ftl";
|
||||
case SESSIONS:
|
||||
return "sessions.ftl";
|
||||
case ACCESS:
|
||||
return "access.ftl";
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package org.keycloak.account.freemarker.model;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.GrantedConsentModel;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.util.MultivaluedHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class AccessBean {
|
||||
|
||||
private List<ClientGrantBean> clientGrants = new LinkedList<ClientGrantBean>();
|
||||
|
||||
public AccessBean(RealmModel realm, UserModel user, URI baseUri, String stateChecker) {
|
||||
List<GrantedConsentModel> grantedConsents = user.getGrantedConsents();
|
||||
for (GrantedConsentModel consent : grantedConsents) {
|
||||
ClientModel client = realm.getClientById(consent.getClientId());
|
||||
|
||||
List<RoleModel> realmRolesGranted = new LinkedList<RoleModel>();
|
||||
MultivaluedHashMap<String, RoleModel> resourceRolesGranted = new MultivaluedHashMap<String, RoleModel>();
|
||||
for (String roleId : consent.getGrantedRoles()) {
|
||||
RoleModel role = realm.getRoleById(roleId);
|
||||
if (role.getContainer() instanceof RealmModel) {
|
||||
realmRolesGranted.add(role);
|
||||
} else {
|
||||
resourceRolesGranted.add(((ClientModel) role.getContainer()).getClientId(), role);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> claimsGranted = new LinkedList<String>();
|
||||
for (String protocolMapperId : consent.getGrantedProtocolMappers()) {
|
||||
ProtocolMapperModel protocolMapper = client.getProtocolMapperById(protocolMapperId);
|
||||
claimsGranted.add(protocolMapper.getConsentText());
|
||||
}
|
||||
|
||||
ClientGrantBean clientGrant = new ClientGrantBean(realmRolesGranted, resourceRolesGranted, client, claimsGranted);
|
||||
clientGrants.add(clientGrant);
|
||||
}
|
||||
}
|
||||
|
||||
public List<ClientGrantBean> getClientGrants() {
|
||||
return clientGrants;
|
||||
}
|
||||
|
||||
public static class ClientGrantBean {
|
||||
|
||||
private final List<RoleModel> realmRolesGranted;
|
||||
private final MultivaluedHashMap<String, RoleModel> resourceRolesGranted;
|
||||
private final ClientModel client;
|
||||
private final List<String> claimsGranted;
|
||||
|
||||
public ClientGrantBean(List<RoleModel> realmRolesGranted, MultivaluedHashMap<String, RoleModel> resourceRolesGranted,
|
||||
ClientModel client, List<String> claimsGranted) {
|
||||
this.realmRolesGranted = realmRolesGranted;
|
||||
this.resourceRolesGranted = resourceRolesGranted;
|
||||
this.client = client;
|
||||
this.claimsGranted = claimsGranted;
|
||||
}
|
||||
|
||||
public List<RoleModel> getRealmRolesGranted() {
|
||||
return realmRolesGranted;
|
||||
}
|
||||
|
||||
public MultivaluedHashMap<String, RoleModel> getResourceRolesGranted() {
|
||||
return resourceRolesGranted;
|
||||
}
|
||||
|
||||
public ClientModel getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public List<String> getClaimsGranted() {
|
||||
return claimsGranted;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -59,6 +59,10 @@ public class UrlBean {
|
|||
return Urls.accountSessionsLogoutPage(baseQueryURI, realm, stateChecker).toString();
|
||||
}
|
||||
|
||||
public String getRevokeClientUrl() {
|
||||
return Urls.accountRevokeClientPage(baseQueryURI, realm).toString();
|
||||
}
|
||||
|
||||
public String getTotpRemoveUrl() {
|
||||
return Urls.accountTotpRemove(baseQueryURI, realm, stateChecker).toString();
|
||||
}
|
||||
|
|
55
forms/common-themes/src/main/resources/theme/base/account/access.ftl
Executable file
55
forms/common-themes/src/main/resources/theme/base/account/access.ftl
Executable file
|
@ -0,0 +1,55 @@
|
|||
<#import "template.ftl" as layout>
|
||||
<@layout.mainLayout active='access' bodyClass='access'; section>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<h2>${msg("accessHtmlTitle")}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action="${url.revokeClientUrl}" method="post">
|
||||
<input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker}">
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>${msg("client")}</td>
|
||||
<td>${msg("grantedPersonalInfo")}</td>
|
||||
<td>${msg("grantedPermissions")}</td>
|
||||
<td>${msg("action")}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<#list access.clientGrants as clientGrant>
|
||||
<tr>
|
||||
<td><#if clientGrant.client.baseUrl??><a href="${clientGrant.client.baseUrl}">${clientGrant.client.clientId}</a><#else>${clientGrant.client.clientId}</#if></td>
|
||||
<td>
|
||||
<#list clientGrant.claimsGranted as claim>
|
||||
${advancedMsg(claim)}<#if claim_has_next>, </#if>
|
||||
</#list>
|
||||
</td>
|
||||
<td>
|
||||
<#list clientGrant.realmRolesGranted as role>
|
||||
<#if role.description??>${advancedMsg(role.description)}<#else>${advancedMsg(role.name)}</#if>
|
||||
<#if role_has_next>, </#if>
|
||||
</#list>
|
||||
<#list clientGrant.resourceRolesGranted?keys as resource>
|
||||
<#if clientGrant.realmRolesGranted?has_content>, </#if>
|
||||
<#list clientGrant.resourceRolesGranted[resource] as role>
|
||||
<#if role.description??>${advancedMsg(role.description)}<#else>${advancedMsg(role.name)}</#if>
|
||||
${msg("inResource", resource)}
|
||||
<#if role_has_next>, </#if>
|
||||
</#list>
|
||||
</#list>
|
||||
</td>
|
||||
<td>
|
||||
<button type='submit' class='btn btn-primary' id='revoke-${clientGrant.client.clientId}' name='clientId' value="${clientGrant.client.id}">${msg("revoke")}</button>
|
||||
</td>
|
||||
</tr>
|
||||
</#list>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
</@layout.mainLayout>
|
|
@ -1,82 +0,0 @@
|
|||
<#-- TODO: Only a placeholder, implementation needed -->
|
||||
<#import "template.ftl" as layout>
|
||||
<@layout.mainLayout active='access' bodyClass='access'; section>
|
||||
|
||||
<#if section = "header">
|
||||
|
||||
<h2>Manage Authorised Access</h2>
|
||||
|
||||
<#elseif section = "content">
|
||||
|
||||
<p class="info">Services requested access to your following accounts:</p>
|
||||
<table class="list">
|
||||
<caption>Table of services</caption>
|
||||
<tbody>
|
||||
<tr class="collapsed">
|
||||
<td class="provider"><a href="#eventjuggler">EventJuggler</a></td>
|
||||
<td class="soft">3 services accessing</td>
|
||||
</tr>
|
||||
<tr class="expanded hidden" id="#eventjuggler">
|
||||
<td colspan="2">
|
||||
<span class="provider">EventJuggler</span>
|
||||
<p>You have granted the following services access to your EventJuggler account:</p>
|
||||
<form>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="item">Event Announcer - Events info</span>
|
||||
<button class="link"><span class="icon-cancel-circle">Icon: remove</span>Revoke Access</button>
|
||||
</li>
|
||||
<li>
|
||||
<span class="item">Facebook - Events info</span>
|
||||
<button class="link"><span class="icon-cancel-circle">Icon: remove</span>Revoke Access</button>
|
||||
</li>
|
||||
<li>
|
||||
<span class="item">Event Announcer - Profile info</span>
|
||||
<span class="status red">Access revoked</span>
|
||||
<button class="link">Undo</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="primary">Save</button>
|
||||
<button type="submit">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="collapsed">
|
||||
<td class="provider"><a href="#another-service">Another Service</a></td>
|
||||
<td class="soft">5 services accessing</td>
|
||||
</tr>
|
||||
<tr class="expanded hidden" id="another-service">
|
||||
<td colspan="2">
|
||||
<span class="provider">EventJuggler</span>
|
||||
<p>You have granted the following services access to your EventJuggler account:</p>
|
||||
<form>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="item">Event Announcer - Events info</span>
|
||||
<button class="link"><span class="icon-cancel-circle">Icon: remove</span>Revoke Access</button>
|
||||
</li>
|
||||
<li>
|
||||
<span class="item">Facebook - Events info</span>
|
||||
<button class="link"><span class="icon-cancel-circle">Icon: remove</span>Revoke Access</button>
|
||||
</li>
|
||||
<li>
|
||||
<span class="item">Event Announcer - Profile info</span>
|
||||
<span class="status red">Access revoked</span>
|
||||
<button class="link">Undo</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="primary">Save</button>
|
||||
<button type="submit">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</#if>
|
||||
</@layout.mainLayout>
|
|
@ -12,20 +12,45 @@ changePasswordHtmlTitle=Change Password
|
|||
sessionsHtmlTitle=Sessions
|
||||
accountManagementTitle=Keycloak Account Management
|
||||
authenticatorTitle=Authenticator
|
||||
accessHtmlTitle=Manage Granted Permissions
|
||||
|
||||
authenticatorCode=One-time code
|
||||
email=Email
|
||||
firstName=First name
|
||||
givenName=Given name
|
||||
fullName=Full name
|
||||
lastName=Last name
|
||||
familyName=Family name
|
||||
password=Password
|
||||
passwordConfirm=Confirmation
|
||||
passwordNew=New Password
|
||||
username=Username
|
||||
address=Address
|
||||
street=Street
|
||||
locality=City or Locality
|
||||
region=State, Province, or Region
|
||||
postal_code=Zip or Postal code
|
||||
country=Country
|
||||
emailVerified=Email verified
|
||||
gssDelegationCredential=gss delegation credential
|
||||
|
||||
role_admin=Admin
|
||||
role_realm-admin=Realm Admin
|
||||
role_create-realm=Create realm
|
||||
role_view-realm=View realm
|
||||
role_view-users=View users
|
||||
role_view-applications=View applications
|
||||
role_view-clients=View clients
|
||||
role_view-events=View events
|
||||
role_view-identity-providers=View identity providers
|
||||
role_manage-realm=Manage realm
|
||||
role_manage-users=Manage users
|
||||
role_manage-applications=Manage applications
|
||||
role_manage-identity-providers=Manage identity providers
|
||||
role_manage-clients=Manage clients
|
||||
role_manage-events=Manage events
|
||||
role_view-profile=View profile
|
||||
|
||||
|
||||
requiredFields=Required fields
|
||||
allFieldsRequired=All fields required
|
||||
|
@ -49,6 +74,13 @@ federatedIdentity=Federated Identity
|
|||
authenticator=Authenticator
|
||||
sessions=Sessions
|
||||
log=Log
|
||||
access=Access
|
||||
|
||||
grantedPersonalInfo=Granted Personal Info
|
||||
grantedPermissions=Granted Permissions
|
||||
action=Action
|
||||
inResource=in <strong>{0}</strong>
|
||||
revoke=Revoke Access
|
||||
|
||||
configureAuthenticators=Configured Authenticators
|
||||
mobile=Mobile
|
||||
|
@ -74,6 +106,8 @@ readOnlyPasswordMessage=You can''t update your password as your account is read
|
|||
successTotpMessage=Mobile authenticator configured.
|
||||
successTotpRemovedMessage=Mobile authenticator removed.
|
||||
|
||||
successGrantRevokedMessage=Access revoked successfully.
|
||||
|
||||
accountUpdatedMessage=Your account has been updated.
|
||||
accountPasswordUpdatedMessage=Your password has been updated.
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
<li class="<#if active=='totp'>active</#if>"><a href="${url.totpUrl}">${msg("authenticator")}</a></li>
|
||||
<#if features.identityFederation><li class="<#if active=='social'>active</#if>"><a href="${url.socialUrl}">${msg("federatedIdentity")}</a></li></#if>
|
||||
<li class="<#if active=='sessions'>active</#if>"><a href="${url.sessionsUrl}">${msg("sessions")}</a></li>
|
||||
<li class="<#if active=='access'>active</#if>"><a href="${url.accessUrl}">${msg("access")}</a></li>
|
||||
<#if features.log><li class="<#if active=='log'>active</#if>"><a href="${url.logUrl}">${msg("log")}</a></li></#if>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
<#-- TODO: Only a placeholder, implementation needed -->
|
||||
<#import "template.ftl" as layout>
|
||||
<@layout.registrationLayout bodyClass="oauth"; section>
|
||||
<#if section = "title">
|
||||
|
|
|
@ -10,6 +10,7 @@ import javax.ws.rs.core.UriInfo;
|
|||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -41,7 +42,7 @@ public interface LoginFormsProvider extends Provider {
|
|||
|
||||
public LoginFormsProvider setClientSessionCode(String accessCode);
|
||||
|
||||
public LoginFormsProvider setAccessRequest(List<RoleModel> realmRolesRequested, MultivaluedMap<String,RoleModel> resourceRolesRequested);
|
||||
public LoginFormsProvider setAccessRequest(List<RoleModel> realmRolesRequested, MultivaluedMap<String,RoleModel> resourceRolesRequested, List<ProtocolMapperModel> protocolMappers);
|
||||
public LoginFormsProvider setAccessRequest(String message);
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.keycloak.login.freemarker.model.UrlBean;
|
|||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -66,6 +67,7 @@ import java.util.concurrent.TimeUnit;
|
|||
private Response.Status status;
|
||||
private List<RoleModel> realmRolesRequested;
|
||||
private MultivaluedMap<String, RoleModel> resourceRolesRequested;
|
||||
private List<ProtocolMapperModel> protocolMappersRequested;
|
||||
private MultivaluedMap<String, String> queryParams;
|
||||
private Map<String, String> httpResponseHeaders = new HashMap<String, String>();
|
||||
private String accessRequestMessage;
|
||||
|
@ -243,7 +245,7 @@ import java.util.concurrent.TimeUnit;
|
|||
attributes.put("register", new RegisterBean(formData));
|
||||
break;
|
||||
case OAUTH_GRANT:
|
||||
attributes.put("oauth", new OAuthGrantBean(accessCode, clientSession, client, realmRolesRequested, resourceRolesRequested, this.accessRequestMessage));
|
||||
attributes.put("oauth", new OAuthGrantBean(accessCode, clientSession, client, realmRolesRequested, resourceRolesRequested, protocolMappersRequested, this.accessRequestMessage));
|
||||
attributes.put("advancedMsg", new AdvancedMessageFormatterMethod(locale, messagesBundle));
|
||||
break;
|
||||
case CODE:
|
||||
|
@ -366,9 +368,10 @@ import java.util.concurrent.TimeUnit;
|
|||
}
|
||||
|
||||
@Override
|
||||
public LoginFormsProvider setAccessRequest(List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested) {
|
||||
public LoginFormsProvider setAccessRequest(List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested, List<ProtocolMapperModel> protocolMappersRequested) {
|
||||
this.realmRolesRequested = realmRolesRequested;
|
||||
this.resourceRolesRequested = resourceRolesRequested;
|
||||
this.protocolMappersRequested = protocolMappersRequested;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,20 +42,18 @@ public class OAuthGrantBean {
|
|||
private ClientModel client;
|
||||
private List<String> claimsRequested;
|
||||
|
||||
public OAuthGrantBean(String code, ClientSessionModel clientSession, ClientModel client, List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested, String accessRequestMessage) {
|
||||
public OAuthGrantBean(String code, ClientSessionModel clientSession, ClientModel client, List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested,
|
||||
List<ProtocolMapperModel> protocolMappersRequested, String accessRequestMessage) {
|
||||
this.code = code;
|
||||
this.client = client;
|
||||
this.realmRolesRequested = realmRolesRequested;
|
||||
this.resourceRolesRequested = resourceRolesRequested;
|
||||
this.accessRequestMessage = accessRequestMessage;
|
||||
|
||||
// todo support locale
|
||||
List<String> claims = new LinkedList<String>();
|
||||
if (clientSession != null) {
|
||||
for (ProtocolMapperModel model : client.getProtocolMappers()) {
|
||||
if (model.isConsentRequired() && model.getProtocol().equals(clientSession.getAuthMethod()) && model.getConsentText() != null) {
|
||||
claims.add(model.getConsentText());
|
||||
}
|
||||
if (protocolMappersRequested != null) {
|
||||
for (ProtocolMapperModel model : protocolMappersRequested) {
|
||||
claims.add(model.getConsentText());
|
||||
}
|
||||
}
|
||||
if (claims.size() > 0) this.claimsRequested = claims;
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class GrantedConsentModel {
|
||||
|
||||
private final String clientId;
|
||||
private Set<String> protocolMapperIds = new HashSet<String>();
|
||||
private Set<String> roleIds = new HashSet<String>();
|
||||
|
||||
public GrantedConsentModel(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void addGrantedRole(String roleId) {
|
||||
roleIds.add(roleId);
|
||||
}
|
||||
|
||||
public Set<String> getGrantedRoles() {
|
||||
return roleIds;
|
||||
}
|
||||
|
||||
public boolean isRoleGranted(String roleId) {
|
||||
return roleIds.contains(roleId);
|
||||
}
|
||||
|
||||
public void addGrantedProtocolMapper(String protocolMapperId) {
|
||||
protocolMapperIds.add(protocolMapperId);
|
||||
}
|
||||
|
||||
public Set<String> getGrantedProtocolMappers() {
|
||||
return protocolMapperIds;
|
||||
}
|
||||
|
||||
public boolean isProtocolMapperGranted(String protocolMapperId) {
|
||||
return protocolMapperIds.contains(protocolMapperId);
|
||||
}
|
||||
|
||||
}
|
|
@ -320,6 +320,16 @@ public class UserFederationManager implements UserProvider {
|
|||
session.userStorage().preRemove(realm, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, ClientModel client) {
|
||||
session.userStorage().preRemove(realm, client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(ClientModel client, ProtocolMapperModel protocolMapper) {
|
||||
session.userStorage().preRemove(client, protocolMapper);
|
||||
}
|
||||
|
||||
public void updateCredential(RealmModel realm, UserModel user, UserCredentialModel credential) {
|
||||
if (credential.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||
if (realm.getPasswordPolicy() != null) {
|
||||
|
|
|
@ -75,9 +75,11 @@ public interface UserModel {
|
|||
String getFederationLink();
|
||||
void setFederationLink(String link);
|
||||
|
||||
|
||||
|
||||
|
||||
GrantedConsentModel addGrantedConsent(GrantedConsentModel consent);
|
||||
GrantedConsentModel getGrantedConsentByClient(String clientId);
|
||||
List<GrantedConsentModel> getGrantedConsents();
|
||||
void updateGrantedConsent(GrantedConsentModel consent);
|
||||
boolean revokeGrantedConsentForClient(String clientId);
|
||||
|
||||
public static enum RequiredAction {
|
||||
VERIFY_EMAIL, UPDATE_PROFILE, CONFIGURE_TOTP, UPDATE_PASSWORD
|
||||
|
|
|
@ -41,6 +41,9 @@ public interface UserProvider extends Provider {
|
|||
|
||||
void preRemove(RealmModel realm, RoleModel role);
|
||||
|
||||
void preRemove(RealmModel realm, ClientModel client);
|
||||
void preRemove(ClientModel realm, ProtocolMapperModel protocolMapper);
|
||||
|
||||
boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input);
|
||||
boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input);
|
||||
CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.keycloak.models.utils;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.GrantedConsentModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserCredentialValueModel;
|
||||
|
@ -185,4 +186,29 @@ public class UserModelDelegate implements UserModel {
|
|||
public void setFederationLink(String link) {
|
||||
delegate.setFederationLink(link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrantedConsentModel addGrantedConsent(GrantedConsentModel consent) {
|
||||
return delegate.addGrantedConsent(consent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrantedConsentModel getGrantedConsentByClient(String clientId) {
|
||||
return delegate.getGrantedConsentByClient(clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GrantedConsentModel> getGrantedConsents() {
|
||||
return delegate.getGrantedConsents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGrantedConsent(GrantedConsentModel consent) {
|
||||
delegate.updateGrantedConsent(consent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean revokeGrantedConsentForClient(String clientId) {
|
||||
return delegate.revokeGrantedConsentForClient(clientId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ package org.keycloak.models.file;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.file.adapter.UserAdapter;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -384,6 +386,16 @@ public class FileUserProvider implements UserProvider {
|
|||
// todo not sure what to do for this
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, ClientModel client) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(ClientModel client, ProtocolMapperModel protocolMapper) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
|
||||
return CredentialValidation.validCredentials(realm, user, input);
|
||||
|
|
|
@ -16,10 +16,11 @@
|
|||
*/
|
||||
package org.keycloak.models.file.adapter;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
|
||||
import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
|
||||
|
||||
import org.keycloak.models.GrantedConsentModel;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
@ -430,6 +431,35 @@ public class UserAdapter implements UserModel, Comparable {
|
|||
user.setFederationLink(link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrantedConsentModel addGrantedConsent(GrantedConsentModel consent) {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrantedConsentModel getGrantedConsentByClient(String clientId) {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GrantedConsentModel> getGrantedConsents() {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGrantedConsent(GrantedConsentModel consent) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean revokeGrantedConsentForClient(String clientId) {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package org.keycloak.models.cache;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.CredentialValidationOutput;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
|
@ -310,4 +312,14 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
|
|||
realmInvalidations.add(realm.getId()); // easier to just invalidate whole realm
|
||||
getDelegate().preRemove(realm, link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, ClientModel client) {
|
||||
getDelegate().preRemove(realm, client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(ClientModel client, ProtocolMapperModel protocolMapper) {
|
||||
getDelegate().preRemove(client, protocolMapper);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package org.keycloak.models.cache;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.CredentialValidationOutput;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
|
@ -175,4 +177,14 @@ public class NoCacheUserProvider implements CacheUserProvider {
|
|||
public void preRemove(RealmModel realm, RoleModel role) {
|
||||
getDelegate().preRemove(realm, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, ClientModel client) {
|
||||
getDelegate().preRemove(realm, client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(ClientModel client, ProtocolMapperModel protocolMapper) {
|
||||
getDelegate().preRemove(client, protocolMapper);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.keycloak.models.cache;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.GrantedConsentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
|
@ -274,4 +275,36 @@ public class UserAdapter implements UserModel {
|
|||
getDelegateForUpdate();
|
||||
updated.deleteRoleMapping(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrantedConsentModel addGrantedConsent(GrantedConsentModel consent) {
|
||||
getDelegateForUpdate();
|
||||
return updated.addGrantedConsent(consent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrantedConsentModel getGrantedConsentByClient(String clientId) {
|
||||
// TODO: caching?
|
||||
getDelegateForUpdate();
|
||||
return updated.getGrantedConsentByClient(clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GrantedConsentModel> getGrantedConsents() {
|
||||
// TODO: caching?
|
||||
getDelegateForUpdate();
|
||||
return updated.getGrantedConsents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGrantedConsent(GrantedConsentModel consent) {
|
||||
getDelegateForUpdate();
|
||||
updated.updateGrantedConsent(consent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean revokeGrantedConsentForClient(String clientId) {
|
||||
getDelegateForUpdate();
|
||||
return updated.revokeGrantedConsentForClient(clientId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -418,6 +418,8 @@ public class ClientAdapter implements ClientModel {
|
|||
public void removeProtocolMapper(ProtocolMapperModel mapping) {
|
||||
ProtocolMapperEntity toDelete = getProtocolMapperEntity(mapping.getId());
|
||||
if (toDelete != null) {
|
||||
session.users().preRemove(this, mapping);
|
||||
|
||||
this.entity.getProtocolMappers().remove(toDelete);
|
||||
em.remove(toDelete);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.keycloak.models.ClientModel;
|
|||
import org.keycloak.models.CredentialValidationOutput;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
|
@ -87,6 +88,9 @@ public class JpaUserProvider implements UserProvider {
|
|||
private void removeUser(UserEntity user) {
|
||||
em.createNamedQuery("deleteUserRoleMappingsByUser").setParameter("user", user).executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedIdentityByUser").setParameter("user", user).executeUpdate();
|
||||
em.createNamedQuery("deleteGrantedConsentRolesByUser").setParameter("user", user).executeUpdate();
|
||||
em.createNamedQuery("deleteGrantedConsentProtMappersByUser").setParameter("user", user).executeUpdate();
|
||||
em.createNamedQuery("deleteGrantedConsentsByUser").setParameter("user", user).executeUpdate();
|
||||
em.remove(user);
|
||||
}
|
||||
|
||||
|
@ -130,7 +134,13 @@ public class JpaUserProvider implements UserProvider {
|
|||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm) {
|
||||
int num = em.createNamedQuery("deleteUserRoleMappingsByRealm")
|
||||
int num = em.createNamedQuery("deleteGrantedConsentRolesByRealm")
|
||||
.setParameter("realmId", realm.getId()).executeUpdate();
|
||||
num = em.createNamedQuery("deleteGrantedConsentProtMappersByRealm")
|
||||
.setParameter("realmId", realm.getId()).executeUpdate();
|
||||
num = em.createNamedQuery("deleteGrantedConsentsByRealm")
|
||||
.setParameter("realmId", realm.getId()).executeUpdate();
|
||||
num = em.createNamedQuery("deleteUserRoleMappingsByRealm")
|
||||
.setParameter("realmId", realm.getId()).executeUpdate();
|
||||
num = em.createNamedQuery("deleteUserRequiredActionsByRealm")
|
||||
.setParameter("realmId", realm.getId()).executeUpdate();
|
||||
|
@ -174,9 +184,24 @@ public class JpaUserProvider implements UserProvider {
|
|||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, RoleModel role) {
|
||||
em.createNamedQuery("deleteGrantedConsentRolesByRole").setParameter("roleId", role.getId()).executeUpdate();
|
||||
em.createNamedQuery("deleteUserRoleMappingsByRole").setParameter("roleId", role.getId()).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, ClientModel client) {
|
||||
em.createNamedQuery("deleteGrantedConsentProtMappersByClient").setParameter("clientId", client.getId()).executeUpdate();
|
||||
em.createNamedQuery("deleteGrantedConsentRolesByClient").setParameter("clientId", client.getId()).executeUpdate();
|
||||
em.createNamedQuery("deleteGrantedConsentsByClient").setParameter("clientId", client.getId()).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(ClientModel client, ProtocolMapperModel protocolMapper) {
|
||||
em.createNamedQuery("deleteGrantedConsentProtMappersByProtocolMapper")
|
||||
.setParameter("protocolMapperId", protocolMapper.getId())
|
||||
.executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserById(String id, RealmModel realm) {
|
||||
TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserById", UserEntity.class);
|
||||
|
|
|
@ -658,6 +658,8 @@ public class RealmAdapter implements RealmModel {
|
|||
ClientModel client = getClientById(id);
|
||||
if (client == null) return false;
|
||||
|
||||
session.users().preRemove(this, client);
|
||||
|
||||
for (RoleModel role : client.getRoles()) {
|
||||
client.removeRole(role);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package org.keycloak.models.jpa;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.GrantedConsentModel;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
|
@ -9,6 +12,9 @@ import org.keycloak.models.UserCredentialModel;
|
|||
import org.keycloak.models.UserCredentialValueModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.jpa.entities.CredentialEntity;
|
||||
import org.keycloak.models.jpa.entities.GrantedConsentEntity;
|
||||
import org.keycloak.models.jpa.entities.GrantedConsentProtocolMapperEntity;
|
||||
import org.keycloak.models.jpa.entities.GrantedConsentRoleEntity;
|
||||
import org.keycloak.models.jpa.entities.UserAttributeEntity;
|
||||
import org.keycloak.models.jpa.entities.UserEntity;
|
||||
import org.keycloak.models.jpa.entities.UserRequiredActionEntity;
|
||||
|
@ -24,6 +30,7 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
@ -472,6 +479,164 @@ public class UserAdapter implements UserModel {
|
|||
user.setFederationLink(link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrantedConsentModel addGrantedConsent(GrantedConsentModel consent) {
|
||||
String clientId = consent.getClientId();
|
||||
if (clientId == null) {
|
||||
throw new ModelException("clientId needs to be filled for newly added consent!");
|
||||
}
|
||||
|
||||
GrantedConsentEntity consentEntity = getGrantedConsentEntity(clientId);
|
||||
if (consentEntity != null) {
|
||||
throw new ModelDuplicateException("Consent already exists for client [" + clientId + "] and user [" + user.getId() + "]");
|
||||
}
|
||||
|
||||
consentEntity = new GrantedConsentEntity();
|
||||
consentEntity.setId(KeycloakModelUtils.generateId());
|
||||
consentEntity.setUser(user);
|
||||
consentEntity.setClientId(clientId);
|
||||
em.persist(consentEntity);
|
||||
em.flush();
|
||||
|
||||
updateGrantedConsentEntity(consentEntity, consent);
|
||||
|
||||
return consent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrantedConsentModel getGrantedConsentByClient(String clientId) {
|
||||
GrantedConsentEntity entity = getGrantedConsentEntity(clientId);
|
||||
return toConsentModel(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GrantedConsentModel> getGrantedConsents() {
|
||||
TypedQuery<GrantedConsentEntity> query = em.createNamedQuery("grantedConsentsByUser", GrantedConsentEntity.class);
|
||||
query.setParameter("userId", getId());
|
||||
List<GrantedConsentEntity> results = query.getResultList();
|
||||
|
||||
List<GrantedConsentModel> consents = new ArrayList<GrantedConsentModel>();
|
||||
for (GrantedConsentEntity entity : results) {
|
||||
GrantedConsentModel model = toConsentModel(entity);
|
||||
consents.add(model);
|
||||
}
|
||||
return consents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGrantedConsent(GrantedConsentModel consent) {
|
||||
String clientId = consent.getClientId();
|
||||
if (clientId == null) {
|
||||
throw new ModelException("clientId needs to be for newly added consent!");
|
||||
}
|
||||
|
||||
GrantedConsentEntity consentEntity = getGrantedConsentEntity(clientId);
|
||||
if (consentEntity == null) {
|
||||
throw new ModelException("Consent not found for client [" + clientId + "] and user [" + user.getId() + "]");
|
||||
}
|
||||
|
||||
updateGrantedConsentEntity(consentEntity, consent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean revokeGrantedConsentForClient(String clientId) {
|
||||
GrantedConsentEntity consentEntity = getGrantedConsentEntity(clientId);
|
||||
if (consentEntity == null) return false;
|
||||
|
||||
em.remove(consentEntity);
|
||||
em.flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private GrantedConsentEntity getGrantedConsentEntity(String clientId) {
|
||||
TypedQuery<GrantedConsentEntity> query = em.createNamedQuery("grantedConsentByUserAndClient", GrantedConsentEntity.class);
|
||||
query.setParameter("userId", getId());
|
||||
query.setParameter("clientId", clientId);
|
||||
List<GrantedConsentEntity> results = query.getResultList();
|
||||
if (results.size() > 1) {
|
||||
throw new ModelException("More results found for user [" + getUsername() + "] and client [" + clientId + "]");
|
||||
} else if (results.size() == 1) {
|
||||
return results.get(0);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private GrantedConsentModel toConsentModel(GrantedConsentEntity entity) {
|
||||
if (entity == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
GrantedConsentModel model = new GrantedConsentModel(entity.getClientId());
|
||||
|
||||
Collection<GrantedConsentRoleEntity> grantedRoleEntities = entity.getGrantedRoles();
|
||||
if (grantedRoleEntities != null) {
|
||||
for (GrantedConsentRoleEntity grantedRole : grantedRoleEntities) {
|
||||
model.addGrantedRole(grantedRole.getRoleId());
|
||||
}
|
||||
}
|
||||
|
||||
Collection<GrantedConsentProtocolMapperEntity> grantedProtocolMapperEntities = entity.getGrantedProtocolMappers();
|
||||
if (grantedProtocolMapperEntities != null) {
|
||||
for (GrantedConsentProtocolMapperEntity grantedProtMapper : grantedProtocolMapperEntities) {
|
||||
model.addGrantedProtocolMapper(grantedProtMapper.getProtocolMapperId());
|
||||
}
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
// Update roles and protocolMappers to given consentEntity from the consentModel
|
||||
private void updateGrantedConsentEntity(GrantedConsentEntity consentEntity, GrantedConsentModel consentModel) {
|
||||
Collection<GrantedConsentProtocolMapperEntity> grantedProtocolMapperEntities = consentEntity.getGrantedProtocolMappers();
|
||||
Collection<GrantedConsentProtocolMapperEntity> mappersToRemove = new HashSet<GrantedConsentProtocolMapperEntity>(grantedProtocolMapperEntities);
|
||||
|
||||
for (String protocolMapperId : consentModel.getGrantedProtocolMappers()) {
|
||||
GrantedConsentProtocolMapperEntity grantedProtocolMapperEntity = new GrantedConsentProtocolMapperEntity();
|
||||
grantedProtocolMapperEntity.setGrantedConsent(consentEntity);
|
||||
grantedProtocolMapperEntity.setProtocolMapperId(protocolMapperId);
|
||||
|
||||
// Check if it's already there
|
||||
if (!grantedProtocolMapperEntities.contains(grantedProtocolMapperEntity)) {
|
||||
em.persist(grantedProtocolMapperEntity);
|
||||
em.flush();
|
||||
grantedProtocolMapperEntities.add(grantedProtocolMapperEntity);
|
||||
} else {
|
||||
mappersToRemove.remove(grantedProtocolMapperEntity);
|
||||
}
|
||||
}
|
||||
// Those mappers were no longer on consentModel and will be removed
|
||||
for (GrantedConsentProtocolMapperEntity toRemove : mappersToRemove) {
|
||||
grantedProtocolMapperEntities.remove(toRemove);
|
||||
em.remove(toRemove);
|
||||
}
|
||||
|
||||
Collection<GrantedConsentRoleEntity> grantedRoleEntities = consentEntity.getGrantedRoles();
|
||||
Set<GrantedConsentRoleEntity> rolesToRemove = new HashSet<GrantedConsentRoleEntity>(grantedRoleEntities);
|
||||
for (String roleId : consentModel.getGrantedRoles()) {
|
||||
GrantedConsentRoleEntity consentRoleEntity = new GrantedConsentRoleEntity();
|
||||
consentRoleEntity.setGrantedConsent(consentEntity);
|
||||
consentRoleEntity.setRoleId(roleId);
|
||||
|
||||
// Check if it's already there
|
||||
if (!grantedRoleEntities.contains(consentRoleEntity)) {
|
||||
em.persist(consentRoleEntity);
|
||||
em.flush();
|
||||
grantedRoleEntities.add(consentRoleEntity);
|
||||
} else {
|
||||
rolesToRemove.remove(consentRoleEntity);
|
||||
}
|
||||
}
|
||||
// Those roles were no longer on consentModel and will be removed
|
||||
for (GrantedConsentRoleEntity toRemove : rolesToRemove) {
|
||||
grantedRoleEntities.remove(toRemove);
|
||||
em.remove(toRemove);
|
||||
}
|
||||
|
||||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
package org.keycloak.models.jpa.entities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.CollectionTable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@Entity
|
||||
@Table(name="GRANTED_CONSENT", uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = {"USER_ID", "CLIENT_ID"})
|
||||
})
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="grantedConsentByUserAndClient", query="select consent from GrantedConsentEntity consent where consent.user.id = :userId and consent.clientId = :clientId"),
|
||||
@NamedQuery(name="grantedConsentsByUser", query="select consent from GrantedConsentEntity consent where consent.user.id = :userId"),
|
||||
@NamedQuery(name="deleteGrantedConsentsByRealm", query="delete from GrantedConsentEntity consent where consent.user IN (select user from UserEntity user where user.realmId = :realmId)"),
|
||||
@NamedQuery(name="deleteGrantedConsentsByUser", query="delete from GrantedConsentEntity consent where consent.user = :user"),
|
||||
@NamedQuery(name="deleteGrantedConsentsByClient", query="delete from GrantedConsentEntity consent where consent.clientId = :clientId"),
|
||||
})
|
||||
public class GrantedConsentEntity {
|
||||
|
||||
@Id
|
||||
@Column(name="ID", length = 36)
|
||||
protected String id;
|
||||
|
||||
@ManyToOne(fetch= FetchType.LAZY)
|
||||
@JoinColumn(name="USER_ID")
|
||||
protected UserEntity user;
|
||||
|
||||
@Column(name="CLIENT_ID")
|
||||
protected String clientId;
|
||||
|
||||
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "grantedConsent")
|
||||
Collection<GrantedConsentRoleEntity> grantedRoles = new ArrayList<GrantedConsentRoleEntity>();
|
||||
|
||||
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "grantedConsent")
|
||||
Collection<GrantedConsentProtocolMapperEntity> grantedProtocolMappers = new ArrayList<GrantedConsentProtocolMapperEntity>();
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public UserEntity getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(UserEntity user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public Collection<GrantedConsentRoleEntity> getGrantedRoles() {
|
||||
return grantedRoles;
|
||||
}
|
||||
|
||||
public void setGrantedRoles(Collection<GrantedConsentRoleEntity> grantedRoles) {
|
||||
this.grantedRoles = grantedRoles;
|
||||
}
|
||||
|
||||
public Collection<GrantedConsentProtocolMapperEntity> getGrantedProtocolMappers() {
|
||||
return grantedProtocolMappers;
|
||||
}
|
||||
|
||||
public void setGrantedProtocolMappers(Collection<GrantedConsentProtocolMapperEntity> grantedProtocolMappers) {
|
||||
this.grantedProtocolMappers = grantedProtocolMappers;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
package org.keycloak.models.jpa.entities;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="deleteGrantedConsentProtMappersByRealm", query=
|
||||
"delete from GrantedConsentProtocolMapperEntity csm where csm.grantedConsent IN (select consent from GrantedConsentEntity consent where consent.user IN (select user from UserEntity user where user.realmId = :realmId))"),
|
||||
@NamedQuery(name="deleteGrantedConsentProtMappersByUser", query="delete from GrantedConsentProtocolMapperEntity csm where csm.grantedConsent IN (select consent from GrantedConsentEntity consent where consent.user = :user)"),
|
||||
@NamedQuery(name="deleteGrantedConsentProtMappersByProtocolMapper", query="delete from GrantedConsentProtocolMapperEntity csm where csm.protocolMapperId = :protocolMapperId)"),
|
||||
@NamedQuery(name="deleteGrantedConsentProtMappersByClient", query="delete from GrantedConsentProtocolMapperEntity csm where csm.grantedConsent IN (select consent from GrantedConsentEntity consent where consent.clientId = :clientId))"),
|
||||
})
|
||||
@Entity
|
||||
@Table(name="GRANTED_CONSENT_PROT_MAPPER")
|
||||
@IdClass(GrantedConsentProtocolMapperEntity.Key.class)
|
||||
public class GrantedConsentProtocolMapperEntity {
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch= FetchType.LAZY)
|
||||
@JoinColumn(name = "GRANTED_CONSENT_ID")
|
||||
protected GrantedConsentEntity grantedConsent;
|
||||
|
||||
@Id
|
||||
@Column(name="PROTOCOL_MAPPER_ID")
|
||||
protected String protocolMapperId;
|
||||
|
||||
public GrantedConsentEntity getGrantedConsent() {
|
||||
return grantedConsent;
|
||||
}
|
||||
|
||||
public void setGrantedConsent(GrantedConsentEntity grantedConsent) {
|
||||
this.grantedConsent = grantedConsent;
|
||||
}
|
||||
|
||||
public String getProtocolMapperId() {
|
||||
return protocolMapperId;
|
||||
}
|
||||
|
||||
public void setProtocolMapperId(String protocolMapperId) {
|
||||
this.protocolMapperId = protocolMapperId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
GrantedConsentProtocolMapperEntity that = (GrantedConsentProtocolMapperEntity)o;
|
||||
Key myKey = new Key(this.grantedConsent, this.protocolMapperId);
|
||||
Key hisKey = new Key(that.grantedConsent, that.protocolMapperId);
|
||||
return myKey.equals(hisKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
Key myKey = new Key(this.grantedConsent, this.protocolMapperId);
|
||||
return myKey.hashCode();
|
||||
}
|
||||
|
||||
public static class Key implements Serializable {
|
||||
|
||||
protected GrantedConsentEntity grantedConsent;
|
||||
|
||||
protected String protocolMapperId;
|
||||
|
||||
public Key() {
|
||||
}
|
||||
|
||||
public Key(GrantedConsentEntity grantedConsent, String protocolMapperId) {
|
||||
this.grantedConsent = grantedConsent;
|
||||
this.protocolMapperId = protocolMapperId;
|
||||
}
|
||||
|
||||
public GrantedConsentEntity getGrantedConsent() {
|
||||
return grantedConsent;
|
||||
}
|
||||
|
||||
public String getProtocolMapperId() {
|
||||
return protocolMapperId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Key key = (Key) o;
|
||||
|
||||
if (grantedConsent != null ? !grantedConsent.getId().equals(key.grantedConsent != null ? key.grantedConsent.getId() : null) : key.grantedConsent != null) return false;
|
||||
if (protocolMapperId != null ? !protocolMapperId.equals(key.protocolMapperId) : key.protocolMapperId != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = grantedConsent != null ? grantedConsent.getId().hashCode() : 0;
|
||||
result = 31 * result + (protocolMapperId != null ? protocolMapperId.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
package org.keycloak.models.jpa.entities;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="deleteGrantedConsentRolesByRealm", query="delete from GrantedConsentRoleEntity grantedRole where grantedRole.grantedConsent IN (select consent from GrantedConsentEntity consent where consent.user IN (select user from UserEntity user where user.realmId = :realmId))"),
|
||||
@NamedQuery(name="deleteGrantedConsentRolesByUser", query="delete from GrantedConsentRoleEntity grantedRole where grantedRole.grantedConsent IN (select consent from GrantedConsentEntity consent where consent.user = :user)"),
|
||||
@NamedQuery(name="deleteGrantedConsentRolesByRole", query="delete from GrantedConsentRoleEntity grantedRole where grantedRole.roleId = :roleId)"),
|
||||
@NamedQuery(name="deleteGrantedConsentRolesByClient", query="delete from GrantedConsentRoleEntity grantedRole where grantedRole.grantedConsent IN (select consent from GrantedConsentEntity consent where consent.clientId = :clientId)"),
|
||||
})
|
||||
@Entity
|
||||
@Table(name="GRANTED_CONSENT_ROLE")
|
||||
@IdClass(GrantedConsentRoleEntity.Key.class)
|
||||
public class GrantedConsentRoleEntity {
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch= FetchType.LAZY)
|
||||
@JoinColumn(name = "GRANTED_CONSENT_ID")
|
||||
protected GrantedConsentEntity grantedConsent;
|
||||
|
||||
@Id
|
||||
@Column(name="ROLE_ID")
|
||||
protected String roleId;
|
||||
|
||||
public GrantedConsentEntity getGrantedConsent() {
|
||||
return grantedConsent;
|
||||
}
|
||||
|
||||
public void setGrantedConsent(GrantedConsentEntity grantedConsent) {
|
||||
this.grantedConsent = grantedConsent;
|
||||
}
|
||||
|
||||
public String getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(String roleId) {
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
GrantedConsentRoleEntity that = (GrantedConsentRoleEntity)o;
|
||||
Key myKey = new Key(this.grantedConsent, this.roleId);
|
||||
Key hisKey = new Key(that.grantedConsent, that.roleId);
|
||||
return myKey.equals(hisKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
Key myKey = new Key(this.grantedConsent, this.roleId);
|
||||
return myKey.hashCode();
|
||||
}
|
||||
|
||||
public static class Key implements Serializable {
|
||||
|
||||
protected GrantedConsentEntity grantedConsent;
|
||||
|
||||
protected String roleId;
|
||||
|
||||
public Key() {
|
||||
}
|
||||
|
||||
public Key(GrantedConsentEntity grantedConsent, String roleId) {
|
||||
this.grantedConsent = grantedConsent;
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
public GrantedConsentEntity getGrantedConsent() {
|
||||
return grantedConsent;
|
||||
}
|
||||
|
||||
public String getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Key key = (Key) o;
|
||||
|
||||
if (grantedConsent != null ? !grantedConsent.getId().equals(key.grantedConsent != null ? key.grantedConsent.getId() : null) : key.grantedConsent != null) return false;
|
||||
if (roleId != null ? !roleId.equals(key.roleId) : key.roleId != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = grantedConsent != null ? grantedConsent.getId().hashCode() : 0;
|
||||
result = 31 * result + (roleId != null ? roleId.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -321,6 +321,8 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
|
|||
public void removeProtocolMapper(ProtocolMapperModel mapping) {
|
||||
for (ProtocolMapperEntity entity : getMongoEntity().getProtocolMappers()) {
|
||||
if (entity.getId().equals(mapping.getId())) {
|
||||
session.users().preRemove(this, mapping);
|
||||
|
||||
getMongoEntity().getProtocolMappers().remove(entity);
|
||||
updateMongoEntity();
|
||||
break;
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.keycloak.models.ClientModel;
|
|||
import org.keycloak.models.CredentialValidationOutput;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
|
@ -351,6 +352,16 @@ public class MongoUserProvider implements UserProvider {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, ClientModel client) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(ClientModel client, ProtocolMapperModel protocolMapper) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, RoleModel role) {
|
||||
// todo not sure what to do for this
|
||||
|
|
|
@ -639,6 +639,12 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
|
||||
@Override
|
||||
public boolean removeClient(String id) {
|
||||
if (id == null) return false;
|
||||
ClientModel client = getClientById(id);
|
||||
if (client == null) return false;
|
||||
|
||||
session.users().preRemove(this, client);
|
||||
|
||||
return getMongoStore().removeEntity(MongoClientEntity.class, id, invocationContext);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
|
|||
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.GrantedConsentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -420,6 +421,35 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
|
|||
updateUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrantedConsentModel addGrantedConsent(GrantedConsentModel consent) {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrantedConsentModel getGrantedConsentByClient(String clientId) {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GrantedConsentModel> getGrantedConsents() {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGrantedConsent(GrantedConsentModel consent) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean revokeGrantedConsentForClient(String clientId) {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
|
@ -118,6 +118,11 @@ public class Urls {
|
|||
.build(realmId);
|
||||
}
|
||||
|
||||
public static URI accountRevokeClientPage(URI baseUri, String realmId) {
|
||||
return accountBase(baseUri).path(AccountService.class, "processRevokeGrant")
|
||||
.build(realmId);
|
||||
}
|
||||
|
||||
public static URI accountLogout(URI baseUri, URI redirectUri, String realmId) {
|
||||
return realmLogout(baseUri).queryParam("redirect_uri", redirectUri).build(realmId);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,9 @@ import org.keycloak.jose.jws.JWSBuilder;
|
|||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.GrantedConsentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
@ -418,9 +420,17 @@ public class AuthenticationManager {
|
|||
if (client.isConsentRequired()) {
|
||||
accessCode.setAction(ClientSessionModel.Action.OAUTH_GRANT);
|
||||
|
||||
GrantedConsentModel grantedConsent = user.getGrantedConsentByClient(client.getId());
|
||||
|
||||
List<RoleModel> realmRoles = new LinkedList<RoleModel>();
|
||||
MultivaluedMap<String, RoleModel> resourceRoles = new MultivaluedMapImpl<String, RoleModel>();
|
||||
for (RoleModel r : accessCode.getRequestedRoles()) {
|
||||
|
||||
// Consent already granted by user
|
||||
if (grantedConsent != null && grantedConsent.getGrantedRoles().contains(r.getId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (r.getContainer() instanceof RealmModel) {
|
||||
realmRoles.add(r);
|
||||
} else {
|
||||
|
@ -428,10 +438,22 @@ public class AuthenticationManager {
|
|||
}
|
||||
}
|
||||
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setClientSessionCode(accessCode.getCode())
|
||||
.setAccessRequest(realmRoles, resourceRoles)
|
||||
.createOAuthGrant(clientSession);
|
||||
List<ProtocolMapperModel> protocolMappers = new LinkedList<ProtocolMapperModel>();
|
||||
for (ProtocolMapperModel model : client.getProtocolMappers()) {
|
||||
if (model.isConsentRequired() && model.getProtocol().equals(clientSession.getAuthMethod()) && model.getConsentText() != null) {
|
||||
if (grantedConsent == null || !grantedConsent.getGrantedProtocolMappers().contains(model.getId())) {
|
||||
protocolMappers.add(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Skip grant screen if everything was already approved by this user
|
||||
if (realmRoles.size() > 0 || resourceRoles.size() > 0 || protocolMappers.size() > 0) {
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setClientSessionCode(accessCode.getCode())
|
||||
.setAccessRequest(realmRoles, resourceRoles, protocolMappers)
|
||||
.createOAuthGrant(clientSession);
|
||||
}
|
||||
}
|
||||
|
||||
event.success();
|
||||
|
|
|
@ -152,6 +152,8 @@ public class Messages {
|
|||
|
||||
public static final String SUCCESS_TOTP = "successTotpMessage";
|
||||
|
||||
public static final String SUCCESS_GRANT_REVOKED = "successGrantRevokedMessage";
|
||||
|
||||
public static final String MISSING_IDENTITY_PROVIDER = "missingIdentityProviderMessage";
|
||||
|
||||
public static final String INVALID_FEDERATED_IDENTITY_ACTION = "invalidFederatedIdentityActionMessage";
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.keycloak.models.utils.ModelToRepresentation;
|
|||
import org.keycloak.models.utils.TimeBasedOTP;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.protocol.oidc.utils.RedirectUtils;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
@ -75,6 +76,7 @@ import javax.ws.rs.core.Variant;
|
|||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -348,6 +350,12 @@ public class AccountService {
|
|||
return forwardToPage("sessions", AccountPages.SESSIONS);
|
||||
}
|
||||
|
||||
@Path("access")
|
||||
@GET
|
||||
public Response accessPage() {
|
||||
return forwardToPage("access", AccountPages.ACCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if form post has sessionId hidden field and match it against the session id.
|
||||
*
|
||||
|
@ -483,6 +491,46 @@ public class AccountService {
|
|||
return Response.seeOther(location).build();
|
||||
}
|
||||
|
||||
@Path("revoke-grant")
|
||||
@POST
|
||||
public Response processRevokeGrant(final MultivaluedMap<String, String> formData) {
|
||||
if (auth == null) {
|
||||
return login("access");
|
||||
}
|
||||
|
||||
require(AccountRoles.MANAGE_ACCOUNT);
|
||||
csrfCheck(formData);
|
||||
|
||||
String clientId = formData.getFirst("clientId");
|
||||
if (clientId == null) {
|
||||
return account.setError(Messages.CLIENT_NOT_FOUND).createResponse(AccountPages.ACCESS);
|
||||
}
|
||||
ClientModel client = realm.getClientById(clientId);
|
||||
if (client == null) {
|
||||
return account.setError(Messages.CLIENT_NOT_FOUND).createResponse(AccountPages.ACCESS);
|
||||
}
|
||||
|
||||
// Revoke grant in UserModel
|
||||
UserModel user = auth.getUser();
|
||||
user.revokeGrantedConsentForClient(client.getId());
|
||||
|
||||
// Logout clientSessions for this user and client
|
||||
List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
|
||||
for (UserSessionModel userSession : userSessions) {
|
||||
List<ClientSessionModel> clientSessions = userSession.getClientSessions();
|
||||
for (ClientSessionModel clientSession : clientSessions) {
|
||||
if (clientSession.getClient().getId().equals(clientId)) {
|
||||
TokenManager.dettachClientSession(session.sessions(), realm, clientSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event.event(EventType.REVOKE_GRANT).client(auth.getClient()).user(auth.getUser()).detail(Details.REVOKED_CLIENT, client.getClientId()).success();
|
||||
setReferrerOnPage();
|
||||
|
||||
return account.setSuccess(Messages.SUCCESS_GRANT_REVOKED).createResponse(AccountPages.ACCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the TOTP for this account.
|
||||
*
|
||||
|
|
|
@ -34,8 +34,10 @@ import org.keycloak.jose.jws.JWSBuilder;
|
|||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.GrantedConsentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
|
@ -576,19 +578,19 @@ public class LoginActionsService {
|
|||
event.detail(Details.CODE_ID, clientSession.getId());
|
||||
|
||||
String redirect = clientSession.getRedirectUri();
|
||||
UserSessionModel userSession = clientSession.getUserSession();
|
||||
UserModel user = userSession.getUser();
|
||||
ClientModel client = clientSession.getClient();
|
||||
|
||||
event.client(clientSession.getClient())
|
||||
.user(clientSession.getUserSession().getUser())
|
||||
event.client(client)
|
||||
.user(user)
|
||||
.detail(Details.RESPONSE_TYPE, "code")
|
||||
.detail(Details.REDIRECT_URI, redirect);
|
||||
|
||||
UserSessionModel userSession = clientSession.getUserSession();
|
||||
if (userSession != null) {
|
||||
event.detail(Details.AUTH_METHOD, userSession.getAuthMethod());
|
||||
event.detail(Details.USERNAME, userSession.getLoginUsername());
|
||||
if (userSession.isRememberMe()) {
|
||||
event.detail(Details.REMEMBER_ME, "true");
|
||||
}
|
||||
event.detail(Details.AUTH_METHOD, userSession.getAuthMethod());
|
||||
event.detail(Details.USERNAME, userSession.getLoginUsername());
|
||||
if (userSession.isRememberMe()) {
|
||||
event.detail(Details.REMEMBER_ME, "true");
|
||||
}
|
||||
|
||||
if (!AuthenticationManager.isSessionValid(realm, userSession)) {
|
||||
|
@ -607,6 +609,21 @@ public class LoginActionsService {
|
|||
return protocol.consentDenied(clientSession);
|
||||
}
|
||||
|
||||
GrantedConsentModel grantedConsent = user.getGrantedConsentByClient(client.getId());
|
||||
if (grantedConsent == null) {
|
||||
grantedConsent = user.addGrantedConsent(new GrantedConsentModel(client.getId()));
|
||||
}
|
||||
for (String roleId : clientSession.getRoles()) {
|
||||
grantedConsent.addGrantedRole(roleId);
|
||||
}
|
||||
// TODO: It's not 100% sure that approved protocolMappers are same like the protocolMappers retrieved here from the client. Maybe clientSession.setProtocolMappers/getProtocolMappers should be added...
|
||||
for (ProtocolMapperModel protocolMapper : client.getProtocolMappers()) {
|
||||
if (protocolMapper.isConsentRequired() && protocolMapper.getProtocol().equals(clientSession.getAuthMethod()) && protocolMapper.getConsentText() != null) {
|
||||
grantedConsent.addGrantedProtocolMapper(protocolMapper.getId());
|
||||
}
|
||||
}
|
||||
user.updateGrantedConsent(grantedConsent);
|
||||
|
||||
event.success();
|
||||
|
||||
return authManager.redirectAfterSuccessfulFlow(session, realm, userSession, clientSession, request, uriInfo, clientConnection);
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.keycloak.services.managers.RealmManager;
|
|||
import org.keycloak.services.resources.RealmsResource;
|
||||
import org.keycloak.testsuite.Constants;
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.pages.AccountAccessPage;
|
||||
import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.pages.OAuthGrantPage;
|
||||
|
@ -87,6 +88,9 @@ public class ProfileTest {
|
|||
@WebResource
|
||||
protected AccountUpdateProfilePage profilePage;
|
||||
|
||||
@WebResource
|
||||
protected AccountAccessPage accountAccessPage;
|
||||
|
||||
@WebResource
|
||||
protected LoginPage loginPage;
|
||||
|
||||
|
@ -186,6 +190,9 @@ public class ProfileTest {
|
|||
JSONObject profile = new JSONObject(IOUtils.toString(response.getEntity().getContent()));
|
||||
|
||||
assertEquals("test-user@localhost", profile.getString("username"));
|
||||
|
||||
accountAccessPage.open();
|
||||
accountAccessPage.revokeGrant("third-party");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -89,7 +89,7 @@ public abstract class AbstractIdentityProviderTest {
|
|||
public WebRule webRule = new WebRule(this);
|
||||
|
||||
@WebResource
|
||||
private WebDriver driver;
|
||||
protected WebDriver driver;
|
||||
|
||||
@WebResource
|
||||
private LoginPage loginPage;
|
||||
|
@ -122,6 +122,7 @@ public abstract class AbstractIdentityProviderTest {
|
|||
|
||||
@After
|
||||
public void onAfter() {
|
||||
revokeGrant();
|
||||
brokerServerRule.stopSession(this.session, true);
|
||||
}
|
||||
|
||||
|
@ -387,6 +388,9 @@ public abstract class AbstractIdentityProviderTest {
|
|||
assertTrue(accountFederatedIdentityPage.isCurrent());
|
||||
assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getAlias() + "\""));
|
||||
|
||||
// Revoke grant in account mgmt
|
||||
revokeGrant();
|
||||
|
||||
// Logout from account management
|
||||
accountFederatedIdentityPage.logout();
|
||||
assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
|
||||
|
@ -401,6 +405,9 @@ public abstract class AbstractIdentityProviderTest {
|
|||
accountFederatedIdentityPage.clickRemoveProvider(identityProviderModel.getAlias());
|
||||
assertTrue(driver.getPageSource().contains("id=\"add-" + identityProviderModel.getAlias() + "\""));
|
||||
|
||||
// Revoke grant in account mgmt
|
||||
revokeGrant();
|
||||
|
||||
// Logout from account management
|
||||
accountFederatedIdentityPage.logout();
|
||||
assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
|
||||
|
@ -637,6 +644,10 @@ public abstract class AbstractIdentityProviderTest {
|
|||
|
||||
}
|
||||
|
||||
protected void revokeGrant() {
|
||||
|
||||
}
|
||||
|
||||
protected abstract String getProviderId();
|
||||
|
||||
protected IdentityProviderModel getIdentityProviderModel() {
|
||||
|
|
|
@ -1,19 +1,26 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.Constants;
|
||||
import org.keycloak.testsuite.pages.AccountAccessPage;
|
||||
import org.keycloak.testsuite.pages.OAuthGrantPage;
|
||||
import org.keycloak.testsuite.rule.AbstractKeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testutils.KeycloakServer;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.openqa.selenium.NoSuchElementException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
|
@ -22,12 +29,14 @@ import static org.junit.Assert.fail;
|
|||
*/
|
||||
public class OIDCKeyCloakServerBrokerBasicTest extends AbstractIdentityProviderTest {
|
||||
|
||||
private static final int PORT = 8082;
|
||||
|
||||
@ClassRule
|
||||
public static AbstractKeycloakRule samlServerRule = new AbstractKeycloakRule() {
|
||||
|
||||
@Override
|
||||
protected void configureServer(KeycloakServer server) {
|
||||
server.getConfig().setPort(8082);
|
||||
server.getConfig().setPort(PORT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -44,6 +53,25 @@ public class OIDCKeyCloakServerBrokerBasicTest extends AbstractIdentityProviderT
|
|||
@WebResource
|
||||
private OAuthGrantPage grantPage;
|
||||
|
||||
@WebResource
|
||||
protected AccountAccessPage accountAccessPage;
|
||||
|
||||
@Override
|
||||
protected void revokeGrant() {
|
||||
String currentUrl = driver.getCurrentUrl();
|
||||
|
||||
String accountAccessPath = Urls.accountAccessPage(UriBuilder.fromUri(Constants.AUTH_SERVER_ROOT).port(PORT).build(), "realm-with-oidc-identity-provider").toString();
|
||||
accountAccessPage.setPath(accountAccessPath);
|
||||
accountAccessPage.open();
|
||||
try {
|
||||
accountAccessPage.revokeGrant("broker-app");
|
||||
} catch (NoSuchElementException e) {
|
||||
System.err.println("Couldn't revoke broker-app application, maybe because it wasn't granted or user not logged");
|
||||
}
|
||||
|
||||
driver.navigate().to(currentUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAfterProviderAuthentication() {
|
||||
// grant access to broker-app
|
||||
|
|
|
@ -0,0 +1,266 @@
|
|||
package org.keycloak.testsuite.model;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.GrantedConsentModel;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.mappers.UserPropertyMapper;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class GrantedConsentModelTest extends AbstractModelTest {
|
||||
|
||||
@Before
|
||||
public void setupEnv() {
|
||||
RealmModel realm = realmManager.createRealm("original");
|
||||
|
||||
ClientModel fooClient = realm.addClient("foo-client");
|
||||
ClientModel barClient = realm.addClient("bar-client");
|
||||
|
||||
RoleModel realmRole = realm.addRole("realm-role");
|
||||
RoleModel barClientRole = barClient.addRole("bar-client-role");
|
||||
|
||||
ProtocolMapperModel fooMapper = new ProtocolMapperModel();
|
||||
fooMapper.setName("foo");
|
||||
fooMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
fooMapper.setProtocolMapper(UserPropertyMapper.PROVIDER_ID);
|
||||
fooMapper = fooClient.addProtocolMapper(fooMapper);
|
||||
|
||||
ProtocolMapperModel barMapper = new ProtocolMapperModel();
|
||||
barMapper.setName("bar");
|
||||
barMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
barMapper.setProtocolMapper(UserPropertyMapper.PROVIDER_ID);
|
||||
barMapper = barClient.addProtocolMapper(barMapper);
|
||||
|
||||
UserModel john = session.users().addUser(realm, "john");
|
||||
UserModel mary = session.users().addUser(realm, "mary");
|
||||
|
||||
GrantedConsentModel johnFooGrant = new GrantedConsentModel(fooClient.getId());
|
||||
johnFooGrant.addGrantedRole(realmRole.getId());
|
||||
johnFooGrant.addGrantedRole(barClientRole.getId());
|
||||
johnFooGrant.addGrantedProtocolMapper(fooMapper.getId());
|
||||
john.addGrantedConsent(johnFooGrant);
|
||||
|
||||
GrantedConsentModel johnBarGrant = new GrantedConsentModel(barClient.getId());
|
||||
johnBarGrant.addGrantedProtocolMapper(barMapper.getId());
|
||||
johnBarGrant.addGrantedRole(realmRole.getId());
|
||||
|
||||
// Update should fail as grant doesn't yet exists
|
||||
try {
|
||||
john.updateGrantedConsent(johnBarGrant);
|
||||
Assert.fail("Not expected to end here");
|
||||
} catch (ModelException expected) {
|
||||
}
|
||||
|
||||
john.addGrantedConsent(johnBarGrant);
|
||||
|
||||
GrantedConsentModel maryFooGrant = new GrantedConsentModel(fooClient.getId());
|
||||
maryFooGrant.addGrantedRole(realmRole.getId());
|
||||
maryFooGrant.addGrantedProtocolMapper(fooMapper.getId());
|
||||
mary.addGrantedConsent(maryFooGrant);
|
||||
|
||||
commit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicConsentTest() {
|
||||
RealmModel realm = realmManager.getRealm("original");
|
||||
Map<String, ClientModel> clients = realm.getClientNameMap();
|
||||
ClientModel fooClient = clients.get("foo-client");
|
||||
ClientModel barClient = clients.get("bar-client");
|
||||
|
||||
UserModel john = session.users().getUserByUsername("john", realm);
|
||||
UserModel mary = session.users().getUserByUsername("mary", realm);
|
||||
|
||||
GrantedConsentModel johnFooConsent = john.getGrantedConsentByClient(fooClient.getId());
|
||||
Assert.assertEquals(johnFooConsent.getGrantedRoles().size(), 2);
|
||||
Assert.assertEquals(johnFooConsent.getGrantedProtocolMappers().size(), 1);
|
||||
Assert.assertTrue(isRoleGranted(realm, "realm-role", johnFooConsent));
|
||||
Assert.assertTrue(isRoleGranted(barClient, "bar-client-role", johnFooConsent));
|
||||
Assert.assertTrue(isMapperGranted(fooClient, "foo", johnFooConsent));
|
||||
|
||||
GrantedConsentModel johnBarConsent = john.getGrantedConsentByClient(barClient.getId());
|
||||
Assert.assertEquals(johnBarConsent.getGrantedRoles().size(), 1);
|
||||
Assert.assertEquals(johnBarConsent.getGrantedProtocolMappers().size(), 1);
|
||||
Assert.assertTrue(isRoleGranted(realm, "realm-role", johnBarConsent));
|
||||
Assert.assertTrue(isMapperGranted(barClient, "bar", johnBarConsent));
|
||||
|
||||
GrantedConsentModel maryConsent = mary.getGrantedConsentByClient(fooClient.getId());
|
||||
Assert.assertEquals(maryConsent.getGrantedRoles().size(), 1);
|
||||
Assert.assertEquals(maryConsent.getGrantedProtocolMappers().size(), 1);
|
||||
Assert.assertTrue(isRoleGranted(realm, "realm-role", maryConsent));
|
||||
Assert.assertFalse(isRoleGranted(barClient, "bar-client-role", maryConsent));
|
||||
Assert.assertTrue(isMapperGranted(fooClient, "foo", maryConsent));
|
||||
|
||||
Assert.assertNull(mary.getGrantedConsentByClient(barClient.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAllConsentTest() {
|
||||
RealmModel realm = realmManager.getRealm("original");
|
||||
Map<String, ClientModel> clients = realm.getClientNameMap();
|
||||
ClientModel fooClient = clients.get("foo-client");
|
||||
|
||||
UserModel john = session.users().getUserByUsername("john", realm);
|
||||
UserModel mary = session.users().getUserByUsername("mary", realm);
|
||||
|
||||
List<GrantedConsentModel> johnConsents = john.getGrantedConsents();
|
||||
Assert.assertEquals(2, johnConsents.size());
|
||||
|
||||
List<GrantedConsentModel> maryConsents = mary.getGrantedConsents();
|
||||
Assert.assertEquals(1, maryConsents.size());
|
||||
GrantedConsentModel maryConsent = maryConsents.get(0);
|
||||
Assert.assertEquals(maryConsent.getClientId(), fooClient.getId());
|
||||
Assert.assertEquals(maryConsent.getGrantedRoles().size(), 1);
|
||||
Assert.assertEquals(maryConsent.getGrantedProtocolMappers().size(), 1);
|
||||
Assert.assertTrue(isRoleGranted(realm, "realm-role", maryConsent));
|
||||
Assert.assertTrue(isMapperGranted(fooClient, "foo", maryConsent));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateWithRoleRemovalTest() {
|
||||
RealmModel realm = realmManager.getRealm("original");
|
||||
ClientModel fooClient = realm.getClientNameMap().get("foo-client");
|
||||
UserModel john = session.users().getUserByUsername("john", realm);
|
||||
|
||||
GrantedConsentModel johnConsent = john.getGrantedConsentByClient(fooClient.getId());
|
||||
|
||||
// Remove foo protocol mapper from johnConsent
|
||||
ProtocolMapperModel protMapperModel = fooClient.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "foo");
|
||||
johnConsent.getGrantedProtocolMappers().remove(protMapperModel.getId());
|
||||
|
||||
// Remove realm-role and add new-realm-role to johnConsent
|
||||
RoleModel realmRole = realm.getRole("realm-role");
|
||||
johnConsent.getGrantedRoles().remove(realmRole.getId());
|
||||
|
||||
RoleModel newRealmRole = realm.addRole("new-realm-role");
|
||||
johnConsent.addGrantedRole(newRealmRole.getId());
|
||||
|
||||
john.updateGrantedConsent(johnConsent);
|
||||
|
||||
commit();
|
||||
|
||||
realm = realmManager.getRealm("original");
|
||||
fooClient = realm.getClientNameMap().get("foo-client");
|
||||
john = session.users().getUserByUsername("john", realm);
|
||||
johnConsent = john.getGrantedConsentByClient(fooClient.getId());
|
||||
|
||||
Assert.assertEquals(johnConsent.getGrantedRoles().size(), 2);
|
||||
Assert.assertEquals(johnConsent.getGrantedProtocolMappers().size(), 0);
|
||||
Assert.assertFalse(isRoleGranted(realm, "realm-role", johnConsent));
|
||||
Assert.assertTrue(isRoleGranted(realm, "new-realm-role", johnConsent));
|
||||
Assert.assertFalse(isMapperGranted(fooClient, "foo", johnConsent));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void revokeTest() {
|
||||
RealmModel realm = realmManager.getRealm("original");
|
||||
ClientModel fooClient = realm.getClientNameMap().get("foo-client");
|
||||
UserModel john = session.users().getUserByUsername("john", realm);
|
||||
|
||||
john.revokeGrantedConsentForClient(fooClient.getId());
|
||||
|
||||
commit();
|
||||
|
||||
realm = realmManager.getRealm("original");
|
||||
john = session.users().getUserByUsername("john", realm);
|
||||
Assert.assertNull(john.getGrantedConsentByClient(fooClient.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteUserTest() {
|
||||
// Validate user deleted without any referential constraint errors
|
||||
RealmModel realm = realmManager.getRealm("original");
|
||||
UserModel john = session.users().getUserByUsername("john", realm);
|
||||
session.users().removeUser(realm, john);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteProtocolMapperTest() {
|
||||
RealmModel realm = realmManager.getRealm("original");
|
||||
ClientModel fooClient = realm.getClientNameMap().get("foo-client");
|
||||
ProtocolMapperModel fooMapper = fooClient.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "foo");
|
||||
String fooMapperId = fooMapper.getId();
|
||||
fooClient.removeProtocolMapper(fooMapper);
|
||||
|
||||
commit();
|
||||
|
||||
realm = realmManager.getRealm("original");
|
||||
fooClient = realm.getClientNameMap().get("foo-client");
|
||||
UserModel john = session.users().getUserByUsername("john", realm);
|
||||
GrantedConsentModel johnConsent = john.getGrantedConsentByClient(fooClient.getId());
|
||||
|
||||
Assert.assertEquals(johnConsent.getGrantedRoles().size(), 2);
|
||||
Assert.assertEquals(johnConsent.getGrantedProtocolMappers().size(), 0);
|
||||
Assert.assertFalse(johnConsent.isProtocolMapperGranted(fooMapperId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteRoleTest() {
|
||||
RealmModel realm = realmManager.getRealm("original");
|
||||
RoleModel realmRole = realm.getRole("realm-role");
|
||||
String realmRoleId = realmRole.getId();
|
||||
realm.removeRole(realmRole);
|
||||
|
||||
commit();
|
||||
|
||||
realm = realmManager.getRealm("original");
|
||||
Map<String, ClientModel> clients = realm.getClientNameMap();
|
||||
ClientModel fooClient = clients.get("foo-client");
|
||||
ClientModel barClient = clients.get("bar-client");
|
||||
UserModel john = session.users().getUserByUsername("john", realm);
|
||||
GrantedConsentModel johnConsent = john.getGrantedConsentByClient(fooClient.getId());
|
||||
|
||||
Assert.assertEquals(johnConsent.getGrantedRoles().size(), 1);
|
||||
Assert.assertEquals(johnConsent.getGrantedProtocolMappers().size(), 1);
|
||||
Assert.assertFalse(johnConsent.isRoleGranted(realmRoleId));
|
||||
Assert.assertTrue(isRoleGranted(barClient, "bar-client-role", johnConsent));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteClientTest() {
|
||||
RealmModel realm = realmManager.getRealm("original");
|
||||
Map<String, ClientModel> clients = realm.getClientNameMap();
|
||||
ClientModel barClient = clients.get("bar-client");
|
||||
realm.removeClient(barClient.getId());
|
||||
|
||||
commit();
|
||||
|
||||
realm = realmManager.getRealm("original");
|
||||
clients = realm.getClientNameMap();
|
||||
ClientModel fooClient = clients.get("foo-client");
|
||||
Assert.assertNull(clients.get("bar-client"));
|
||||
|
||||
UserModel john = session.users().getUserByUsername("john", realm);
|
||||
|
||||
GrantedConsentModel johnFooConsent = john.getGrantedConsentByClient(fooClient.getId());
|
||||
Assert.assertEquals(johnFooConsent.getGrantedRoles().size(), 1);
|
||||
Assert.assertEquals(johnFooConsent.getGrantedProtocolMappers().size(), 1);
|
||||
Assert.assertTrue(isRoleGranted(realm, "realm-role", johnFooConsent));
|
||||
Assert.assertTrue(isMapperGranted(fooClient, "foo", johnFooConsent));
|
||||
|
||||
Assert.assertNull(john.getGrantedConsentByClient(barClient.getId()));
|
||||
}
|
||||
|
||||
private boolean isRoleGranted(RoleContainerModel roleContainer, String roleName, GrantedConsentModel consentModel) {
|
||||
RoleModel role = roleContainer.getRole(roleName);
|
||||
return consentModel.isRoleGranted(role.getId());
|
||||
}
|
||||
|
||||
private boolean isMapperGranted(ClientModel client, String protocolMapperName, GrantedConsentModel consentModel) {
|
||||
ProtocolMapperModel protocolMapper = client.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, protocolMapperName);
|
||||
return consentModel.isProtocolMapperGranted(protocolMapper.getId());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package org.keycloak.testsuite.pages;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.testsuite.Constants;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.WebElement;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class AccountAccessPage extends AbstractAccountPage {
|
||||
|
||||
private String path = Urls.accountAccessPage(UriBuilder.fromUri(Constants.AUTH_SERVER_ROOT).build(), "test").toString();
|
||||
|
||||
@Override
|
||||
public boolean isCurrent() {
|
||||
return driver.getTitle().contains("Account Management") && driver.getCurrentUrl().endsWith("/account/access");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() {
|
||||
driver.navigate().to(path);
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public void revokeGrant(String clientId) {
|
||||
driver.findElement(By.id("revoke-" + clientId)).click();
|
||||
}
|
||||
|
||||
public Map<String, ClientGrant> getClientGrants() {
|
||||
Map<String, ClientGrant> table = new HashMap<String, ClientGrant>();
|
||||
for (WebElement r : driver.findElements(By.tagName("tr"))) {
|
||||
int count = 0;
|
||||
ClientGrant currentGrant = null;
|
||||
|
||||
for (WebElement col : r.findElements(By.tagName("td"))) {
|
||||
count++;
|
||||
switch (count) {
|
||||
case 1:
|
||||
currentGrant = new ClientGrant();
|
||||
String clientId = col.getText();
|
||||
table.put(clientId, currentGrant);
|
||||
break;
|
||||
case 2:
|
||||
String protMappersStr = col.getText();
|
||||
String[] protMappers = protMappersStr.split(",");
|
||||
for (String protMapper : protMappers) {
|
||||
protMapper = protMapper.trim();
|
||||
currentGrant.addMapper(protMapper);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
String rolesStr = col.getText();
|
||||
String[] roles = rolesStr.split(",");
|
||||
for (String role : roles) {
|
||||
role = role.trim();
|
||||
currentGrant.addRole(role);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
table.remove("Client");
|
||||
return table;
|
||||
}
|
||||
|
||||
public static class ClientGrant {
|
||||
|
||||
private final List<String> protocolMapperDescriptions = new ArrayList<String>();
|
||||
private final List<String> roleDescriptions = new ArrayList<String>();
|
||||
|
||||
private void addMapper(String protocolMapper) {
|
||||
protocolMapperDescriptions.add(protocolMapper);
|
||||
}
|
||||
|
||||
private void addRole(String role) {
|
||||
roleDescriptions.add(role);
|
||||
}
|
||||
|
||||
public List<String> getProtocolMapperDescriptions() {
|
||||
return protocolMapperDescriptions;
|
||||
}
|
||||
|
||||
public List<String> getRoleDescriptions() {
|
||||
return roleDescriptions;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,7 +32,10 @@
|
|||
{ "type" : "password",
|
||||
"value" : "password" }
|
||||
],
|
||||
"realmRoles": ["manager"]
|
||||
"realmRoles": ["manager"],
|
||||
"applicationRoles": {
|
||||
"account": [ "manage-account" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username" : "test-user-noemail",
|
||||
|
@ -43,7 +46,10 @@
|
|||
{ "type" : "password",
|
||||
"value" : "password" }
|
||||
],
|
||||
"realmRoles": ["manager"]
|
||||
"realmRoles": ["manager"],
|
||||
"applicationRoles": {
|
||||
"account": [ "manage-account" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username" : "pedroigor",
|
||||
|
@ -53,7 +59,10 @@
|
|||
{ "type" : "password",
|
||||
"value" : "password" }
|
||||
],
|
||||
"realmRoles": ["manager"]
|
||||
"realmRoles": ["manager"],
|
||||
"applicationRoles": {
|
||||
"account": [ "manage-account" ]
|
||||
}
|
||||
}
|
||||
],
|
||||
"roles" : {
|
||||
|
|
Loading…
Reference in a new issue