Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
8eb2f35423
87 changed files with 2193 additions and 274 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>
|
||||
|
|
|
@ -18,7 +18,8 @@ public class UriUtils {
|
|||
|
||||
public static String getOrigin(String uri) {
|
||||
String u = uri.toString();
|
||||
return u.substring(0, u.indexOf('/', 8));
|
||||
int e = u.indexOf('/', 8);
|
||||
return e != -1 ? u.substring(0, u.indexOf('/', 8)) : u;
|
||||
}
|
||||
|
||||
public static boolean isOrigin(String url) {
|
||||
|
|
|
@ -80,14 +80,14 @@ HttpClient client = new HttpClientBuilder()
|
|||
try {
|
||||
HttpPost post = new HttpPost(
|
||||
KeycloakUriBuilder.fromUri("http://localhost:8080/auth")
|
||||
.path(ServiceUrlConstants.TOKEN_SERVICE_DIRECT_GRANT_PATH).build("demo"));
|
||||
.path(ServiceUrlConstants.TOKEN_PATH).build("demo"));
|
||||
List <NameValuePair> formparams = new ArrayList <NameValuePair>();
|
||||
formparams.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, "password"));
|
||||
formparams.add(new BasicNameValuePair("username", "bburke"));
|
||||
formparams.add(new BasicNameValuePair("password", "password"));
|
||||
|
||||
if (isPublic()) { // if client is public access type
|
||||
formparams.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, "customer-portal"));
|
||||
formparams.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, "password"));
|
||||
} else {
|
||||
String authorization = BasicAuthHelper.createHeader("customer-portal", "secret-secret-secret");
|
||||
post.setHeader("Authorization", authorization);
|
||||
|
|
|
@ -215,10 +215,11 @@ ktadd -k /tmp/http.keytab HTTP/www.mydomain.org@MYDOMAIN.ORG
|
|||
The scenario is supported by Keycloak, but there is tricky thing that SPNEGO authentication is done by Keycloak server but
|
||||
GSS credential will need to be used by your application. So you need to enable built-in <literal>gss delegation credential</literal> protocol mapper
|
||||
in admin console for your application. This will cause that Keycloak will deserialize GSS credential and transmit it to the application
|
||||
in access token. Application will need to deserialize it and use it for further GSS calls against other services.
|
||||
in access token. Application will need to deserialize it and use it for further GSS calls against other services. We have an example, which is showing it in details. It's in <literal>examples/kerberos</literal>
|
||||
in the Keycloak appliance distribution or WAR distribution download. You can also check the example sources directly <ulink url="https://github.com/keycloak/keycloak/blob/master/examples/kerberos">here</ulink> .
|
||||
</para>
|
||||
<para>
|
||||
GSSContext will need to
|
||||
Once you deserialize the credential from the access token to the GSSCredential object, then GSSContext will need to
|
||||
be created with this credential passed to the method <literal>GSSManager.createContext</literal> for example like this:
|
||||
<programlisting><![CDATA[
|
||||
GSSContext context = gssManager.createContext(serviceName, krb5Oid,
|
||||
|
@ -227,7 +228,7 @@ GSSContext context = gssManager.createContext(serviceName, krb5Oid,
|
|||
</para>
|
||||
<para>
|
||||
Note that you also need to configure <literal>forwardable</literal> kerberos tickets in <literal>krb5.conf</literal> file
|
||||
and add support for delegated credentials to your browser. See the kerberos example from Keycloak example set for details.
|
||||
and add support for delegated credentials to your browser. For details, see the kerberos example from Keycloak examples set as mentioned above.
|
||||
</para>
|
||||
<warning>
|
||||
<para>
|
||||
|
|
|
@ -128,9 +128,10 @@
|
|||
<para>
|
||||
In the admin console, per realm, you can set up a password policy to enforce that users pick hard to guess passwords.
|
||||
A password has to match all policies. The password policies that can be configured are hash iterations, length, digits,
|
||||
lowercase, uppercase, special characters, not username, regex patterns and expired passwords. Expired Passwords policy
|
||||
lowercase, uppercase, special characters, not username, regex patterns, password history and force expired password update.
|
||||
Force expired password update policy forces or requires password updates after specified span of time. Password history policy
|
||||
restricts a user from resetting his password to N old expired passwords. Multiple regex patterns, separated by comma,
|
||||
can be specified. If there's more than one regex added, password has to match all fully.
|
||||
can be specified in regex pattern policy. If there's more than one regex added, password has to match all fully.
|
||||
Increasing number of Hash Iterations (n) does not worsen anything (and certainly not the cipher) and it greatly increases the
|
||||
resistance to dictionary attacks. However the drawback to increasing n is that it has some cost (CPU usage, energy, delay) for
|
||||
the legitimate parties. Increasing n also slightly increases the odds that a random password gives the same result as the right
|
||||
|
|
|
@ -826,4 +826,36 @@ All configuration options are optional. Default value for directory is <literal>
|
|||
<link linkend='themes'>Themes</link> sections for more information on how to do this.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Installing Keycloak Server as Root Context</title>
|
||||
<para>
|
||||
The Keycloak server can be installed as the default web application. This way, instead of referencing
|
||||
the server as <literal>http://mydomain.com/auth</literal>, it would be
|
||||
<literal>http://mydomain.com/</literal>.
|
||||
</para>
|
||||
<para>
|
||||
To do this, you need to add a <literal>default-web-module</literal> attribute in the Undertow subystem in standalone.xml.
|
||||
<programlisting><![CDATA[
|
||||
<subsystem xmlns="urn:jboss:domain:undertow:1.2">
|
||||
<server name="default-server">
|
||||
<host name="default-host" alias="localhost" default-web-module="main-auth-server.war">
|
||||
<location name="/" handler="welcome-content"/>
|
||||
</host>
|
||||
]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
<literal>main-auth-server</literal> is the name of the Keycloak server as defined in the Keycloak subsystem.
|
||||
</para>
|
||||
<para>
|
||||
<note>
|
||||
If you have already run your server before changing to the root context then your database
|
||||
will contain references to the old /auth context. And, your clients may also have incorrect
|
||||
references. To fix this on the server side, you will need to <link linkend="export-import">export
|
||||
your database to json, make corrections, and then import.</link> Client-side keycloak.json
|
||||
files will need to be updated manually as well.
|
||||
</note>
|
||||
</para>
|
||||
|
||||
</section>
|
||||
</chapter>
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -92,6 +92,9 @@ public class LDAPIdentityStoreRegistry {
|
|||
case LDAPConstants.VENDOR_TIVOLI:
|
||||
uniqueIdentifierAttributeName = "uniqueidentifier";
|
||||
break;
|
||||
case LDAPConstants.VENDOR_NOVELL_EDIRECTORY:
|
||||
uniqueIdentifierAttributeName = "guid";
|
||||
break;
|
||||
case LDAPConstants.VENDOR_ACTIVE_DIRECTORY:
|
||||
uniqueIdentifierAttributeName = LDAPConstants.OBJECT_GUID;
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -99,5 +99,6 @@ invalidPasswordRegexPatternMessage=Ung\u00FCltiges Passwort\: nicht Regex-Muster
|
|||
invalidPasswordHistoryMessage=Ung\u00FCltiges Passwort: darf nicht gleich einem der letzten {0} Passwortgeschichte.
|
||||
|
||||
locale_de=Deutsch
|
||||
locale_en=Englisch
|
||||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
|
@ -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.
|
||||
|
||||
|
@ -99,4 +133,5 @@ invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last
|
|||
|
||||
locale_de=German
|
||||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
|
@ -0,0 +1,103 @@
|
|||
doSave=Salva
|
||||
doCancel=Annulla
|
||||
doLogOutAllSessions=Effettua Log out in tutte le sessioni
|
||||
doRemove=Elimina
|
||||
doAdd=Aggiungi
|
||||
doSignOut=Sign Out
|
||||
|
||||
editAccountHtmlTtile=Gestisci Account
|
||||
federatedIdentitiesHtmlTitle=Federated Identities
|
||||
accountLogHtmlTitle=Account Log
|
||||
changePasswordHtmlTitle=Cambia Password
|
||||
sessionsHtmlTitle=Sessioni
|
||||
accountManagementTitle=Keycloak Account Management
|
||||
authenticatorTitle=Authenticator
|
||||
|
||||
authenticatorCode=Codice One-time
|
||||
email=Email
|
||||
firstName=Nome
|
||||
lastName=Cognome
|
||||
password=Password
|
||||
passwordConfirm=Conferma Password
|
||||
passwordNew=Nuova Password
|
||||
username=Username
|
||||
street=Via
|
||||
locality=Citta'' o Localita''
|
||||
region=Stato, Provincia, o Regione
|
||||
postal_code=Cap
|
||||
country=Paese
|
||||
|
||||
requiredFields=Campi obbligatori
|
||||
allFieldsRequired=Tutti campi obbligatori
|
||||
|
||||
backToApplication=« Torna all''applicazione
|
||||
backTo=Torna a {0}
|
||||
|
||||
date=Data
|
||||
event=Evento
|
||||
ip=IP
|
||||
client=Client
|
||||
clients=Client
|
||||
details=Dettagli
|
||||
started=Iniziato
|
||||
lastAccess=Ultimo Accesso
|
||||
expires=Scade
|
||||
applications=Applicazioni
|
||||
|
||||
account=Account
|
||||
federatedIdentity=Federated Identity
|
||||
authenticator=Authenticator
|
||||
sessions=Sessioni
|
||||
log=Log
|
||||
|
||||
configureAuthenticators=Authenticator configurati
|
||||
mobile=Mobile
|
||||
totpStep1=Installa <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> o <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> sul tuo dispositivo mobile.
|
||||
totpStep2=Apri l''applicazione e scansiona il barcode o scrivi la chiave.
|
||||
totpStep3=Scrivi il codice one-time fornito dall''applicazione e clicca Salva per completare il setup.
|
||||
|
||||
missingFirstNameMessage=Inserisci il nome.
|
||||
invalidEmailMessage=Invalid email address.
|
||||
missingLastNameMessage=Inserisci il cognome.
|
||||
missingEmailMessage=Inserisci l''indirizzo email.
|
||||
missingPasswordMessage=Inserisci la password.
|
||||
notMatchPasswordMessage=La password non coincide.
|
||||
|
||||
missingTotpMessage=Inserisci il codice di autenticazione.
|
||||
invalidPasswordExistingMessage=Password esistente non valida.
|
||||
invalidPasswordConfirmMessage=La password di conferma non coincide.
|
||||
invalidTotpMessage=Codice di autenticazione non valido.
|
||||
|
||||
readOnlyUserMessage=Non puoi aggiornare il tuo account dal momento che e'' in sola lettura.
|
||||
readOnlyPasswordMessage=Non puoi aggiornare la tua password dal momento che e'' in sola lettura.
|
||||
|
||||
successTotpMessage=Mobile authenticator configurato.
|
||||
successTotpRemovedMessage=Mobile authenticator rliminato.
|
||||
|
||||
accountUpdatedMessage=Il tuo account e'' stato aggiornato.
|
||||
accountPasswordUpdatedMessage=La tua password e'' stata aggiornata.
|
||||
|
||||
missingIdentityProviderMessage=Identity provider non specificata.
|
||||
invalidFederatedIdentityActionMessage=Azione non valida o mancante.
|
||||
identityProviderNotFoundMessage=L''identity provider specificato non e'' stato trovato.
|
||||
federatedIdentityLinkNotActiveMessage=Questo identity non e'' piu'' attivo.
|
||||
federatedIdentityRemovingLastProviderMessage=Non puoi rimuovere l''ultimo federated identity dal momento che non hai piu'' la password.
|
||||
identityProviderRedirectErrorMessage=Fallito il redirect all''identity provider.
|
||||
identityProviderRemovedMessage=Identity provider eliminato con successo.
|
||||
|
||||
accountDisabledMessage=Account disabilitato, contatta l''amministratore.
|
||||
|
||||
accountTemporarilyDisabledMessage=L''account e'' temporaneamente disabilitato, contatta l''admin o riprova piu'' tardi.
|
||||
invalidPasswordMinLengthMessage=Password non valida: lunghezza minima {0}.
|
||||
invalidPasswordMinLowerCaseCharsMessage=Password non valida: deve contenere almeno {0} caratteri minuscoli.
|
||||
invalidPasswordMinDigitsMessage=Password non valida: deve contenere almeno {0} numeri.
|
||||
invalidPasswordMinUpperCaseCharsMessage=Password non valida: deve contenere almeno {0} caratteri maiuscoli.
|
||||
invalidPasswordMinSpecialCharsMessage=Password non valida: deve contenere almeno {0} caratteri speciali.
|
||||
invalidPasswordNotUsernameMessage=Password non valida: non deve essere uguale allo username.
|
||||
invalidPasswordRegexPatternMessage=Password non valida: fallito il match con una o piu'' espressioni regolari.
|
||||
invalidPasswordHistoryMessage=Password non valida: non deve ssere uguale ad una delle ultime {0} password.
|
||||
|
||||
locale_de=German
|
||||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
|
@ -100,4 +100,5 @@ invalidPasswordHistoryMessage=Senha inv\u00E1lida\: n\u00E3o deve ser igual a qu
|
|||
|
||||
locale_de=Deutsch
|
||||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (BR)
|
|
@ -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>
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
<div data-ng-include data-src="resourceUrl + '/partials/menu.html'"></div>
|
||||
</header>
|
||||
|
||||
<div class="container" data-ng-show="auth.hasAnyAccess">
|
||||
<div class="container">
|
||||
<div data-ng-view id="view"></div>
|
||||
<div id="loading" class="loading-backdrop">
|
||||
<div class="loading">
|
||||
|
|
|
@ -960,8 +960,6 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
controller : 'ProtocolListCtrl'
|
||||
})
|
||||
|
||||
|
||||
.when('/server-info', {
|
||||
templateUrl : resourceUrl + '/partials/server-info.html'
|
||||
})
|
||||
|
@ -969,8 +967,14 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
templateUrl : resourceUrl + '/partials/home.html',
|
||||
controller : 'LogoutCtrl'
|
||||
})
|
||||
.otherwise({
|
||||
.when('/notfound', {
|
||||
templateUrl : resourceUrl + '/partials/notfound.html'
|
||||
})
|
||||
.when('/forbidden', {
|
||||
templateUrl : resourceUrl + '/partials/forbidden.html'
|
||||
})
|
||||
.otherwise({
|
||||
templateUrl : resourceUrl + '/partials/pagenotfound.html'
|
||||
});
|
||||
} ]);
|
||||
|
||||
|
@ -994,29 +998,6 @@ module.config(function($httpProvider) {
|
|||
|
||||
});
|
||||
|
||||
module.factory('errorInterceptor', function($q, $window, $rootScope, $location, Notifications, Auth) {
|
||||
return function(promise) {
|
||||
return promise.then(function(response) {
|
||||
return response;
|
||||
}, function(response) {
|
||||
if (response.status == 401) {
|
||||
Auth.authz.logout();
|
||||
} else if (response.status == 403) {
|
||||
Notifications.error("Forbidden");
|
||||
} else if (response.status == 404) {
|
||||
Notifications.error("Not found");
|
||||
} else if (response.status) {
|
||||
if (response.data && response.data.errorMessage) {
|
||||
Notifications.error(response.data.errorMessage);
|
||||
} else {
|
||||
Notifications.error("An unexpected server error has occurred");
|
||||
}
|
||||
}
|
||||
return $q.reject(response);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.factory('spinnerInterceptor', function($q, $window, $rootScope, $location) {
|
||||
return function(promise) {
|
||||
return promise.then(function(response) {
|
||||
|
@ -1044,6 +1025,29 @@ module.factory('spinnerInterceptor', function($q, $window, $rootScope, $location
|
|||
};
|
||||
});
|
||||
|
||||
module.factory('errorInterceptor', function($q, $window, $rootScope, $location, Notifications, Auth) {
|
||||
return function(promise) {
|
||||
return promise.then(function(response) {
|
||||
return response;
|
||||
}, function(response) {
|
||||
if (response.status == 401) {
|
||||
Auth.authz.logout();
|
||||
} else if (response.status == 403) {
|
||||
$location.path('/forbidden');
|
||||
} else if (response.status == 404) {
|
||||
$location.path('/notfound');
|
||||
} else if (response.status) {
|
||||
if (response.data && response.data.errorMessage) {
|
||||
Notifications.error(response.data.errorMessage);
|
||||
} else {
|
||||
Notifications.error("An unexpected server error has occurred");
|
||||
}
|
||||
}
|
||||
return $q.reject(response);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
// collapsable form fieldsets
|
||||
module.directive('collapsable', function() {
|
||||
return function(scope, element, attrs) {
|
||||
|
|
|
@ -121,12 +121,8 @@ module.controller('RealmDropdownCtrl', function($scope, Realm, Current, Auth, $l
|
|||
|
||||
$scope.changeRealm = function(selectedRealm) {
|
||||
$location.url("/realms/" + selectedRealm);
|
||||
};
|
||||
|
||||
$scope.showNav = function() {
|
||||
var show = Current.realms.length > 0;
|
||||
return Auth.loggedIn && show;
|
||||
}
|
||||
|
||||
$scope.refresh = function() {
|
||||
Current.refresh();
|
||||
}
|
||||
|
@ -399,7 +395,7 @@ module.controller('RealmThemeCtrl', function($scope, Current, Realm, realm, serv
|
|||
$scope.supportedLocalesOptions = {
|
||||
'multiple' : true,
|
||||
'simple_tags' : true,
|
||||
'tags' : ['en', 'de', 'pt-BR']
|
||||
'tags' : ['en', 'de', 'pt-BR', 'it']
|
||||
};
|
||||
|
||||
$scope.$watch('realm.supportedLocales', function(oldVal, newVal) {
|
||||
|
|
|
@ -547,6 +547,7 @@ module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog,
|
|||
{ "id": "ad", "name": "Active Directory" },
|
||||
{ "id": "rhds", "name": "Red Hat Directory Server" },
|
||||
{ "id": "tivoli", "name": "Tivoli" },
|
||||
{ "id": "edirectory", "name": "Novell eDirectory" },
|
||||
{ "id": "other", "name": "Other" }
|
||||
];
|
||||
|
||||
|
|
|
@ -908,7 +908,8 @@ module.factory('PasswordPolicy', function() {
|
|||
specialChars: "Minimal number (integer type) of special characters in password. Default value is 1.",
|
||||
notUsername: "Block passwords that are equal to the username",
|
||||
regexPatterns: "Block passwords that do not match all of the regex patterns (string type).",
|
||||
passwordHistory: "Block passwords that are equal to previous passwords. Default value is 3."
|
||||
passwordHistory: "Block passwords that are equal to previous passwords. Default value is 3.",
|
||||
forceExpiredPasswordChange: "Force password change when password credential is expired. Default value is 365 days."
|
||||
}
|
||||
|
||||
p.allPolicies = [
|
||||
|
@ -920,7 +921,8 @@ module.factory('PasswordPolicy', function() {
|
|||
{ name: 'specialChars', value: 1 },
|
||||
{ name: 'notUsername', value: 1 },
|
||||
{ name: 'regexPatterns', value: ''},
|
||||
{ name: 'passwordHistory', value: 3 }
|
||||
{ name: 'passwordHistory', value: 3 },
|
||||
{ name: 'forceExpiredPasswordChange', value: 365 }
|
||||
];
|
||||
|
||||
p.parse = function(policyString) {
|
||||
|
|
|
@ -178,12 +178,11 @@
|
|||
<span tooltip-placement="right" tooltip="Valid URI pattern a browser can redirect to after a successful login or logout. Simple wildcards are allowed i.e. 'http://example.com/*'. Relative path can be specified too i.e. /my/relative/path/*. Relative paths will generate a redirect URI using the request's host and port. For SAML, you must set valid URI patterns if you are relying on the consumer service URL embedded with the login request." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group" data-ng-show="!client.bearerOnly && !create">
|
||||
<label class="col-sm-2 control-label" for="baseUrl">Default Redirect URL</label>
|
||||
<label class="col-sm-2 control-label" for="baseUrl">Base URL</label>
|
||||
<div class="col-sm-6">
|
||||
<input class="form-control" type="text" name="baseUrl" id="baseUrl"
|
||||
data-ng-model="client.baseUrl">
|
||||
<input class="form-control" type="text" name="baseUrl" id="baseUrl" data-ng-model="client.baseUrl">
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Default URL to use when the auth server needs to redirect back to the client. This URL will also be used when the auth server needs to link to the client for any reason." class="fa fa-info-circle"></span>
|
||||
<span tooltip-placement="right" tooltip="Default URL to use when the auth server needs to redirect or link back to the client." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group" data-ng-hide="create || protocol == 'saml'">
|
||||
<label class="col-sm-2 control-label" for="adminUrl">Admin URL</label>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<div id="content-area" class="col-sm-12" role="main">
|
||||
<div class="error-container">
|
||||
<h2>Forbidden</h2>
|
||||
<p class="instruction">You don't have access to the requested resource.</p>
|
||||
<a href="#" class="link-right">Go to the home page »</a>
|
||||
</div>
|
||||
</div>
|
|
@ -26,10 +26,10 @@
|
|||
</li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-primary persistent-secondary" data-ng-controller="RealmDropdownCtrl">
|
||||
<li class="dropdown context" data-ng-show="showNav()">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<li class="dropdown context" data-ng-show="current.realm.realm">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
{{current.realm.realm}}
|
||||
<b class="caret"></b>
|
||||
<b class="caret" data-ng-show="current.realms.length > 1"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu" data-ng-show="current.realms.length > 1">
|
||||
<li data-ng-repeat="realm in current.realms" data-ng-if="realm.realm != current.realm.realm">
|
||||
|
@ -37,6 +37,17 @@
|
|||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown context" data-ng-show="!current.realm.realm">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
Select realm...
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li data-ng-repeat="realm in current.realms">
|
||||
<a href="" ng-click="changeRealm(realm.realm)">{{realm.realm}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="active pull-right" data-ng-show="auth.user && access.createRealm">
|
||||
<a class="button primary" href="#/create/realm" data-ng-class="path[0] == 'create' && path[1] == 'realm' && 'active'"
|
||||
data-ng-show="auth.user">Add Realm</a>
|
||||
|
@ -45,6 +56,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- TODO remove once this page is properly styled -->
|
||||
<style type="text/css">
|
||||
.icon-spinner6 {
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
<div id="wrapper" class="container">
|
||||
<div class="row">
|
||||
<div class="bs-sidebar col-md-3 clearfix"></div>
|
||||
<div id="content-area" class="col-md-9" role="main">
|
||||
<div class="error-container">
|
||||
<h2>Page <strong>not found</strong>...</h2>
|
||||
<p class="instruction">We could not find the page you are looking for. Please make sure the URL you entered is correct.</p>
|
||||
<a href="#" class="link-right">Go to the home page »</a>
|
||||
<!-- <a href="#" class="link-right">Go to the realm page »</a> -->
|
||||
</div>
|
||||
</div>
|
||||
<div id="container-right-bg"></div>
|
||||
<div id="content-area" class="col-sm-12" role="main">
|
||||
<div class="error-container">
|
||||
<h2>Resource <strong>not found</strong>...</h2>
|
||||
<p class="instruction">We could not find the resource you are looking for. Please make sure the URL you entered is correct.</p>
|
||||
<a href="#" class="link-right">Go to the home page »</a>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,7 @@
|
|||
<div id="content-area" class="col-sm-12" role="main">
|
||||
<div class="error-container">
|
||||
<h2>Page <strong>not found</strong>...</h2>
|
||||
<p class="instruction">We could not find the page you are looking for. Please make sure the URL you entered is correct.</p>
|
||||
<a href="#" class="link-right">Go to the home page »</a>
|
||||
</div>
|
||||
</div>
|
|
@ -7,6 +7,9 @@
|
|||
<#elseif section = "form">
|
||||
<div id="kc-error-message">
|
||||
<p class="instruction">${message.summary}</p>
|
||||
<#if client?? && client.baseUrl?has_content>
|
||||
<p><a href="${client.baseUrl}">${msg("backToApplication")}</a></p>
|
||||
</#if>
|
||||
</div>
|
||||
</#if>
|
||||
</@layout.registrationLayout>
|
|
@ -1,4 +1,3 @@
|
|||
<#-- TODO: Only a placeholder, implementation needed -->
|
||||
<#import "template.ftl" as layout>
|
||||
<@layout.registrationLayout bodyClass="oauth"; section>
|
||||
<#if section = "title">
|
||||
|
|
|
@ -174,5 +174,6 @@ clientNotFoundMessage=Client not found.
|
|||
emailVerifiedMessage=Ihr E-Mail Adresse wurde erfolgreich verifiziert.
|
||||
|
||||
locale_de=Deutsch
|
||||
locale_en=Englisch
|
||||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
|
@ -157,8 +157,7 @@ couldNotSendAuthenticationRequestMessage=Could not send authentication request t
|
|||
unexpectedErrorHandlingRequestMessage=Unexpected error when handling authentication request to identity provider [{0}].
|
||||
invalidAccessCodeMessage=Invalid access code.
|
||||
sessionNotActiveMessage=Session not active.
|
||||
unknownCodeMessage=Unknown code, please login again through your application.
|
||||
invalidCodeMessage=Invalid code, please login again through your application.
|
||||
invalidCodeMessage=An error occurred, please login again through your application.
|
||||
identityProviderUnexpectedErrorMessage=Unexpected error when authenticating with identity provider
|
||||
identityProviderNotFoundMessage=Could not find an identity provider with the identifier [{0}].
|
||||
realmSupportsNoCredentialsMessage=Realm [{0}] does not support any credential type.
|
||||
|
@ -167,6 +166,7 @@ emailVerifiedMessage=Your email address has been verified.
|
|||
|
||||
locale_de=German
|
||||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
|
||||
backToApplication=« Back to Application
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
doLogIn=Accedi
|
||||
doRegister=Registrati
|
||||
doCancel=Annulla
|
||||
doSubmit=Invia
|
||||
doYes=Si
|
||||
doNo=No
|
||||
doForgotPassword=Password Dimenticata?
|
||||
doClickHere=Clicca qui
|
||||
|
||||
registerWithTitle=Registrati come {0}
|
||||
registerWithTitleHtml=Registrati come <strong>{0}</strong>
|
||||
loginTitle=Accedi a {0}
|
||||
loginTitleHtml=Accedi a <strong>{0}</strong>
|
||||
loginTotpTitle=Configura Autenticazione Mobile
|
||||
loginProfileTitle=Aggiorna Profilo
|
||||
oauthGrantTitle=OAuth Grant
|
||||
oauthGrantTitleHtml=Accesso temporaneo per <strong>{0}</strong> richiesto da <strong>{1}</strong>.
|
||||
errorTitle=Siamo spiacenti...
|
||||
errorTitleHtml=Siamo <strong>spiacenti</strong> ...
|
||||
emailVerifyTitle=Verifica Email
|
||||
emailForgotTitle=Password Dimenticata?
|
||||
updatePasswordTitle=Modifica Password
|
||||
codeSuccessTitle=Codice di Successo
|
||||
codeErrorTitle=Codice di Errore\: {0}
|
||||
|
||||
noAccount=Nuovo Utente?
|
||||
username=Username
|
||||
usernameOrEmail=Username o email
|
||||
firstName=Nome
|
||||
givenName=Nome
|
||||
fullName=Nome Completo
|
||||
lastName=Cognome
|
||||
familyName=Cognome
|
||||
email=Email
|
||||
password=Password
|
||||
passwordConfirm=Conferma password
|
||||
passwordNew=Nuova Password
|
||||
passwordNewConfirm=Conferma nuova password
|
||||
rememberMe=Ricordami
|
||||
authenticatorCode=Codice One-time
|
||||
address=Indirizzo
|
||||
street=Via
|
||||
locality=Citta'' o Localita''
|
||||
region=Stato, Provincia, o Regione
|
||||
postal_code=Cap
|
||||
country=Paese
|
||||
emailVerified=Email verificata
|
||||
gssDelegationCredential=credenziali gss delegation
|
||||
|
||||
loginTotpStep1=Installa <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> or <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> sul tuo dispositivo mobile
|
||||
loginTotpStep2=Apri l''applicazione e scansione il barcode o scrivi la chiave
|
||||
loginTotpStep3=Scrivi il codice one-time fornito dall''applicazione e premi Invia per finire il setup
|
||||
loginTotpOneTime=Codice one-time
|
||||
|
||||
oauthGrantRequest=Vuoi assegnare questi privilegi di accesso?
|
||||
inResource=per <strong>{0}</strong>
|
||||
|
||||
emailVerifyInstruction1=Ti e'' stata inviata una email con le istruzioni per la verifica della tua email.
|
||||
emailVerifyInstruction2=Non hai ricevuto un codice di verifica nella tua email?
|
||||
emailVerifyInstruction3=per reinviare la mail.
|
||||
|
||||
backToLogin=« Torna al Login
|
||||
|
||||
emailInstruction=Scrivi il tuo username o indirizzo email e noi ti invieremo le istruzioni per creare una nuova password.
|
||||
|
||||
copyCodeInstruction=Copiaquesto codice e incollalo nella tua applicazione:
|
||||
|
||||
personalInfo=Informazioni personali:
|
||||
|
||||
role_admin=Admin
|
||||
role_realm-admin=Realm Admin
|
||||
role_create-realm=Crea realm
|
||||
role_view-realm=Visualizza realm
|
||||
role_view-users=Visualizza utenti
|
||||
role_view-applications=Visualizza applicazioni
|
||||
role_view-clients=Visualizza client
|
||||
role_view-events=Visualizza eventi
|
||||
role_view-identity-providers=Visualizza identity provider
|
||||
role_manage-realm=Gestisci realm
|
||||
role_manage-users=Gestisci utenti
|
||||
role_manage-applications=Gestisci applicazioni
|
||||
role_manage-identity-providers=Gestisci identity provider
|
||||
role_manage-clients=Gestisci client
|
||||
role_manage-events=Gestisci eventi
|
||||
role_view-profile=Visualizza profilo
|
||||
role_manage-account=Gestisci account
|
||||
|
||||
invalidUserMessage=Username o password non valida.
|
||||
invalidEmailMessage=Indirizzo email non valido.
|
||||
accountDisabledMessage=Account disabilitato, contatta l''admin.
|
||||
accountTemporarilyDisabledMessage=Account temporaneamente disabilitato, contatta l''admin o riprova piu'' tardi.
|
||||
expiredCodeMessage=Login timeout. Effettua login di nuovo.
|
||||
|
||||
missingFirstNameMessage=Inserisci il nome.
|
||||
missingLastNameMessage=Inserisci il cognome.
|
||||
missingEmailMessage=Inserisci l''indirizzo email.
|
||||
missingUsernameMessage=Inserisci lo username.
|
||||
missingPasswordMessage=Inserisci la password.
|
||||
missingTotpMessage=Inserisci il codice di autenticazione.
|
||||
notMatchPasswordMessage=Le passwords non coincidono.
|
||||
|
||||
invalidPasswordExistingMessage=Password esistente non valida.
|
||||
invalidPasswordConfirmMessage=La password di conferma non coincide.
|
||||
invalidTotpMessage=Codice di autenticazione non valido.
|
||||
|
||||
usernameExistsMessage=Username gia'' esistente.
|
||||
emailExistsMessage=Email gia'' esistente.
|
||||
|
||||
federatedIdentityEmailExistsMessage=Utente con email gia'' esistente. Effettua il login nella gestione account per associare l''account.
|
||||
federatedIdentityUsernameExistsMessage=Utente con username gia'' esistente.Effettua il login nella gestione account per associare l''account.
|
||||
|
||||
configureTotpMessage=Devi impostare un Mobile Authenticator per attivare il tuo account.
|
||||
updateProfileMessage=Devi aggiornare il tuo profilo utente per attivare il tuo account.
|
||||
updatePasswordMessage=Devi cambiare la password per attivare il tuo account.
|
||||
verifyEmailMessage=Devi verificare il tuo indirizzo email per attivare il tuo account.
|
||||
|
||||
emailSentMessage=Riceverai a breve una email con maggiori istruzioni.
|
||||
emailSendErrorMessage=Invio email fallito, riptoav piu'' tardi.
|
||||
|
||||
accountUpdatedMessage=Il tuo account e'' stato aggiornato.
|
||||
accountPasswordUpdatedMessage=La tua password e'' stata aggiornata.
|
||||
|
||||
noAccessMessage=Nessun accesso
|
||||
|
||||
invalidPasswordMinLengthMessage=Password non valida: lunghezza minima {0}.
|
||||
invalidPasswordMinDigitsMessage=Password non valida: deve contenere almeno {0} numeri.
|
||||
invalidPasswordMinLowerCaseCharsMessage=Password non valida: deve contenere almeno {0} caratteri minuscoli.
|
||||
invalidPasswordMinUpperCaseCharsMessage=Password non valida: deve contenere almeno {0} caratteri maiuscoli.
|
||||
invalidPasswordMinSpecialCharsMessage=Password non valida: deve contenere almeno {0} caratteri speciali.
|
||||
invalidPasswordNotUsernameMessage=Password non valida: non deve essere uguale allo username.
|
||||
invalidPasswordRegexPatternMessage=Password non valida: fallito il match con una o piu'' espressioni regolari.
|
||||
invalidPasswordHistoryMessage=Password non valida: non deve ssere uguale ad una delle ultime {0} password.
|
||||
|
||||
failedToProcessResponseMessage=Fallimento nell''elaborazione della risposta
|
||||
httpsRequiredMessage=HTTPS richiesto
|
||||
realmNotEnabledMessage=Realm non abilitato
|
||||
invalidRequestMessage=Richiesta non valida
|
||||
unknownLoginRequesterMessage=Richiedente di Login non riconosciuto
|
||||
loginRequesterNotEnabledMessage=Richiedente di Login non abilitato
|
||||
bearerOnlyMessage=Alle applicazioni di tipo Bearer-only non e'' consentito di effettuare il login tramite browser
|
||||
directGrantsOnlyMessage=Ai client di tipo Direct-grants-only non e'' consentito di effettuare il login tramite browser
|
||||
invalidRedirectUriMessage=Redirect uri non valido
|
||||
unsupportedNameIdFormatMessage=NameIDFormat non supportato
|
||||
invlidRequesterMessage=Richiedente non valido
|
||||
registrationNotAllowedMessage=Registrazione non permessa
|
||||
|
||||
permissionNotApprovedMessage=Permesso non approvato.
|
||||
noRelayStateInResponseMessage=Nessun relay state in risposta dall''identity provider [{0}].
|
||||
identityProviderAlreadyLinkedMessage=L''identita'' restituita dall''identity provider [{0}] e'' gia'' associata ad un altro utente.
|
||||
insufficientPermissionMessage=Permessi insufficienti per associare le identita''.
|
||||
couldNotProceedWithAuthenticationRequestMessage=Non posso procedere con la richiesta di autenticazione all''identity provider.
|
||||
couldNotObtainTokenMessage=Non posso ottenere un token dall''identity provider [{0}].
|
||||
unexpectedErrorRetrievingTokenMessage=Errore inaspettato nella gestione del token dall''identity provider [{0}].
|
||||
unexpectedErrorHandlingResponseMessage=Errore inaspettato nella gestione della risposta dall''identity provider [{0}].
|
||||
identityProviderAuthenticationFailedMessage=Autenticazione fallita. Non posso effettuare l''autenticazione con l''identity provider [{0}].
|
||||
couldNotSendAuthenticationRequestMessage=Non posso inviare la richiesta di autenticazione all''identity provider [{0}].
|
||||
unexpectedErrorHandlingRequestMessage=Errore imprevisto durante l''autenticazione con identity provider [{0}].
|
||||
invalidAccessCodeMessage=Codice di accesso non valido.
|
||||
sessionNotActiveMessage=Sessione non attiva.
|
||||
invalidCodeMessage=Si e'' verificato un errore, per piacere effettua di nuovo il login nella tua applicazione.
|
||||
identityProviderUnexpectedErrorMessage=Errore imprevisto durante l''autenticazione con identity provider
|
||||
identityProviderNotFoundMessage=Non posso trovare un identity provider con l''identificativo [{0}].
|
||||
realmSupportsNoCredentialsMessage=Il Realm [{0}] non supporta nessun tipo di credenziali.
|
||||
identityProviderNotUniqueMessage=Il Realm [{0}] supporta piu'' di un identity provider. Non posso determinare quale identity provider con il quale autenticarti.
|
||||
emailVerifiedMessage=Il tuo indirizzo email e'' stato verificato.
|
||||
|
||||
locale_de=German
|
||||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
|
||||
backToApplication=« Torna all''Applicazione
|
||||
missingParameterMessage=Parametri Mancanti\: {0}
|
||||
clientNotFoundMessage=Client non trovato.
|
||||
invalidParameterMessage=Parametro non valido\: {0}
|
|
@ -167,6 +167,7 @@ emailVerifiedMessage=O seu endere\u00E7o de e-mail foi confirmado.
|
|||
|
||||
locale_de=Deutsch
|
||||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (BR)
|
||||
|
||||
backToApplication=« Voltar para a aplica\u00E7\u00E3o
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -10,6 +10,7 @@ public class LDAPConstants {
|
|||
public static final String VENDOR_ACTIVE_DIRECTORY = "ad";
|
||||
public static final String VENDOR_OTHER = "other";
|
||||
public static final String VENDOR_TIVOLI = "tivoli";
|
||||
public static final String VENDOR_NOVELL_EDIRECTORY="edirectory" ;
|
||||
|
||||
public static final String USERNAME_LDAP_ATTRIBUTE = "usernameLDAPAttribute";
|
||||
public static final String USER_OBJECT_CLASSES = "userObjectClasses";
|
||||
|
|
|
@ -78,6 +78,8 @@ public class PasswordPolicy {
|
|||
list.add(new RegexPatterns(args));
|
||||
} else if (name.equals(PasswordHistory.NAME)) {
|
||||
list.add(new PasswordHistory(args));
|
||||
} else if (name.equals(ForceExpiredPasswordChange.NAME)) {
|
||||
list.add(new ForceExpiredPasswordChange(args));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
|
@ -115,6 +117,22 @@ public class PasswordPolicy {
|
|||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return -1 if no force expired password change setting
|
||||
*/
|
||||
public int getDaysToExpirePassword() {
|
||||
if (policies == null)
|
||||
return -1;
|
||||
for (Policy p : policies) {
|
||||
if (p instanceof ForceExpiredPasswordChange) {
|
||||
return ((ForceExpiredPasswordChange) p).daysToExpirePassword;
|
||||
}
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Error validate(UserModel user, String password) {
|
||||
for (Policy p : policies) {
|
||||
Error error = p.validate(user, password);
|
||||
|
@ -418,6 +436,25 @@ public class PasswordPolicy {
|
|||
}
|
||||
}
|
||||
|
||||
private static class ForceExpiredPasswordChange implements Policy {
|
||||
private static final String NAME = "forceExpiredPasswordChange";
|
||||
private int daysToExpirePassword;
|
||||
|
||||
public ForceExpiredPasswordChange(String[] args) {
|
||||
daysToExpirePassword = intArg(NAME, 365, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Error validate(String username, String password) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Error validate(UserModel user, String password) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static int intArg(String policy, int defaultValue, String... args) {
|
||||
if (args == null || args.length == 0) {
|
||||
return defaultValue;
|
||||
|
|
|
@ -12,7 +12,7 @@ public class UserCredentialValueModel {
|
|||
private String device;
|
||||
private byte[] salt;
|
||||
private int hashIterations;
|
||||
private long createdDate;
|
||||
private Long createdDate;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
|
@ -54,11 +54,11 @@ public class UserCredentialValueModel {
|
|||
this.hashIterations = iterations;
|
||||
}
|
||||
|
||||
public long getCreatedDate() {
|
||||
public Long getCreatedDate() {
|
||||
return createdDate;
|
||||
}
|
||||
|
||||
public void setCreatedDate(long createdDate) {
|
||||
public void setCreatedDate(Long createdDate) {
|
||||
this.createdDate = createdDate;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -11,7 +11,7 @@ public class CredentialEntity {
|
|||
private String device;
|
||||
private byte[] salt;
|
||||
private int hashIterations;
|
||||
private long createdDate;
|
||||
private Long createdDate;
|
||||
private UserEntity user;
|
||||
|
||||
|
||||
|
@ -63,11 +63,11 @@ public class CredentialEntity {
|
|||
this.hashIterations = hashIterations;
|
||||
}
|
||||
|
||||
public long getCreatedDate() {
|
||||
public Long getCreatedDate() {
|
||||
return createdDate;
|
||||
}
|
||||
|
||||
public void setCreatedDate(long createdDate) {
|
||||
public void setCreatedDate(Long createdDate) {
|
||||
this.createdDate = createdDate;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.keycloak.representations.idm.ScopeMappingRepresentation;
|
|||
import org.keycloak.representations.idm.SocialLinkRepresentation;
|
||||
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.util.UriUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
@ -526,7 +527,7 @@ public class RepresentationToModel {
|
|||
client.setManagementUrl(resourceRep.getAdminUrl());
|
||||
if (resourceRep.isSurrogateAuthRequired() != null)
|
||||
client.setSurrogateAuthRequired(resourceRep.isSurrogateAuthRequired());
|
||||
client.setBaseUrl(resourceRep.getBaseUrl());
|
||||
if (resourceRep.getBaseUrl() != null) client.setBaseUrl(resourceRep.getBaseUrl());
|
||||
if (resourceRep.isBearerOnly() != null) client.setBearerOnly(resourceRep.isBearerOnly());
|
||||
if (resourceRep.isConsentRequired() != null) client.setConsentRequired(resourceRep.isConsentRequired());
|
||||
if (resourceRep.isPublicClient() != null) client.setPublicClient(resourceRep.isPublicClient());
|
||||
|
@ -576,12 +577,8 @@ public class RepresentationToModel {
|
|||
Set<String> origins = new HashSet<String>();
|
||||
for (String redirectUri : resourceRep.getRedirectUris()) {
|
||||
logger.debugv("add redirect-uri to origin: {0}", redirectUri);
|
||||
if (redirectUri.startsWith("http:")) {
|
||||
URI uri = URI.create(redirectUri);
|
||||
String origin = uri.getScheme() + "://" + uri.getHost();
|
||||
if (uri.getPort() != -1) {
|
||||
origin += ":" + uri.getPort();
|
||||
}
|
||||
if (redirectUri.startsWith("http")) {
|
||||
String origin = UriUtils.getOrigin(redirectUri);
|
||||
logger.debugv("adding default client origin: {0}" , origin);
|
||||
origins.add(origin);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -17,8 +17,10 @@
|
|||
package org.keycloak.models.file.adapter;
|
||||
|
||||
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;
|
||||
|
@ -31,7 +33,6 @@ import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -43,6 +44,7 @@ import org.keycloak.models.ModelDuplicateException;
|
|||
import org.keycloak.models.entities.FederatedIdentityEntity;
|
||||
import org.keycloak.models.entities.RoleEntity;
|
||||
import org.keycloak.models.entities.UserEntity;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
/**
|
||||
* UserModel for JSON persistence.
|
||||
|
@ -271,7 +273,6 @@ public class UserAdapter implements UserModel, Comparable {
|
|||
private CredentialEntity setCredentials(UserEntity user, UserCredentialModel cred) {
|
||||
CredentialEntity credentialEntity = new CredentialEntity();
|
||||
credentialEntity.setType(cred.getType());
|
||||
credentialEntity.setCreatedDate(new Date().getTime());
|
||||
credentialEntity.setDevice(cred.getDevice());
|
||||
return credentialEntity;
|
||||
}
|
||||
|
@ -285,6 +286,7 @@ public class UserAdapter implements UserModel, Comparable {
|
|||
if (hashIterations == -1)
|
||||
hashIterations = 1;
|
||||
}
|
||||
credentialEntity.setCreatedDate(Time.toMillis(Time.currentTime()));
|
||||
credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
|
||||
credentialEntity.setSalt(salt);
|
||||
credentialEntity.setHashIterations(hashIterations);
|
||||
|
@ -429,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,12 +12,16 @@ 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;
|
||||
import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
@ -23,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;
|
||||
|
@ -273,7 +281,6 @@ public class UserAdapter implements UserModel {
|
|||
CredentialEntity credentialEntity = new CredentialEntity();
|
||||
credentialEntity.setId(KeycloakModelUtils.generateId());
|
||||
credentialEntity.setType(cred.getType());
|
||||
credentialEntity.setCreatedDate(new Date().getTime());
|
||||
credentialEntity.setDevice(cred.getDevice());
|
||||
credentialEntity.setUser(user);
|
||||
return credentialEntity;
|
||||
|
@ -288,6 +295,7 @@ public class UserAdapter implements UserModel {
|
|||
if (hashIterations == -1)
|
||||
hashIterations = 1;
|
||||
}
|
||||
credentialEntity.setCreatedDate(Time.toMillis(Time.currentTime()));
|
||||
credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
|
||||
credentialEntity.setSalt(salt);
|
||||
credentialEntity.setHashIterations(hashIterations);
|
||||
|
@ -471,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;
|
||||
|
|
|
@ -38,7 +38,7 @@ public class CredentialEntity {
|
|||
@Column(name="HASH_ITERATIONS")
|
||||
protected int hashIterations;
|
||||
@Column(name="CREATED_DATE")
|
||||
protected long createdDate;
|
||||
protected Long createdDate;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name="USER_ID")
|
||||
|
@ -100,11 +100,11 @@ public class CredentialEntity {
|
|||
this.hashIterations = hashIterations;
|
||||
}
|
||||
|
||||
public long getCreatedDate() {
|
||||
public Long getCreatedDate() {
|
||||
return createdDate;
|
||||
}
|
||||
|
||||
public void setCreatedDate(long createdDate) {
|
||||
public void setCreatedDate(Long createdDate) {
|
||||
this.createdDate = createdDate;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -16,6 +17,7 @@ import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
|||
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
|
||||
import org.keycloak.models.mongo.utils.MongoModelUtils;
|
||||
import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -239,7 +241,6 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
|
|||
private CredentialEntity setCredentials(MongoUserEntity user, UserCredentialModel cred) {
|
||||
CredentialEntity credentialEntity = new CredentialEntity();
|
||||
credentialEntity.setType(cred.getType());
|
||||
credentialEntity.setCreatedDate(new Date().getTime());
|
||||
credentialEntity.setDevice(cred.getDevice());
|
||||
return credentialEntity;
|
||||
}
|
||||
|
@ -253,6 +254,7 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
|
|||
if (hashIterations == -1)
|
||||
hashIterations = 1;
|
||||
}
|
||||
credentialEntity.setCreatedDate(Time.toMillis(Time.currentTime()));
|
||||
credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
|
||||
credentialEntity.setSalt(salt);
|
||||
credentialEntity.setHashIterations(hashIterations);
|
||||
|
@ -419,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;
|
||||
|
|
|
@ -107,7 +107,7 @@ public class OIDCLoginProtocolService {
|
|||
public Object auth() {
|
||||
AuthorizationEndpoint endpoint = new AuthorizationEndpoint(authManager, realm, event);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(endpoint);
|
||||
return endpoint.init();
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -117,7 +117,7 @@ public class OIDCLoginProtocolService {
|
|||
public Object registerPage() {
|
||||
AuthorizationEndpoint endpoint = new AuthorizationEndpoint(authManager, realm, event);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(endpoint);
|
||||
return endpoint.init().register();
|
||||
return endpoint.register();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,7 +127,7 @@ public class OIDCLoginProtocolService {
|
|||
public Object token() {
|
||||
TokenEndpoint endpoint = new TokenEndpoint(tokenManager, authManager, realm, event);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(endpoint);
|
||||
return endpoint.init();
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
@Path("login")
|
||||
|
@ -135,7 +135,7 @@ public class OIDCLoginProtocolService {
|
|||
public Object loginPage() {
|
||||
AuthorizationEndpoint endpoint = new AuthorizationEndpoint(authManager, realm, event);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(endpoint);
|
||||
return endpoint.legacy(OIDCLoginProtocol.CODE_PARAM).init();
|
||||
return endpoint.legacy(OIDCLoginProtocol.CODE_PARAM);
|
||||
}
|
||||
|
||||
@Path("login-status-iframe.html")
|
||||
|
@ -150,7 +150,7 @@ public class OIDCLoginProtocolService {
|
|||
public Object grantAccessToken() {
|
||||
TokenEndpoint endpoint = new TokenEndpoint(tokenManager, authManager, realm, event);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(endpoint);
|
||||
return endpoint.legacy(OAuth2Constants.PASSWORD).init();
|
||||
return endpoint.legacy(OAuth2Constants.PASSWORD);
|
||||
}
|
||||
|
||||
@Path("refresh")
|
||||
|
@ -158,7 +158,7 @@ public class OIDCLoginProtocolService {
|
|||
public Object refreshAccessToken() {
|
||||
TokenEndpoint endpoint = new TokenEndpoint(tokenManager, authManager, realm, event);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(endpoint);
|
||||
return endpoint.legacy(OAuth2Constants.REFRESH_TOKEN).init();
|
||||
return endpoint.legacy(OAuth2Constants.REFRESH_TOKEN);
|
||||
}
|
||||
|
||||
@Path("access/codes")
|
||||
|
@ -166,7 +166,7 @@ public class OIDCLoginProtocolService {
|
|||
public Object accessCodeToToken() {
|
||||
TokenEndpoint endpoint = new TokenEndpoint(tokenManager, authManager, realm, event);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(endpoint);
|
||||
return endpoint.legacy(OAuth2Constants.AUTHORIZATION_CODE).init();
|
||||
return endpoint.legacy(OAuth2Constants.AUTHORIZATION_CODE);
|
||||
}
|
||||
|
||||
@Path("validate")
|
||||
|
|
|
@ -91,6 +91,25 @@ public class AuthorizationEndpoint {
|
|||
|
||||
@GET
|
||||
public Response build() {
|
||||
MultivaluedMap<String, String> params = uriInfo.getQueryParameters();
|
||||
|
||||
clientId = params.getFirst(OIDCLoginProtocol.CLIENT_ID_PARAM);
|
||||
responseType = params.getFirst(OIDCLoginProtocol.RESPONSE_TYPE_PARAM);
|
||||
redirectUriParam = params.getFirst(OIDCLoginProtocol.REDIRECT_URI_PARAM);
|
||||
state = params.getFirst(OIDCLoginProtocol.STATE_PARAM);
|
||||
scope = params.getFirst(OIDCLoginProtocol.SCOPE_PARAM);
|
||||
loginHint = params.getFirst(OIDCLoginProtocol.LOGIN_HINT_PARAM);
|
||||
prompt = params.getFirst(OIDCLoginProtocol.PROMPT_PARAM);
|
||||
idpHint = params.getFirst(AdapterConstants.KC_IDP_HINT);
|
||||
|
||||
checkSsl();
|
||||
checkRealm();
|
||||
checkClient();
|
||||
checkResponseType();
|
||||
checkRedirectUri();
|
||||
|
||||
createClientSession();
|
||||
|
||||
switch (action) {
|
||||
case REGISTER:
|
||||
return buildRegister();
|
||||
|
@ -121,29 +140,6 @@ public class AuthorizationEndpoint {
|
|||
return this;
|
||||
}
|
||||
|
||||
public AuthorizationEndpoint init() {
|
||||
MultivaluedMap<String, String> params = uriInfo.getQueryParameters();
|
||||
|
||||
clientId = params.getFirst(OIDCLoginProtocol.CLIENT_ID_PARAM);
|
||||
responseType = params.getFirst(OIDCLoginProtocol.RESPONSE_TYPE_PARAM);
|
||||
redirectUriParam = params.getFirst(OIDCLoginProtocol.REDIRECT_URI_PARAM);
|
||||
state = params.getFirst(OIDCLoginProtocol.STATE_PARAM);
|
||||
scope = params.getFirst(OIDCLoginProtocol.SCOPE_PARAM);
|
||||
loginHint = params.getFirst(OIDCLoginProtocol.LOGIN_HINT_PARAM);
|
||||
prompt = params.getFirst(OIDCLoginProtocol.PROMPT_PARAM);
|
||||
idpHint = params.getFirst(AdapterConstants.KC_IDP_HINT);
|
||||
|
||||
checkSsl();
|
||||
checkRealm();
|
||||
checkClient();
|
||||
checkResponseType();
|
||||
checkRedirectUri();
|
||||
|
||||
createClientSession();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private void checkSsl() {
|
||||
if (!uriInfo.getBaseUri().getScheme().equals("https") && realm.getSslRequired().isRequired(clientConnection)) {
|
||||
event.error(Errors.SSL_REQUIRED);
|
||||
|
@ -172,7 +168,7 @@ public class AuthorizationEndpoint {
|
|||
throw new ErrorPageException(session, Messages.CLIENT_NOT_FOUND );
|
||||
}
|
||||
|
||||
if ((client instanceof ClientModel) && ((ClientModel) client).isBearerOnly()) {
|
||||
if (client.isBearerOnly()) {
|
||||
event.error(Errors.NOT_ALLOWED);
|
||||
throw new ErrorPageException(session, Messages.BEARER_ONLY );
|
||||
}
|
||||
|
@ -198,7 +194,9 @@ public class AuthorizationEndpoint {
|
|||
event.detail(Details.RESPONSE_TYPE, responseType);
|
||||
|
||||
if (responseType.equals(OAuth2Constants.CODE)) {
|
||||
action = Action.CODE;
|
||||
if (action == null) {
|
||||
action = Action.CODE;
|
||||
}
|
||||
} else {
|
||||
event.error(Errors.INVALID_REQUEST);
|
||||
throw new ErrorPageException(session, Messages.INVALID_PARAMETER, OIDCLoginProtocol.RESPONSE_TYPE_PARAM );
|
||||
|
|
|
@ -87,6 +87,14 @@ public class TokenEndpoint {
|
|||
|
||||
@POST
|
||||
public Response build() {
|
||||
formParams = request.getDecodedFormParameters();
|
||||
grantType = formParams.getFirst(OIDCLoginProtocol.GRANT_TYPE_PARAM);
|
||||
|
||||
checkSsl();
|
||||
checkRealm();
|
||||
checkGrantType();
|
||||
checkClient();
|
||||
|
||||
switch (action) {
|
||||
case AUTHORIZATION_CODE:
|
||||
return buildAuthorizationCodeAccessTokenResponse();
|
||||
|
@ -116,18 +124,6 @@ public class TokenEndpoint {
|
|||
return this;
|
||||
}
|
||||
|
||||
public TokenEndpoint init() {
|
||||
formParams = request.getDecodedFormParameters();
|
||||
grantType = formParams.getFirst(OIDCLoginProtocol.GRANT_TYPE_PARAM);
|
||||
|
||||
checkSsl();
|
||||
checkRealm();
|
||||
checkGrantType();
|
||||
checkClient();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private void checkSsl() {
|
||||
if (!uriInfo.getBaseUri().getScheme().equals("https") && realm.getSslRequired().isRequired(clientConnection)) {
|
||||
throw new ErrorResponseException("invalid_request", "HTTPS required", Response.Status.FORBIDDEN);
|
||||
|
|
|
@ -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,11 +14,14 @@ 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;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserCredentialValueModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserModel.RequiredAction;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
|
@ -41,12 +44,14 @@ import javax.ws.rs.core.MultivaluedMap;
|
|||
import javax.ws.rs.core.NewCookie;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Stateless object that manages authentication
|
||||
|
@ -375,6 +380,7 @@ public class AuthenticationManager {
|
|||
HttpRequest request, UriInfo uriInfo, EventBuilder event) {
|
||||
RealmModel realm = clientSession.getRealm();
|
||||
UserModel user = userSession.getUser();
|
||||
isForcePasswordUpdateRequired(realm, user);
|
||||
isTotpConfigurationRequired(realm, user);
|
||||
isEmailVerificationRequired(realm, user);
|
||||
ClientModel client = clientSession.getClient();
|
||||
|
@ -414,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 {
|
||||
|
@ -424,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();
|
||||
|
@ -435,6 +461,30 @@ public class AuthenticationManager {
|
|||
|
||||
}
|
||||
|
||||
private static void isForcePasswordUpdateRequired(RealmModel realm, UserModel user) {
|
||||
int daysToExpirePassword = realm.getPasswordPolicy().getDaysToExpirePassword();
|
||||
if(daysToExpirePassword != -1) {
|
||||
for (UserCredentialValueModel entity : user.getCredentialsDirectly()) {
|
||||
if (entity.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||
|
||||
if(entity.getCreatedDate() == null) {
|
||||
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
|
||||
logger.debug("User is required to update password");
|
||||
} else {
|
||||
long timeElapsed = Time.toMillis(Time.currentTime()) - entity.getCreatedDate();
|
||||
long timeToExpire = TimeUnit.DAYS.toMillis(daysToExpirePassword);
|
||||
|
||||
if(timeElapsed > timeToExpire) {
|
||||
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
|
||||
logger.debug("User is required to update password");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void isTotpConfigurationRequired(RealmModel realm, UserModel user) {
|
||||
for (RequiredCredentialModel c : realm.getRequiredCredentials()) {
|
||||
if (c.getType().equals(CredentialRepresentation.TOTP) && !user.isTotp()) {
|
||||
|
|
|
@ -143,13 +143,13 @@ public class ClientManager {
|
|||
|
||||
public InstallationAdapterConfig toInstallationRepresentation(RealmModel realmModel, ClientModel clientModel, URI baseUri) {
|
||||
InstallationAdapterConfig rep = new InstallationAdapterConfig();
|
||||
rep.setAuthServerUrl(baseUri.toString());
|
||||
rep.setRealm(realmModel.getName());
|
||||
rep.setRealmKey(realmModel.getPublicKeyPem());
|
||||
rep.setSslRequired(realmModel.getSslRequired().name().toLowerCase());
|
||||
|
||||
if (clientModel.isPublicClient() && !clientModel.isBearerOnly()) rep.setPublicClient(true);
|
||||
if (clientModel.isBearerOnly()) rep.setBearerOnly(true);
|
||||
if (!clientModel.isBearerOnly()) rep.setAuthServerUrl(baseUri.toString());
|
||||
if (clientModel.getRoles().size() > 0) rep.setUseResourceRoleMappings(true);
|
||||
|
||||
rep.setResource(clientModel.getClientId());
|
||||
|
@ -169,14 +169,12 @@ public class ClientManager {
|
|||
buffer.append("<secure-deployment name=\"WAR MODULE NAME.war\">\n");
|
||||
buffer.append(" <realm>").append(realmModel.getName()).append("</realm>\n");
|
||||
buffer.append(" <realm-public-key>").append(realmModel.getPublicKeyPem()).append("</realm-public-key>\n");
|
||||
buffer.append(" <auth-server-url>").append(baseUri.toString()).append("</auth-server-url>\n");
|
||||
if (clientModel.isBearerOnly()){
|
||||
buffer.append(" <bearer-only>true</bearer-only>\n");
|
||||
|
||||
} else {
|
||||
buffer.append(" <auth-server-url>").append(baseUri.toString()).append("</auth-server-url>\n");
|
||||
if (clientModel.isPublicClient() && !clientModel.isBearerOnly()) {
|
||||
buffer.append(" <public-client>true</public-client>\n");
|
||||
}
|
||||
} else if (clientModel.isPublicClient()) {
|
||||
buffer.append(" <public-client>true</public-client>\n");
|
||||
}
|
||||
buffer.append(" <ssl-required>").append(realmModel.getSslRequired().name()).append("</ssl-required>\n");
|
||||
buffer.append(" <resource>").append(clientModel.getClientId()).append("</resource>\n");
|
||||
|
|
|
@ -134,8 +134,6 @@ public class Messages {
|
|||
|
||||
public static final String SESSION_NOT_ACTIVE = "sessionNotActiveMessage";
|
||||
|
||||
public static final String UNKNOWN_CODE = "unknownCodeMessage";
|
||||
|
||||
public static final String INVALID_CODE = "invalidCodeMessage";
|
||||
|
||||
public static final String IDENTITY_PROVIDER_UNEXPECTED_ERROR = "identityProviderUnexpectedErrorMessage";
|
||||
|
@ -154,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;
|
||||
|
@ -195,7 +197,7 @@ public class LoginActionsService {
|
|||
clientCode = ClientSessionCode.parse(code, session, realm);
|
||||
if (clientCode == null) {
|
||||
event.error(Errors.INVALID_CODE);
|
||||
response = ErrorPage.error(session, Messages.UNKNOWN_CODE);
|
||||
response = ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
return false;
|
||||
}
|
||||
session.getContext().setClient(clientCode.getClientSession().getClient());
|
||||
|
@ -288,7 +290,7 @@ public class LoginActionsService {
|
|||
ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm);
|
||||
if (clientCode == null) {
|
||||
event.error(Errors.INVALID_CODE);
|
||||
return ErrorPage.error(session, Messages.UNKNOWN_CODE);
|
||||
return ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
}
|
||||
|
||||
ClientSessionModel clientSession = clientCode.getClientSession();
|
||||
|
@ -428,7 +430,7 @@ public class LoginActionsService {
|
|||
ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm);
|
||||
if (clientCode == null) {
|
||||
event.error(Errors.INVALID_CODE);
|
||||
return ErrorPage.error(session, Messages.UNKNOWN_CODE);
|
||||
return ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
}
|
||||
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE)) {
|
||||
event.error(Errors.INVALID_CODE);
|
||||
|
@ -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);
|
||||
|
@ -865,7 +882,7 @@ public class LoginActionsService {
|
|||
ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm);
|
||||
if (accessCode == null) {
|
||||
event.error(Errors.INVALID_CODE);
|
||||
return ErrorPage.error(session, Messages.UNKNOWN_CODE);
|
||||
return ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
}
|
||||
ClientSessionModel clientSession = accessCode.getClientSession();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -28,7 +28,9 @@ import org.junit.Test;
|
|||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.BrowserSecurityHeaders;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -39,6 +41,7 @@ import org.keycloak.testsuite.OAuthClient;
|
|||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
|
@ -48,8 +51,10 @@ import org.openqa.selenium.WebDriver;
|
|||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
@ -94,6 +99,9 @@ public class LoginTest {
|
|||
@WebResource
|
||||
protected LoginPage loginPage;
|
||||
|
||||
@WebResource
|
||||
protected LoginPasswordUpdatePage updatePasswordPage;
|
||||
|
||||
private static String userId;
|
||||
|
||||
@Test
|
||||
|
@ -220,6 +228,85 @@ public class LoginTest {
|
|||
events.expectLogin().user(userId).removeDetail(Details.USERNAME).detail(Details.AUTH_METHOD, "sso").assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithForcePasswordChangePolicy() {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setPasswordPolicy(new PasswordPolicy("forceExpiredPasswordChange(1)"));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// Setting offset to more than one day to force password update
|
||||
// elapsedTime > timeToExpire
|
||||
Time.setOffset(86405);
|
||||
|
||||
loginPage.open();
|
||||
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
updatePasswordPage.assertCurrent();
|
||||
|
||||
updatePasswordPage.changePassword("updatedPassword", "updatedPassword");
|
||||
|
||||
events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
|
||||
assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
|
||||
} finally {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setPasswordPolicy(new PasswordPolicy(null));
|
||||
|
||||
UserModel user = manager.getSession().users().getUserByUsername("login-test", appRealm);
|
||||
UserCredentialModel cred = new UserCredentialModel();
|
||||
cred.setType(CredentialRepresentation.PASSWORD);
|
||||
cred.setValue("password");
|
||||
user.updateCredential(cred);
|
||||
}
|
||||
});
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithoutForcePasswordChangePolicy() {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setPasswordPolicy(new PasswordPolicy("forceExpiredPasswordChange(1)"));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// Setting offset to less than one day to avoid forced password update
|
||||
// elapsedTime < timeToExpire
|
||||
Time.setOffset(86205);
|
||||
|
||||
loginPage.open();
|
||||
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
|
||||
} finally {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setPasswordPolicy(new PasswordPolicy(null));
|
||||
}
|
||||
});
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginNoTimeoutWithLongWait() {
|
||||
try {
|
||||
|
|
|
@ -155,7 +155,7 @@ public class ResetPasswordTest {
|
|||
events.expect(EventType.RESET_PASSWORD_ERROR).client((String) null).user((String) null).error("invalid_code").clearDetails().assertEvent();
|
||||
|
||||
assertTrue(errorPage.isCurrent());
|
||||
assertEquals("Unknown code, please login again through your application.", errorPage.getError());
|
||||
assertEquals("An error occurred, please login again through your application.", errorPage.getError());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -354,7 +354,7 @@ public class ResetPasswordTest {
|
|||
|
||||
errorPage.assertCurrent();
|
||||
|
||||
assertEquals("Invalid code, please login again through your application.", errorPage.getError());
|
||||
assertEquals("An error occurred, please login again through your application.", errorPage.getError());
|
||||
|
||||
events.expectRequiredAction(EventType.RESET_PASSWORD).error("invalid_code").client((String) null).user((String) null).session((String) null).clearDetails().assertEvent();
|
||||
} finally {
|
||||
|
@ -538,19 +538,35 @@ public class ResetPasswordTest {
|
|||
}
|
||||
});
|
||||
|
||||
resetPassword("login-test", "password1");
|
||||
resetPasswordInvalidPassword("login-test", "password1", "Invalid password: must not be equal to any of last 3 passwords.");
|
||||
try {
|
||||
Time.setOffset(2000000);
|
||||
resetPassword("login-test", "password1");
|
||||
|
||||
resetPassword("login-test", "password2");
|
||||
resetPasswordInvalidPassword("login-test", "password1", "Invalid password: must not be equal to any of last 3 passwords.");
|
||||
resetPasswordInvalidPassword("login-test", "password2", "Invalid password: must not be equal to any of last 3 passwords.");
|
||||
resetPasswordInvalidPassword("login-test", "password1", "Invalid password: must not be equal to any of last 3 passwords.");
|
||||
|
||||
resetPassword("login-test", "password3");
|
||||
resetPasswordInvalidPassword("login-test", "password1", "Invalid password: must not be equal to any of last 3 passwords.");
|
||||
resetPasswordInvalidPassword("login-test", "password2", "Invalid password: must not be equal to any of last 3 passwords.");
|
||||
resetPasswordInvalidPassword("login-test", "password3", "Invalid password: must not be equal to any of last 3 passwords.");
|
||||
Time.setOffset(4000000);
|
||||
resetPassword("login-test", "password2");
|
||||
|
||||
resetPassword("login-test", "password");
|
||||
resetPasswordInvalidPassword("login-test", "password1", "Invalid password: must not be equal to any of last 3 passwords.");
|
||||
resetPasswordInvalidPassword("login-test", "password2", "Invalid password: must not be equal to any of last 3 passwords.");
|
||||
|
||||
Time.setOffset(8000000);
|
||||
resetPassword("login-test", "password3");
|
||||
|
||||
resetPasswordInvalidPassword("login-test", "password1", "Invalid password: must not be equal to any of last 3 passwords.");
|
||||
resetPasswordInvalidPassword("login-test", "password2", "Invalid password: must not be equal to any of last 3 passwords.");
|
||||
resetPasswordInvalidPassword("login-test", "password3", "Invalid password: must not be equal to any of last 3 passwords.");
|
||||
|
||||
resetPassword("login-test", "password");
|
||||
} finally {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setPasswordPolicy(new PasswordPolicy(null));
|
||||
}
|
||||
});
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -77,7 +77,7 @@ public class AccountPageTest {
|
|||
accountUpdateProfilePage.openLanguage("German");
|
||||
Assert.assertEquals("Deutsch", accountUpdateProfilePage.getLanguageDropdownText());
|
||||
|
||||
accountUpdateProfilePage.openLanguage("Englisch");
|
||||
accountUpdateProfilePage.openLanguage("English");
|
||||
Assert.assertEquals("English", accountUpdateProfilePage.getLanguageDropdownText());
|
||||
accountUpdateProfilePage.logout();
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ public class LoginPageTest {
|
|||
loginPage.openLanguage("German");
|
||||
Assert.assertEquals("Deutsch", loginPage.getLanguageDropdownText());
|
||||
|
||||
loginPage.openLanguage("Englisch");
|
||||
loginPage.openLanguage("English");
|
||||
Assert.assertEquals("English", loginPage.getLanguageDropdownText());
|
||||
}
|
||||
|
||||
|
|
|
@ -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