resolve conflicts
This commit is contained in:
commit
33ac048c8c
156 changed files with 5143 additions and 1262 deletions
|
@ -92,4 +92,9 @@ public abstract class AbstractIdentityProvider<C extends IdentityProviderModel>
|
|||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityProviderDataMarshaller getMarshaller() {
|
||||
return new DefaultDataMarshaller();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package org.keycloak.broker.provider;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.keycloak.common.util.Base64Url;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class DefaultDataMarshaller implements IdentityProviderDataMarshaller {
|
||||
|
||||
@Override
|
||||
public String serialize(Object value) {
|
||||
if (value instanceof String) {
|
||||
return (String) value;
|
||||
} else {
|
||||
try {
|
||||
byte[] bytes = JsonSerialization.writeValueAsBytes(value);
|
||||
return Base64Url.encode(bytes);
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T deserialize(String serialized, Class<T> clazz) {
|
||||
if (clazz.equals(String.class)) {
|
||||
return clazz.cast(serialized);
|
||||
} else {
|
||||
byte[] bytes = Base64Url.decode(serialized);
|
||||
try {
|
||||
return JsonSerialization.readValue(bytes, clazz);
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -103,4 +103,10 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
|
|||
*/
|
||||
Response export(UriInfo uriInfo, RealmModel realm, String format);
|
||||
|
||||
/**
|
||||
* Implementation of marshaller to serialize/deserialize attached data to Strings, which can be saved in clientSession
|
||||
* @return
|
||||
*/
|
||||
IdentityProviderDataMarshaller getMarshaller();
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package org.keycloak.broker.provider;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface IdentityProviderDataMarshaller {
|
||||
|
||||
String serialize(Object obj);
|
||||
<T> T deserialize(String serialized, Class<T> clazz);
|
||||
|
||||
}
|
|
@ -45,6 +45,11 @@
|
|||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
package org.keycloak.broker.saml;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.xml.stream.XMLEventReader;
|
||||
|
||||
import org.keycloak.broker.provider.DefaultDataMarshaller;
|
||||
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
|
||||
import org.keycloak.dom.saml.v2.protocol.ResponseType;
|
||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||
import org.keycloak.saml.common.exceptions.ProcessingException;
|
||||
import org.keycloak.saml.common.util.StaxUtil;
|
||||
import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
|
||||
import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil;
|
||||
import org.keycloak.saml.processing.core.saml.v2.writers.SAMLAssertionWriter;
|
||||
import org.keycloak.saml.processing.core.saml.v2.writers.SAMLResponseWriter;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class SAMLDataMarshaller extends DefaultDataMarshaller {
|
||||
|
||||
@Override
|
||||
public String serialize(Object obj) {
|
||||
|
||||
// Lame impl, but hopefully sufficient for now. See if something better is needed...
|
||||
if (obj.getClass().getName().startsWith("org.keycloak.dom.saml")) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
if (obj instanceof ResponseType) {
|
||||
ResponseType responseType = (ResponseType) obj;
|
||||
SAMLResponseWriter samlWriter = new SAMLResponseWriter(StaxUtil.getXMLStreamWriter(bos));
|
||||
samlWriter.write(responseType);
|
||||
} else if (obj instanceof AssertionType) {
|
||||
AssertionType assertion = (AssertionType) obj;
|
||||
SAMLAssertionWriter samlWriter = new SAMLAssertionWriter(StaxUtil.getXMLStreamWriter(bos));
|
||||
samlWriter.write(assertion);
|
||||
} else if (obj instanceof AuthnStatementType) {
|
||||
AuthnStatementType authnStatement = (AuthnStatementType) obj;
|
||||
SAMLAssertionWriter samlWriter = new SAMLAssertionWriter(StaxUtil.getXMLStreamWriter(bos));
|
||||
samlWriter.write(authnStatement, true);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don't know how to serialize object of type " + obj.getClass().getName());
|
||||
}
|
||||
} catch (ProcessingException pe) {
|
||||
throw new RuntimeException(pe);
|
||||
}
|
||||
|
||||
return new String(bos.toByteArray());
|
||||
} else {
|
||||
return super.serialize(obj);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T deserialize(String serialized, Class<T> clazz) {
|
||||
if (clazz.getName().startsWith("org.keycloak.dom.saml")) {
|
||||
String xmlString = serialized;
|
||||
|
||||
try {
|
||||
if (clazz.equals(ResponseType.class) || clazz.equals(AssertionType.class)) {
|
||||
byte[] bytes = xmlString.getBytes();
|
||||
InputStream is = new ByteArrayInputStream(bytes);
|
||||
Object respType = new SAMLParser().parse(is);
|
||||
return clazz.cast(respType);
|
||||
} else if (clazz.equals(AuthnStatementType.class)) {
|
||||
byte[] bytes = xmlString.getBytes();
|
||||
InputStream is = new ByteArrayInputStream(bytes);
|
||||
XMLEventReader xmlEventReader = new SAMLParser().createEventReader(is);
|
||||
AuthnStatementType authnStatement = SAMLParserUtil.parseAuthnStatement(xmlEventReader);
|
||||
return clazz.cast(authnStatement);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don't know how to deserialize object of type " + clazz.getName());
|
||||
}
|
||||
} catch (ParsingException pe) {
|
||||
throw new RuntimeException(pe);
|
||||
}
|
||||
|
||||
} else {
|
||||
return super.deserialize(serialized, clazz);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -22,6 +22,7 @@ import org.keycloak.broker.provider.AbstractIdentityProvider;
|
|||
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.broker.provider.IdentityProviderDataMarshaller;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
|
||||
|
@ -263,4 +264,8 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
|||
return SignatureAlgorithm.RSA_SHA256;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityProviderDataMarshaller getMarshaller() {
|
||||
return new SAMLDataMarshaller();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package org.keycloak.broker.saml;
|
||||
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
|
||||
import org.keycloak.dom.saml.v2.assertion.NameIDType;
|
||||
import org.keycloak.dom.saml.v2.protocol.ResponseType;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class SAMLDataMarshallerTest {
|
||||
|
||||
private static final String TEST_RESPONSE = "<samlp:Response xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"ID_4804cf50-cd96-4b92-823e-89adaa0c78ba\" Version=\"2.0\" IssueInstant=\"2015-11-06T11:00:33.920Z\" Destination=\"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint\" InResponseTo=\"ID_c6b90123-f0bb-4c5c-bf9d-388d5bbe467a\"><saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://localhost:8082/auth/realms/realm-with-saml-idp-basic</saml:Issuer><samlp:Status><samlp:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"></samlp:StatusCode></samlp:Status><saml:Assertion xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"ID_29b196c2-d641-45c8-a423-8ed8e54d4cf9\" Version=\"2.0\" IssueInstant=\"2015-11-06T11:00:33.911Z\"><saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://localhost:8082/auth/realms/realm-with-saml-idp-basic</saml:Issuer><saml:Subject><saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\">test-user</saml:NameID><saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><saml:SubjectConfirmationData InResponseTo=\"ID_c6b90123-f0bb-4c5c-bf9d-388d5bbe467a\" NotOnOrAfter=\"2015-11-06T11:05:31.911Z\" Recipient=\"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint\"></saml:SubjectConfirmationData></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore=\"2015-11-06T11:00:31.911Z\" NotOnOrAfter=\"2015-11-06T11:01:31.911Z\"><saml:AudienceRestriction><saml:Audience>http://localhost:8081/auth/realms/realm-with-broker</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant=\"2015-11-06T11:00:33.923Z\" SessionIndex=\"fa0f4fd3-8a11-44f4-9acb-ee30c5bb8fe5\"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement><saml:Attribute Name=\"mobile\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">617-666-7777</saml:AttributeValue></saml:Attribute><saml:Attribute Name=\"urn:oid:1.2.840.113549.1.9.1\" FriendlyName=\"email\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">test-user@localhost</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AttributeStatement><saml:Attribute Name=\"Role\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">manager</saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion></samlp:Response>";
|
||||
|
||||
private static final String TEST_ASSERTION = "<saml:Assertion xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"ID_29b196c2-d641-45c8-a423-8ed8e54d4cf9\" Version=\"2.0\" IssueInstant=\"2015-11-06T11:00:33.911Z\"><saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://localhost:8082/auth/realms/realm-with-saml-idp-basic</saml:Issuer><saml:Subject><saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\">test-user</saml:NameID><saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><saml:SubjectConfirmationData InResponseTo=\"ID_c6b90123-f0bb-4c5c-bf9d-388d5bbe467a\" NotOnOrAfter=\"2015-11-06T11:05:31.911Z\" Recipient=\"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint\"></saml:SubjectConfirmationData></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore=\"2015-11-06T11:00:31.911Z\" NotOnOrAfter=\"2015-11-06T11:01:31.911Z\"><saml:AudienceRestriction><saml:Audience>http://localhost:8081/auth/realms/realm-with-broker</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant=\"2015-11-06T11:00:33.923Z\" SessionIndex=\"fa0f4fd3-8a11-44f4-9acb-ee30c5bb8fe5\"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement><saml:Attribute Name=\"mobile\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">617-666-7777</saml:AttributeValue></saml:Attribute><saml:Attribute Name=\"urn:oid:1.2.840.113549.1.9.1\" FriendlyName=\"email\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">test-user@localhost</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AttributeStatement><saml:Attribute Name=\"Role\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">manager</saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion>";
|
||||
|
||||
private static final String TEST_AUTHN_TYPE = "<saml:AuthnStatement xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" AuthnInstant=\"2015-11-06T11:00:33.923Z\" SessionIndex=\"fa0f4fd3-8a11-44f4-9acb-ee30c5bb8fe5\"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement>";
|
||||
|
||||
@Test
|
||||
public void testParseResponse() throws Exception {
|
||||
SAMLDataMarshaller serializer = new SAMLDataMarshaller();
|
||||
ResponseType responseType = serializer.deserialize(TEST_RESPONSE, ResponseType.class);
|
||||
|
||||
// test ResponseType
|
||||
Assert.assertEquals(responseType.getID(), "ID_4804cf50-cd96-4b92-823e-89adaa0c78ba");
|
||||
Assert.assertEquals(responseType.getDestination(), "http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint");
|
||||
Assert.assertEquals(responseType.getIssuer().getValue(), "http://localhost:8082/auth/realms/realm-with-saml-idp-basic");
|
||||
Assert.assertEquals(responseType.getAssertions().get(0).getID(), "ID_29b196c2-d641-45c8-a423-8ed8e54d4cf9");
|
||||
|
||||
// back to String
|
||||
String serialized = serializer.serialize(responseType);
|
||||
Assert.assertEquals(TEST_RESPONSE, serialized);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseAssertion() throws Exception {
|
||||
SAMLDataMarshaller serializer = new SAMLDataMarshaller();
|
||||
AssertionType assertion = serializer.deserialize(TEST_ASSERTION, AssertionType.class);
|
||||
|
||||
// test assertion
|
||||
Assert.assertEquals(assertion.getID(), "ID_29b196c2-d641-45c8-a423-8ed8e54d4cf9");
|
||||
Assert.assertEquals(((NameIDType) assertion.getSubject().getSubType().getBaseID()).getValue(), "test-user");
|
||||
|
||||
// back to String
|
||||
String serialized = serializer.serialize(assertion);
|
||||
Assert.assertEquals(TEST_ASSERTION, serialized);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseAuthnType() throws Exception {
|
||||
SAMLDataMarshaller serializer = new SAMLDataMarshaller();
|
||||
AuthnStatementType authnStatement = serializer.deserialize(TEST_AUTHN_TYPE, AuthnStatementType.class);
|
||||
|
||||
// test authnStatement
|
||||
Assert.assertEquals(authnStatement.getSessionIndex(), "fa0f4fd3-8a11-44f4-9acb-ee30c5bb8fe5");
|
||||
|
||||
// back to String
|
||||
String serialized = serializer.serialize(authnStatement);
|
||||
Assert.assertEquals(TEST_AUTHN_TYPE, serialized);
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ public class ObjectUtil {
|
|||
* @param str2
|
||||
* @return true if both strings are null or equal
|
||||
*/
|
||||
public static boolean isEqualOrNull(Object str1, Object str2) {
|
||||
public static boolean isEqualOrBothNull(Object str1, Object str2) {
|
||||
if (str1 == null && str2 == null) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -37,6 +37,14 @@
|
|||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
|
||||
<addColumn tableName="IDENTITY_PROVIDER">
|
||||
<column name="FIRST_BROKER_LOGIN_FLOW_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<dropColumn tableName="IDENTITY_PROVIDER" columnName="UPDATE_PROFILE_FIRST_LGN_MD"/>
|
||||
|
||||
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_GROUP" tableName="KEYCLOAK_GROUP"/>
|
||||
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="KEYCLOAK_GROUP" constraintName="FK_GROUP_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
|
||||
|
||||
|
@ -51,6 +59,5 @@
|
|||
<addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="GROUP_ROLE_MAPPING" constraintName="FK_GROUP_ROLE_ROLE" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
|
||||
|
||||
|
||||
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
|
@ -46,12 +46,14 @@ public class IdentityProviderRepresentation {
|
|||
* @see #UPFLM_MISSING
|
||||
* @see #UPFLM_OFF
|
||||
*/
|
||||
@Deprecated
|
||||
protected String updateProfileFirstLoginMode = UPFLM_ON;
|
||||
|
||||
protected boolean trustEmail;
|
||||
protected boolean storeToken;
|
||||
protected boolean addReadTokenRoleOnCreate;
|
||||
protected boolean authenticateByDefault;
|
||||
protected String firstBrokerLoginFlowAlias;
|
||||
protected Map<String, String> config = new HashMap<String, String>();
|
||||
|
||||
public String getInternalId() {
|
||||
|
@ -106,15 +108,17 @@ public class IdentityProviderRepresentation {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return see {@link #updateProfileFirstLoginMode}
|
||||
* @deprecated deprecated and replaced by configuration on IdpReviewProfileAuthenticator
|
||||
*/
|
||||
@Deprecated
|
||||
public String getUpdateProfileFirstLoginMode() {
|
||||
return updateProfileFirstLoginMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param updateProfileFirstLoginMode see {@link #updateProfileFirstLoginMode}
|
||||
* @deprecated deprecated and replaced by configuration on IdpReviewProfileAuthenticator
|
||||
*/
|
||||
@Deprecated
|
||||
public void setUpdateProfileFirstLoginMode(String updateProfileFirstLoginMode) {
|
||||
this.updateProfileFirstLoginMode = updateProfileFirstLoginMode;
|
||||
}
|
||||
|
@ -127,6 +131,14 @@ public class IdentityProviderRepresentation {
|
|||
this.authenticateByDefault = authenticateByDefault;
|
||||
}
|
||||
|
||||
public String getFirstBrokerLoginFlowAlias() {
|
||||
return firstBrokerLoginFlowAlias;
|
||||
}
|
||||
|
||||
public void setFirstBrokerLoginFlowAlias(String firstBrokerLoginFlowAlias) {
|
||||
this.firstBrokerLoginFlowAlias = firstBrokerLoginFlowAlias;
|
||||
}
|
||||
|
||||
public boolean isStoreToken() {
|
||||
return this.storeToken;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>../../target/site/apidocs</directory>
|
||||
<directory>target/site/apidocs</directory>
|
||||
<outputDirectory>javadocs</outputDirectory>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
|
|
|
@ -1,11 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
td { padding: 5px; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Keyloak Documentation</h1>
|
||||
<ul>
|
||||
<li><a href="userguide/keycloak-server/html/index.html">Server and Keycloak Adapter Userguide HTML</a></li>
|
||||
<li><a href="userguide/keycloak-server/html_single/index.html">Server and Keycloak Adapter Userguide HTML Single Page</a></li>
|
||||
<li><a href="userguide/keycloak-server/pdf/keycloak-reference-guide-en-US.pdf">Server and Keycloak Adapter Userguide PDF</a></li>
|
||||
<li><a href="userguide/saml-client-adapter/html/index.html">SAML Client Adapter Userguide HTML</a></li>
|
||||
<li><a href="userguide/saml-client-adapter/html_single/index.html">>SAML Client Adapter Userguide HTML Single Page</a></li>
|
||||
<li><a href="userguide/saml-client-adapter/pdf/keycloak-reference-guide-en-US.pdf">SAML Client Adapter Userguide PDF</a></li>
|
||||
<li><a href="rest-api/overview-index.html">Admin REST API</a></li>
|
||||
<li><a href="javadocs/index.html">Javadocs</a></li>
|
||||
</ul>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Server and Keycloak Adapter Userguide</td>
|
||||
<td><a href="userguide/keycloak-server/html/index.html">HTML</a></td>
|
||||
<td><a href="userguide/keycloak-server/html_single/index.html">HTML Single Page</a></td>
|
||||
<td><a href="userguide/keycloak-server/pdf/keycloak-reference-guide-en-US.pdf">PDF</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SAML Client Adapter Userguide</td>
|
||||
<td><a href="userguide/saml-client-adapter/html/index.html">HTML</a></td>
|
||||
<td><a href="userguide/saml-client-adapter/html_single/index.html">HTML Single Page</a></td>
|
||||
<td><a href="userguide/saml-client-adapter/pdf/keycloak-saml-adapter-reference-guide-en-US.pdf">PDF</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Admin REST API</td>
|
||||
<td colspan="3"><a href="rest-api/index.html">HTML</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Javadocs</td>
|
||||
<td colspan="3"><a href="javadocs/index.html">HTML</a></td>
|
||||
</tr>
|
||||
</body>
|
||||
</html>
|
|
@ -66,4 +66,15 @@
|
|||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
|
||||
<files>
|
||||
<file>
|
||||
<source>src/main/resources/content/standalone/configuration/keycloak-server.json</source>
|
||||
<outputDirectory>content/domain/servers/server-one/configuration</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>src/main/resources/content/standalone/configuration/keycloak-server.json</source>
|
||||
<outputDirectory>content/domain/servers/server-two/configuration</outputDirectory>
|
||||
</file>
|
||||
</files>
|
||||
</assembly>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<subsystem>ee.xml</subsystem>
|
||||
<subsystem>ejb3.xml</subsystem>
|
||||
<subsystem>io.xml</subsystem>
|
||||
<subsystem>infinispan.xml</subsystem>
|
||||
<subsystem>keycloak-infinispan.xml</subsystem>
|
||||
<subsystem>jaxrs.xml</subsystem>
|
||||
<subsystem>jca.xml</subsystem>
|
||||
<subsystem>jdr.xml</subsystem>
|
||||
|
@ -41,7 +41,7 @@
|
|||
<subsystem>ee.xml</subsystem>
|
||||
<subsystem supplement="ha">ejb3.xml</subsystem>
|
||||
<subsystem>io.xml</subsystem>
|
||||
<subsystem supplement="ha">infinispan.xml</subsystem>
|
||||
<subsystem supplement="ha">keycloak-infinispan.xml</subsystem>
|
||||
<subsystem>jaxrs.xml</subsystem>
|
||||
<subsystem>jca.xml</subsystem>
|
||||
<subsystem>jdr.xml</subsystem>
|
||||
|
@ -74,7 +74,7 @@
|
|||
<subsystem supplement="full">ee.xml</subsystem>
|
||||
<subsystem supplement="full">ejb3.xml</subsystem>
|
||||
<subsystem>io.xml</subsystem>
|
||||
<subsystem>infinispan.xml</subsystem>
|
||||
<subsystem>keycloak-infinispan.xml</subsystem>
|
||||
<subsystem>iiop-openjdk.xml</subsystem>
|
||||
<subsystem>jaxrs.xml</subsystem>
|
||||
<subsystem>jca.xml</subsystem>
|
||||
|
@ -108,7 +108,7 @@
|
|||
<subsystem supplement="full">ee.xml</subsystem>
|
||||
<subsystem supplement="full-ha">ejb3.xml</subsystem>
|
||||
<subsystem>io.xml</subsystem>
|
||||
<subsystem supplement="ha">infinispan.xml</subsystem>
|
||||
<subsystem supplement="ha">keycloak-infinispan.xml</subsystem>
|
||||
<subsystem>iiop-openjdk.xml</subsystem>
|
||||
<subsystem>jaxrs.xml</subsystem>
|
||||
<subsystem>jca.xml</subsystem>
|
||||
|
|
|
@ -79,16 +79,34 @@
|
|||
|
||||
<section>
|
||||
<title>Version specific migration</title>
|
||||
<section>
|
||||
<title>Migrating to 1.7.0.CR1</title>
|
||||
<simplesect>
|
||||
<title>Option 'Update Profile On First Login' moved from Identity provider to Review Profile authenticator</title>
|
||||
<para>
|
||||
In this version, we added <literal>First Broker Login</literal>, which allows you to specify what exactly should be done
|
||||
when new user is logged through Identity provider (or Social provider), but there is no existing Keycloak user
|
||||
yet linked to the social account. As part of this work, we added option <literal>First Login Flow</literal> to identity providers where
|
||||
you can specify the flow and then you can configure this flow under <literal>Authentication</literal> tab in admin console.
|
||||
</para>
|
||||
<para>
|
||||
We also removed the option <literal>Update Profile On First Login</literal> from the Identity provider settings and moved it
|
||||
to the configuration of <literal>Review Profile</literal> authenticator. So once you specify which flow should be used for your
|
||||
Identity provider (by default it's <literal>First Broker Login</literal> flow), you go to <literal>Authentication</literal> tab, select the flow
|
||||
and then you configure the option under <literal>Review Profile</literal> authenticator.
|
||||
</para>
|
||||
</simplesect>
|
||||
</section>
|
||||
<section>
|
||||
<title>Migrating to 1.6.0.Final</title>
|
||||
<simplesect>
|
||||
<title>Refresh tokens are not reusable anymore</title>
|
||||
<title>Option that refresh tokens are not reusable anymore</title>
|
||||
<para>
|
||||
Old versions of Keycloak allowed reusing refresh tokens multiple times. Keycloak no longer permits
|
||||
this by default. When a refresh token is used to obtain a new access token a new refresh token is also
|
||||
included. This new refresh token should be used next time the access token is refreshed. If this is
|
||||
a problem for you it's possible to enable reuse of refresh tokens in the admin console under token
|
||||
settings.
|
||||
Old versions of Keycloak allowed reusing refresh tokens multiple times. Keycloak still permits this,
|
||||
but also have an option <literal>Revoke refresh token</literal> to disallow it. Option is in in admin console under token settings.
|
||||
When a refresh token is used to obtain a new access token a new refresh token is also
|
||||
included. When option is enabled, then this new refresh token should be used next time the access token is refreshed.
|
||||
It won't be possible to reuse old refresh token multiple times.
|
||||
</para>
|
||||
</simplesect>
|
||||
<simplesect>
|
||||
|
|
|
@ -53,4 +53,5 @@ public interface Errors {
|
|||
String EMAIL_SEND_FAILED = "email_send_failed";
|
||||
String INVALID_EMAIL = "invalid_email";
|
||||
String IDENTITY_PROVIDER_LOGIN_FAILURE = "identity_provider_login_failure";
|
||||
String IDENTITY_PROVIDER_ERROR = "identity_provider_error";
|
||||
}
|
||||
|
|
|
@ -60,6 +60,8 @@ public enum EventType {
|
|||
|
||||
IDENTITY_PROVIDER_LOGIN(false),
|
||||
IDENTITY_PROVIDER_LOGIN_ERROR(false),
|
||||
IDENTITY_PROVIDER_FIRST_LOGIN(true),
|
||||
IDENTITY_PROVIDER_FIRST_LOGIN_ERROR(true),
|
||||
IDENTITY_PROVIDER_RESPONSE(false),
|
||||
IDENTITY_PROVIDER_RESPONSE_ERROR(false),
|
||||
IDENTITY_PROVIDER_RETRIEVE_TOKEN(false),
|
||||
|
|
8
examples/js-console/src/main/webapp/WEB-INF/web.xml
Normal file
8
examples/js-console/src/main/webapp/WEB-INF/web.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||
version="3.0">
|
||||
|
||||
<module-name>js-console</module-name>
|
||||
</web-app>
|
|
@ -17,6 +17,7 @@ import javax.security.auth.login.LoginException;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.federation.kerberos.CommonKerberosConfig;
|
||||
import org.keycloak.models.ModelException;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
@ -54,6 +55,8 @@ public class KerberosUsernamePasswordAuthenticator {
|
|||
String message = le.getMessage();
|
||||
logger.debug("Message from kerberos: " + message);
|
||||
|
||||
checkKerberosServerAvailable(le);
|
||||
|
||||
// Bit cumbersome, but seems to work with tested kerberos servers
|
||||
boolean exists = (!message.contains("Client not found"));
|
||||
return exists;
|
||||
|
@ -74,11 +77,19 @@ public class KerberosUsernamePasswordAuthenticator {
|
|||
logoutSubject();
|
||||
return true;
|
||||
} catch (LoginException le) {
|
||||
checkKerberosServerAvailable(le);
|
||||
|
||||
logger.debug("Failed to authenticate user " + username, le);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkKerberosServerAvailable(LoginException le) {
|
||||
if (le.getMessage().contains("Port Unreachable")) {
|
||||
throw new ModelException("Kerberos unreachable", le);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if user was successfully authenticated against Kerberos
|
||||
|
|
|
@ -126,4 +126,4 @@ locale_de=Deutsch
|
|||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
locale_fr=Français
|
||||
locale_fr=Fran\u00e7ais
|
|
@ -150,4 +150,4 @@ locale_de=German
|
|||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
locale_fr=Français
|
||||
locale_fr=Fran\u00e7ais
|
|
@ -0,0 +1,154 @@
|
|||
doSave=Guardar
|
||||
doCancel=Cancelar
|
||||
doLogOutAllSessions=Desconectar de todas las sesiones
|
||||
doRemove=Eliminar
|
||||
doAdd=A\u00F1adir
|
||||
doSignOut=Desconectar
|
||||
|
||||
editAccountHtmlTtile=Editar cuenta
|
||||
federatedIdentitiesHtmlTitle=Identidades federadas
|
||||
accountLogHtmlTitle=Registro de la cuenta
|
||||
changePasswordHtmlTitle=Cambiar contrase\u00F1a
|
||||
sessionsHtmlTitle=Sesiones
|
||||
accountManagementTitle=Gesti\u00F3n de Cuenta Keycloak
|
||||
authenticatorTitle=Autenticador
|
||||
applicationsHtmlTitle=Aplicaciones
|
||||
|
||||
authenticatorCode=C\u00F3digo de un solo uso
|
||||
email=Email
|
||||
firstName=Nombre
|
||||
givenName=Nombre de pila
|
||||
fullName=Nombre completo
|
||||
lastName=Apellidos
|
||||
familyName=Apellido
|
||||
password=Contrase\u00F1a
|
||||
passwordConfirm=Confirma la contrase\u00F1a
|
||||
passwordNew=Nueva contrase\u00F1a
|
||||
username=Usuario
|
||||
address=Direcci\u00F3n
|
||||
street=Calle
|
||||
locality=Ciudad o Municipio
|
||||
region=Estado, Provincia, o Regi\u00F3n
|
||||
postal_code=C\u00F3digo Postal
|
||||
country=Pa\u00EDs
|
||||
emailVerified=Email verificado
|
||||
gssDelegationCredential=GSS Delegation Credential
|
||||
|
||||
role_admin=Administrador
|
||||
role_realm-admin=Administrador del dominio
|
||||
role_create-realm=Crear dominio
|
||||
role_view-realm=Ver dominio
|
||||
role_view-users=Ver usuarios
|
||||
role_view-applications=Ver aplicaciones
|
||||
role_view-clients=Ver clientes
|
||||
role_view-events=Ver eventos
|
||||
role_view-identity-providers=Ver proveedores de identidad
|
||||
role_manage-realm=Gestionar dominio
|
||||
role_manage-users=Gestionar usuarios
|
||||
role_manage-applications=Gestionar aplicaciones
|
||||
role_manage-identity-providers=Gestionar proveedores de identidad
|
||||
role_manage-clients=Gestionar clientes
|
||||
role_manage-events=Gestionar eventos
|
||||
role_view-profile=Ver perfil
|
||||
role_manage-account=Gestionar cuenta
|
||||
role_read-token=Leer token
|
||||
role_offline-access=Acceso sin conexi\u00F3n
|
||||
client_account=Cuenta
|
||||
client_security-admin-console=Consola de Administraci\u00F3n de Seguridad
|
||||
client_realm-management=Gesti\u00F3n de dominio
|
||||
client_broker=Broker
|
||||
|
||||
|
||||
requiredFields=Campos obligatorios
|
||||
allFieldsRequired=Todos los campos obligatorios
|
||||
|
||||
backToApplication=« Volver a la aplicaci\u00F3n
|
||||
backTo=Volver a {0}
|
||||
|
||||
date=Fecha
|
||||
event=Evento
|
||||
ip=IP
|
||||
client=Cliente
|
||||
clients=Clientes
|
||||
details=Detalles
|
||||
started=Iniciado
|
||||
lastAccess=\u00DAltimo acceso
|
||||
expires=Expira
|
||||
applications=Aplicaciones
|
||||
|
||||
account=Cuenta
|
||||
federatedIdentity=Identidad federada
|
||||
authenticator=Autenticador
|
||||
sessions=Sesiones
|
||||
log=Regisro
|
||||
|
||||
application=Aplicaci\u00F3n
|
||||
availablePermissions=Permisos disponibles
|
||||
grantedPermissions=Permisos concedidos
|
||||
grantedPersonalInfo=Informaci\u00F3n personal concedida
|
||||
additionalGrants=Permisos adicionales
|
||||
action=Acci\u00F3n
|
||||
inResource=en
|
||||
fullAccess=Acceso total
|
||||
offlineToken=C\u00F3digo de autorizaci\u00F3n offline
|
||||
revoke=Revocar permiso
|
||||
|
||||
configureAuthenticators=Autenticadores configurados
|
||||
mobile=M\u00F3vil
|
||||
totpStep1=Instala <a href=\"https://fedorahosted.org/freeotp/\" target=\"_blank\">FreeOTP</a> o Google Authenticator en tu tel\u00E9fono m\u00F3vil. Ambas aplicaciones est\u00E1n disponibles en <a href=\"https://play.google.com\">Google Play</a> y en la App Store de Apple.
|
||||
totpStep2=Abre la aplicacvi\u00F3n y escanea el c\u00F3digo o introduce la clave.
|
||||
totpStep3=Introduce el c\u00F3digo \u00FAnico que te muestra la aplicaci\u00F3n de autenticaci\u00F3n y haz clic en Enviar para finalizar la configuraci\u00F3n
|
||||
|
||||
missingUsernameMessage=Por favor indica tu usuario.
|
||||
missingFirstNameMessage=Por favor indica el nombre.
|
||||
invalidEmailMessage=Email no v\u00E1lido
|
||||
missingLastNameMessage=Por favor indica tus apellidos.
|
||||
missingEmailMessage=Por favor indica el email.
|
||||
missingPasswordMessage=Por favor indica tu contrase\u00F1a.
|
||||
notMatchPasswordMessage=Las contrase\u00F1as no coinciden.
|
||||
|
||||
missingTotpMessage=Por favor indica tu c\u00F3digo de autenticaci\u00F3n
|
||||
invalidPasswordExistingMessage=La contrase\u00F1a actual no es correcta.
|
||||
invalidPasswordConfirmMessage=La confirmaci\u00F3n de contrase\u00F1a no coincide.
|
||||
invalidTotpMessage=El c\u00F3digo de autenticaci\u00F3n no es v\u00E1lido.
|
||||
|
||||
usernameExistsMessage=El usuario ya existe
|
||||
emailExistsMessage=El email ya existe
|
||||
|
||||
readOnlyUserMessage=No puedes actualizar tu usuario porque tu cuenta es de solo lectura.
|
||||
readOnlyPasswordMessage=No puedes actualizar tu contrase\u00F1a porque tu cuenta es de solo lectura.
|
||||
|
||||
successTotpMessage=Aplicaci\u00F3n de autenticaci\u00F3n m\u00F3vil configurada.
|
||||
successTotpRemovedMessage=Aplicaci\u00F3n de autenticaci\u00F3n m\u00F3vil eliminada.
|
||||
|
||||
successGrantRevokedMessage=Permiso revocado correctamente
|
||||
|
||||
accountUpdatedMessage=Tu cuenta se ha actualizado.
|
||||
accountPasswordUpdatedMessage=Tu contrase\u00F1a se ha actualizado.
|
||||
|
||||
missingIdentityProviderMessage=Proveedor de identidad no indicado.
|
||||
invalidFederatedIdentityActionMessage=Acci\u00F3n no v\u00E1lida o no indicada.
|
||||
identityProviderNotFoundMessage=No se encontr\u00F3 un proveedor de identidad.
|
||||
federatedIdentityLinkNotActiveMessage=Esta identidad ya no est\u00E1 activa
|
||||
federatedIdentityRemovingLastProviderMessage=No puedes eliminar la \u00FAltima identidad federada porque no tienes fijada una contrase\u00F1a.
|
||||
identityProviderRedirectErrorMessage=Error en la redirecci\u00F3n al proveedor de identidad
|
||||
identityProviderRemovedMessage=Proveedor de identidad borrado correctamente.
|
||||
|
||||
accountDisabledMessage=La cuenta est\u00E1 desactivada, contacta con el administrador.
|
||||
|
||||
accountTemporarilyDisabledMessage=La cuenta est\u00E1 temporalmente desactivada, contacta con el administrador o int\u00E9ntalo de nuevo m\u00E1s tarde.
|
||||
invalidPasswordMinLengthMessage=Contrase\u00F1a incorrecta: longitud m\u00EDnima {0}.
|
||||
invalidPasswordMinLowerCaseCharsMessage=Contrase\u00F1a incorrecta: debe contener al menos {0} letras min\u00FAsculas.
|
||||
invalidPasswordMinDigitsMessage=Contrase\u00F1a incorrecta: debe contener al menos {0} caracteres num\u00E9ricos.
|
||||
invalidPasswordMinUpperCaseCharsMessage=Contrase\u00F1a incorrecta: debe contener al menos {0} letras may\u00FAsculas.
|
||||
invalidPasswordMinSpecialCharsMessage=Contrase\u00F1a incorrecta: debe contener al menos {0} caracteres especiales.
|
||||
invalidPasswordNotUsernameMessage=Contrase\u00F1a incorrecta: no puede ser igual al nombre de usuario.
|
||||
invalidPasswordRegexPatternMessage=Contrase\u00F1a incorrecta: no cumple la expresi\u00F3n regular.
|
||||
invalidPasswordHistoryMessage=Contrase\u00F1a incorrecta: no puede ser igual a ninguna de las \u00FAltimas {0} contrase\u00F1as.
|
||||
|
||||
locale_de=German
|
||||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
locale_fr=Fran\u00E7ais
|
||||
locale_es=Espa\u00F1ol
|
|
@ -10,7 +10,7 @@ doSignOut=D\u00e9connexion
|
|||
|
||||
editAccountHtmlTtile=Edition du compte
|
||||
federatedIdentitiesHtmlTitle=Identit\u00e9s f\u00e9d\u00e9r\u00e9es
|
||||
accountLogHtmlTitle=Acces au compte
|
||||
accountLogHtmlTitle=Acc\u00e8s au compte
|
||||
changePasswordHtmlTitle=Changer de mot de passe
|
||||
sessionsHtmlTitle=Sessions
|
||||
accountManagementTitle=Gestion de Compte Keycloak
|
||||
|
@ -22,7 +22,7 @@ email=Courriel
|
|||
firstName=Nom
|
||||
givenName=Pr\u00e9nom
|
||||
fullName=Nom Complet
|
||||
lastName=Last name
|
||||
lastName=Nom
|
||||
familyName=Nom de Famille
|
||||
password=Mot de passe
|
||||
passwordConfirm=Confirmation
|
||||
|
@ -31,7 +31,7 @@ username=Compte
|
|||
address=Adresse
|
||||
street=Rue
|
||||
locality=Ville ou Localit\u00e9
|
||||
region=State, Province, or R\u00e9gion
|
||||
region=\u00c9tat, Province ou R\u00e9gion
|
||||
postal_code=Code Postal
|
||||
country=Pays
|
||||
emailVerified=Courriel v\u00e9rifi\u00e9
|
||||
|
@ -50,7 +50,7 @@ role_manage-realm=G\u00e9rer le domaine
|
|||
role_manage-users=G\u00e9rer les utilisateurs
|
||||
role_manage-applications=G\u00e9rer les applications
|
||||
role_manage-identity-providers=G\u00e9rer les fournisseurs d''identit\u00e9s
|
||||
role_manage-clients=G\u00e9rer les clients
|
||||
role_manage-clients=G\u00e9rer les clients
|
||||
role_manage-events=G\u00e9rer les \u00e9v\u00e9nements
|
||||
role_view-profile=Voir le profile
|
||||
role_manage-account=G\u00e9rer le compte
|
||||
|
@ -63,7 +63,7 @@ client_broker=Broker
|
|||
|
||||
|
||||
requiredFields=Champs obligatoires
|
||||
allFieldsRequired=Tous les champs obligatoires
|
||||
allFieldsRequired=Tous les champs sont obligatoires
|
||||
|
||||
backToApplication=« Revenir \u00e0 l''application
|
||||
backTo=Revenir \u00e0 {0}
|
||||
|
@ -74,9 +74,9 @@ ip=IP
|
|||
client=Client
|
||||
clients=Clients
|
||||
details=D\u00e9tails
|
||||
started=S\u00e9lectionn\u00e9
|
||||
started=D\u00e9but
|
||||
lastAccess=Dernier acc\u00e8s
|
||||
expires=Expires
|
||||
expires=Expiration
|
||||
applications=Applications
|
||||
|
||||
account=Compte
|
||||
|
@ -88,7 +88,7 @@ log=Connexion
|
|||
application=Application
|
||||
availablePermissions=Permissions Disponibles
|
||||
grantedPermissions=Permissions accord\u00e9es
|
||||
grantedPersonalInfo=Informations personnels accord\u00e9es
|
||||
grantedPersonalInfo=Informations personnelles accord\u00e9es
|
||||
additionalGrants=Droits additionnels
|
||||
action=Action
|
||||
inResource=dans
|
||||
|
@ -99,7 +99,7 @@ revoke=R\u00e9voquer un droit
|
|||
configureAuthenticators=Authentifications configur\u00e9es.
|
||||
mobile=T\u00e9l\u00e9phone mobile
|
||||
totpStep1=Installez <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> ou bien Google Authenticator sur votre mobile. Ces deux applications sont disponibles sur <a href="https://play.google.com">Google Play</a> et Apple App Store.
|
||||
totpStep2=Ouvrez l''application et scanner le code bar ou entrez la clef.
|
||||
totpStep2=Ouvrez l''application et scanner le code barre ou entrez la cl\u00e9.
|
||||
totpStep3=Entrez le code \u00e0 usage unique fourni par l''application et cliquez sur Sauvegarder pour terminer.
|
||||
|
||||
missingUsernameMessage=Veuillez entrer votre nom d''utilisateur.
|
||||
|
@ -140,17 +140,18 @@ identityProviderRemovedMessage=Le fournisseur d''identit\u00e9 a \u00e9t\u00e9 s
|
|||
accountDisabledMessage=Ce compte est d\u00e9sactiv\u00e9, veuillez contacter votre administrateur.
|
||||
|
||||
accountTemporarilyDisabledMessage=Ce compte est temporairement d\u00e9sactiv\u00e9, veuillez contacter votre administrateur ou r\u00e9essayez plus tard..
|
||||
invalidPasswordMinLengthMessage=Mot de passe invalide : longueur minimale {0}.
|
||||
invalidPasswordMinLowerCaseCharsMessage=Mot de passe invalide : doit contenir au moins {0} lettre(s) en minuscule.
|
||||
invalidPasswordMinDigitsMessage=Mot de passe invalide : doit contenir au moins {0} chiffre(s).
|
||||
invalidPasswordMinUpperCaseCharsMessage=Mot de passe invalide : doit contenir au moins {0} lettre(s) en majuscule.
|
||||
invalidPasswordMinSpecialCharsMessage=Mot de passe invalide : doit contenir au moins {0} caract\u00e8re(s) sp\u00e9ciaux.
|
||||
invalidPasswordNotUsernameMessage=Mot de passe invalide : ne doit pas etre identique au nom d''utilisateur.
|
||||
invalidPasswordRegexPatternMessage=Mot de passe invalide : ne valide pas l''expression rationnelle.
|
||||
invalidPasswordHistoryMessage=Mot de passe invalide : ne doit pas \u00eatre \u00e9gal aux {0} derniers mot de passe.
|
||||
invalidPasswordMinLengthMessage=Mot de passe invalide: longueur minimale {0}.
|
||||
invalidPasswordMinLowerCaseCharsMessage=Mot de passe invalide: doit contenir au moins {0} lettre(s) en minuscule.
|
||||
invalidPasswordMinDigitsMessage=Mot de passe invalide: doit contenir au moins {0} chiffre(s).
|
||||
invalidPasswordMinUpperCaseCharsMessage=Mot de passe invalide: doit contenir au moins {0} lettre(s) en majuscule.
|
||||
invalidPasswordMinSpecialCharsMessage=Mot de passe invalide: doit contenir au moins {0} caract\u00e8re(s) sp\u00e9ciaux.
|
||||
invalidPasswordNotUsernameMessage=Mot de passe invalide: ne doit pas \u00eatre identique au nom d''utilisateur.
|
||||
invalidPasswordRegexPatternMessage=Mot de passe invalide: ne valide pas l''expression rationnelle.
|
||||
invalidPasswordHistoryMessage=Mot de passe invalide: ne doit pas \u00eatre \u00e9gal aux {0} derniers mot de passe.
|
||||
|
||||
locale_de=German
|
||||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
locale_fr=Fran\u00e7ais
|
||||
locale_es=Espa\u00F1ol
|
||||
|
|
|
@ -125,4 +125,4 @@ locale_de=German
|
|||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
locale_fr=Français
|
||||
locale_fr=Fran\u00e7ais
|
|
@ -147,4 +147,4 @@ locale_de=Deutsch
|
|||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (BR)
|
||||
locale_fr=Français
|
||||
locale_fr=Fran\u00e7ais
|
|
@ -22,7 +22,7 @@ registrationEmailAsUsername=de Email as username
|
|||
registrationEmailAsUsername.tooltip=de If enabled then username field is hidden from registration form and email is used as username for new user.
|
||||
editUsernameAllowed=de Edit username
|
||||
editUsernameAllowed.tooltip=de If enabled, the username field is editable, readonly otherwise.
|
||||
resetPasswordAllowed=de Forget password
|
||||
resetPasswordAllowed=de Forgot password
|
||||
resetPasswordAllowed.tooltip=de Show a link on login page for user to click on when they have forgotten their credentials.
|
||||
rememberMe=de Remember Me
|
||||
rememberMe.tooltip=de Show checkbox on login page to allow user to remain logged in between browser restarts until session expires.
|
||||
|
|
|
@ -22,7 +22,7 @@ registrationEmailAsUsername=Email as username
|
|||
registrationEmailAsUsername.tooltip=If enabled then username field is hidden from registration form and email is used as username for new user.
|
||||
editUsernameAllowed=Edit username
|
||||
editUsernameAllowed.tooltip=If enabled, the username field is editable, readonly otherwise.
|
||||
resetPasswordAllowed=Forget password
|
||||
resetPasswordAllowed=Forgot password
|
||||
resetPasswordAllowed.tooltip=Show a link on login page for user to click on when they have forgotten their credentials.
|
||||
rememberMe=Remember Me
|
||||
rememberMe.tooltip=Show checkbox on login page to allow user to remain logged in between browser restarts until session expires.
|
||||
|
@ -374,6 +374,7 @@ table-of-identity-providers=Table of identity providers
|
|||
add-provider.placeholder=Add provider...
|
||||
provider=Provider
|
||||
gui-order=GUI order
|
||||
first-broker-login-flow=First Login Flow
|
||||
redirect-uri=Redirect URI
|
||||
redirect-uri.tooltip=The redirect uri to use when configuring the identity provider.
|
||||
alias=Alias
|
||||
|
@ -393,6 +394,7 @@ update-profile-on-first-login.tooltip=Define conditions under which a user has t
|
|||
trust-email=Trust Email
|
||||
trust-email.tooltip=If enabled then email provided by this provider is not verified even if verification is enabled for the realm.
|
||||
gui-order.tooltip=Number defining order of the provider in GUI (eg. on Login page).
|
||||
first-broker-login-flow.tooltip=Alias of authentication flow, which is triggered after first login with this identity provider.
|
||||
openid-connect-config=OpenID Connect Config
|
||||
openid-connect-config.tooltip=OIDC SP and external IDP configuration.
|
||||
authorization-url=Authorization URL
|
||||
|
|
|
@ -0,0 +1,466 @@
|
|||
# Common messages
|
||||
enabled=Habilitado
|
||||
name=Nombre
|
||||
save=Guardar
|
||||
cancel=Cancelar
|
||||
onText=SI
|
||||
offText=NO
|
||||
client=Cliente
|
||||
clients=Clientes
|
||||
clear=Limpiar
|
||||
selectOne=Selecciona uno...
|
||||
|
||||
true=S\u00ED
|
||||
false=No
|
||||
|
||||
|
||||
# Realm settings
|
||||
realm-detail.enabled.tooltip=Los usuarios y clientes solo pueden acceder a un dominio si est\u00E1 habilitado
|
||||
registrationAllowed=Registro de usuario
|
||||
registrationAllowed.tooltip=Habilitar/deshabilitar la p\u00E1gina de registro. Un enlace para el registro se motrar\u00E1 tambi\u00E9n en la p\u00E1gina de inicio de sesi\u00F3n.
|
||||
registrationEmailAsUsername=Email como nombre de usuario
|
||||
registrationEmailAsUsername.tooltip=Si est\u00E1 habilitado el nombre de usuario queda oculto del formulario de registro y el email se usa como nombre de usuario para los nuevos usuarios.
|
||||
editUsernameAllowed=Editar nombre de usuario
|
||||
editUsernameAllowed.tooltip=Si est\u00E1 habilitado, el nombre de usuario es editable, en otro caso es de solo lectura.
|
||||
resetPasswordAllowed=Olvido contrase\u00F1a
|
||||
resetPasswordAllowed.tooltip=Muestra un enlace en la p\u00E1gina de inicio de sesi\u00F3n para que el usuario haga clic cuando ha olvidado sus credenciales.
|
||||
rememberMe=Seguir conectado
|
||||
rememberMe.tooltip=Muestra la casilla de selecci\u00F3n en la p\u00E1gina de inicio de sesi\u00F3n para permitir al usuario permanecer conectado entre reinicios del navegador hasta que la sesi\u00F3n expire.
|
||||
verifyEmail=Verificar email
|
||||
verifyEmail.tooltip=Forzar al usuario a verificar su direcci\u00F3n de email la primera vez que inicie sesi\u00F3n.
|
||||
sslRequired=Solicitar SSL
|
||||
sslRequired.option.all=todas las peticiones
|
||||
sslRequired.option.external=peticiones externas
|
||||
sslRequired.option.none=ninguna
|
||||
sslRequired.tooltip=\u00BFEs HTTP obligatorio? 'ninguna' significa que HTTPS no es obligatorio para ninguna direcic\u00F3n IP de cliente, 'peticiones externas' indica que localhost y las direcciones IP privadas pueden acceder sin HTTPS, 'todas las peticiones' significa que HTTPS es obligatorio para todas las direcciones IP.
|
||||
publicKey=Clave p\u00FAblica
|
||||
gen-new-keys=Generar nuevas claves
|
||||
certificate=Certificado
|
||||
host=Host
|
||||
smtp-host=Host SMTP
|
||||
port=Puerto
|
||||
smtp-port=Puerto SMTP (por defecto 25)
|
||||
from=De
|
||||
sender-email-addr=Email del emisor
|
||||
enable-ssl=Habilitar SSL
|
||||
enable-start-tls=Habilitar StartTLS
|
||||
enable-auth=Habilitar autenticaci\u00F3n
|
||||
username=Usuario
|
||||
login-username=Usuario
|
||||
password=Contrase\u00F1a
|
||||
login-password=Contrase\u00F1a
|
||||
login-theme=Tema de inicio de sesi\u00F3n
|
||||
select-one=Selecciona uno...
|
||||
login-theme.tooltip=Selecciona el tema para las p\u00E1gina de inicio de sesi\u00F3n, TOTP, permisos, registro y recordatorio de contrase\u00F1a.
|
||||
account-theme=Tema de cuenta
|
||||
account-theme.tooltip=Selecciona el tema para las p\u00E1ginas de gesti\u00F3n de la cuenta de usuario.
|
||||
admin-console-theme=Tema de consola de administraci\u00F3n
|
||||
select-theme-admin-console=Selecciona el tema para la consola de administraci\u00F3n.
|
||||
email-theme=Tema de email
|
||||
select-theme-email=Selecciona el tema para los emails que son enviados por el servidor.
|
||||
i18n-enabled=Internacionalizaci\u00F3n activa
|
||||
supported-locales=Idiomas soportados
|
||||
supported-locales.placeholder=Indica el idioma y pulsa Intro
|
||||
default-locale=Idioma por defecto
|
||||
realm-cache-enabled=Cach\u00E9 de dominio habilitada
|
||||
realm-cache-enabled.tooltip=Activar/desactivar la cach\u00E9 para el dominio, cliente y datos de roles.
|
||||
user-cache-enabled=Cach\u00E9 de usuario habilitada
|
||||
user-cache-enabled.tooltip=Habilitar/deshabilitar la cach\u00E9 de usuarios y de asignaciones de usuarios a roles.
|
||||
revoke-refresh-token=Revocar el token de actualizaci\u00F3n
|
||||
revoke-refresh-token.tooltip=Si est\u00E1 activado los tokens de actualizaci\u00F3n solo pueden usarse una vez. En otro caso los tokens de actualizaci\u00F3n no se revocan cuando se utilizan y pueden ser usado m\u00FAltiples veces.
|
||||
sso-session-idle=Sesiones SSO inactivas
|
||||
seconds=Segundos
|
||||
minutes=Minutos
|
||||
hours=Horas
|
||||
days=D\u00EDas
|
||||
sso-session-max=Tiempo m\u00E1ximo sesi\u00F3n SSO
|
||||
sso-session-idle.tooltip=Tiempo m\u00E1ximo que una sesi\u00F3n puede estar inactiva antes de que expire. Los tokens y sesiones de navegador son invalidadas cuando la sesi\u00F3n expira.
|
||||
sso-session-max.tooltip=Tiempo m\u00E1ximo antes de que una sesi\u00F3n expire. Los tokesn y sesiones de navegador son invalidados cuando una sesi\u00F3n expira.
|
||||
offline-session-idle=Inactividad de sesi\u00F3n sin conexi\u00F3n
|
||||
offline-session-idle.tooltip=Tiempo m\u00E1ximo inactivo de una sesi\u00F3n sin conexi\u00F3n antes de que expire. Necesitas usar un token sin conexi\u00F3n para refrescar al menos una vez dentro de este periodo, en otro caso la sesi\u00F3n sin conexi\u00F3n expirar\u00E1.
|
||||
access-token-lifespan=Duraci\u00F3n del token de acceso
|
||||
access-token-lifespan.tooltip=Tiempo m\u00E1ximo antes de que un token de acceso expire. Se recomiena que esta valor sea corto en relaci\u00F3n al tiempo m\u00E1ximo de SSO
|
||||
client-login-timeout=Tiempo m\u00E1ximo de autenticaci\u00F3n
|
||||
client-login-timeout.tooltip=Tiempo m\u00E1ximo que un cliente tien para finalizar el protocolo de obtenci\u00F3n del token de acceso. Deber\u00EDa ser normalmente del orden de 1 minuto.
|
||||
login-timeout=Tiempo m\u00E1ximo de desconexi\u00F3n
|
||||
login-timeout.tooltip=Tiempo m\u00E1xmo que un usuario tiene para completar el inicio de sesi\u00F3n. Se recomienda que sea relativamente alto. 30 minutos o m\u00E1s.
|
||||
login-action-timeout=Tiempo m\u00E1ximo de acci\u00F3n en el inicio de sesi\u00F3n
|
||||
login-action-timeout.tooltip=Tiempo m\u00E1ximo que un usuario tiene para completar acciones relacionadas con el inicio de sesi\u00F3n, como la actualizaci\u00F3n de contrase\u00F1a o configuraci\u00F3n de TOTP. Es recomendado que sea relativamente alto. 5 minutos o m\u00E1s.
|
||||
headers=Cabeceras
|
||||
brute-force-detection=Detecci\u00F3n de ataques por fuerza bruta
|
||||
x-frame-options=X-Frame-Options
|
||||
click-label-for-info=Haz clic en el enlace de la etiqueta para obtener m\u00E1s informaci\u00F3n. El valor por defecto evita que las p\u00E1ginas sean incluidaos desde iframes externos.
|
||||
content-sec-policy=Content-Security-Policy
|
||||
max-login-failures=N\u00FAmero m\u00E1ximo de fallos de inicios de sesi\u00F3n
|
||||
max-login-failures.tooltip=Indica cuantos fallos se permiten antes de que se dispare una espera.
|
||||
wait-increment=Incremento de espera
|
||||
wait-increment.tooltip=Cuando se ha alcanzado el umbral de fallo, \u00BFcuanto tiempo debe estar un usuario bloqueado?
|
||||
quick-login-check-millis=Tiempo en milisegundos entre inicios de sesi\u00F3n r\u00E1pidos
|
||||
quick-login-check-millis.tooltip=Si ocurren errores de forma concurrente y muy r\u00E1pida, bloquear al usuario.
|
||||
min-quick-login-wait=Tiempo m\u00EDnimo entre fallos de conexi\u00F3n r\u00E1pidos
|
||||
min-quick-login-wait.tooltip=Cuanto tiempo se debe esperar tras un fallo en un intento r\u00E1pido de identificaci\u00F3n
|
||||
max-wait=Espera m\u00E1xima
|
||||
max-wait.tooltip=Tiempo m\u00E1ximo que un usuario quedar\u00E1 bloqueado.
|
||||
failure-reset-time=Reinicio del contador de errores
|
||||
failure-reset-time.tooltip=\u00BFCuando se debe reiniciar el contador de errores?
|
||||
realm-tab-login=Inicio de sesi\u00F3n
|
||||
realm-tab-keys=Claves
|
||||
realm-tab-email=Email
|
||||
realm-tab-themes=Temas
|
||||
realm-tab-cache=Cache
|
||||
realm-tab-tokens=Tokens
|
||||
realm-tab-security-defenses=Defensas de seguridad
|
||||
realm-tab-general=General
|
||||
add-realm=A\u00F1adir dominio
|
||||
|
||||
#Session settings
|
||||
realm-sessions=Sesiones de dominio
|
||||
revocation=Revocaci\u00F3n
|
||||
logout-all=Desconectar todo
|
||||
active-sessions=Sesiones activas
|
||||
sessions=Sesiones
|
||||
not-before=No antes de
|
||||
not-before.tooltip=Revocar cualquier token emitido antes de esta fecha.
|
||||
set-to-now=Fijar a ahora
|
||||
push=Push
|
||||
push.tooltip=Para cada cliente que tiene una URL de administraci\u00F3n, notificarlos the las nuevas pol\u00EDticas de revocaci\u00F3n.
|
||||
|
||||
#Protocol Mapper
|
||||
usermodel.prop.label=Propiedad
|
||||
usermodel.prop.tooltip=Nombre del m\u00E9todo de propiedad in la interfaz UserModel. Por ejemplo, un valor de 'email' referenciar\u00EDa al m\u00E9todo UserModel.getEmail().
|
||||
usermodel.attr.label=Atributo de usuario
|
||||
usermodel.attr.tooltip=Nombre del atributo de usuario almacenado que es el nombre del atributo dentro del map UserModel.attribute.
|
||||
userSession.modelNote.label=Nota sesi\u00F3n usuario
|
||||
userSession.modelNote.tooltip=Nombre de la nota almacenada en la sesi\u00F3n de usuario dentro del mapa UserSessionModel.note
|
||||
multivalued.label=Valores m\u00FAltiples
|
||||
multivalued.tooltip=Indica si el atributo soporta m\u00FAltiples valores. Si est\u00E1 habilitado, la lista de todos los valores de este atributo se fijar\u00E1 como reclamaci\u00F3n. Si est\u00E1 deshabilitado, solo el primer valor ser\u00E1 fijado como reclamaci\u00F3n.
|
||||
selectRole.label=Selecciona rol
|
||||
selectRole.tooltip=Introduce el rol en la caja de texto de la izquierda, o haz clic en este bot\u00F3n para navegar y buscar el rol que quieres.
|
||||
tokenClaimName.label=Nombre de reclamo del token
|
||||
tokenClaimName.tooltip=Nombre del reclamo a insertar en el token. Puede ser un nombre completo como 'address.street'. En este caso, se crear\u00E1 un objeto JSON anidado.
|
||||
jsonType.label=Tipo JSON de reclamaci\u00F3n
|
||||
jsonType.tooltip=El tipo de JSON que deber\u00EDa ser usado para rellenar la petici\u00F3n de JSON en el token. long, int, boolean y String son valores v\u00E1lidos
|
||||
includeInIdToken.label=A\u00F1adir al token de ID
|
||||
includeInAccessToken.label=A\u00F1adir al token de acceso
|
||||
includeInAccessToken.tooltip=\u00BFDeber\u00EDa a\u00F1adirse la identidad reclamada al token de acceso?
|
||||
|
||||
|
||||
# client details
|
||||
clients.tooltip=Los clientes son aplicaciones de navegador de confianza y servicios web de un dominio. Estos clientes pueden solicitar un inicio de sesi\u00F3n. Tambi\u00E9n puedes definir roles espec\u00EDficos de cliente.
|
||||
search.placeholder=Buscar...
|
||||
create=Crear
|
||||
import=Importar
|
||||
client-id=ID Cliente
|
||||
base-url=URL Base
|
||||
actions=Acciones
|
||||
not-defined=No definido
|
||||
edit=Editar
|
||||
delete=Borrar
|
||||
no-results=Sin resultados
|
||||
no-clients-available=No hay clientes disponibles
|
||||
add-client=A\u00F1adir Cliente
|
||||
select-file=Selecciona archivo
|
||||
view-details=Ver detalles
|
||||
clear-import=Limpiar importaci\u00F3n
|
||||
client-id.tooltip=Indica el identificador (ID) referenciado en URIs y tokens. Por ejemplo 'my-client'
|
||||
client.name.tooltip=Indica el nombre visible del cliente. Por ejemplo 'My Client'. Tambi\u00E9n soporta claves para vallores localizados. Por ejemplo: ${my_client}
|
||||
client.enabled.tooltip=Los clientes deshabilitados no pueden iniciar una identificaci\u00F3n u obtener c\u00F3digos de acceso.
|
||||
consent-required=Consentimiento necesario
|
||||
consent-required.tooltip=Si est\u00E1 habilitado, los usuarios tienen que consentir el acceso del cliente.
|
||||
direct-grants-only=Solo permisos directos
|
||||
direct-grants-only.tooltip=Cuando est\u00E1 habilitado, el cliente solo puede obtener permisos de la API REST.
|
||||
client-protocol=Protocolo del Cliente
|
||||
client-protocol.tooltip='OpenID connect' permite a los clientes verificar la identidad del usuario final basado en la autenticaci\u00F3n realizada por un servidor de autorizaci\u00F3n. 'SAML' habilita la autenticaci\u00F3n y autorizaci\u00F3n de escenarios basados en web incluyendo cross-domain y single sign-on (SSO) y utiliza tokdne de seguridad que contienen afirmaciones para pasar informaci\u00F3n.
|
||||
access-type=Tipo de acceso
|
||||
access-type.tooltip=Los clientes 'Confidential' necesitan un secreto para iniciar el protocolo de identificaci\u00F3n. Los clientes 'Public' no requieren un secreto. Los clientes 'Bearer-only' son servicios web que nunca inician un login.
|
||||
service-accounts-enabled=Cuentas de servicio habilitadas
|
||||
service-accounts-enabled.tooltip=Permitir autenticar este cliente contra Keycloak y recivir un token de acceso dedicado para este cliente.
|
||||
include-authnstatement=Incluir AuthnStatement
|
||||
include-authnstatement.tooltip=null
|
||||
sign-documents=Firmar documentos
|
||||
sign-documents.tooltip=\u00BFDeber\u00EDa el dominio firmar los documentos SAML?
|
||||
sign-assertions=Firmar aserciones
|
||||
sign-assertions.tooltip=\u00BFDeber\u00EDan firmarse las aserciones en documentos SAML? Este ajuste no es necesario si el documento ya est\u00E1 siendo firmado.
|
||||
signature-algorithm=Algoritmo de firma
|
||||
signature-algorithm.tooltip=El algoritmo de firma usado para firmar los documentos.
|
||||
canonicalization-method=M\u00E9todo de canonicalizaci\u00F3n
|
||||
canonicalization-method.tooltip=M\u00E9todo de canonicalizaci\u00F3n para las firmas XML
|
||||
encrypt-assertions=Cifrar afirmaciones
|
||||
encrypt-assertions.tooltip=\u00BFDeber\u00EDan cifrarse las afirmaciones SAML con la clave p\u00FAblica del cliente usando AES?
|
||||
client-signature-required=Firma de Cliente requerida
|
||||
client-signature-required.tooltip=\u00BFFirmar\u00E1 el cliente sus peticiones y respuestas SAML? \u00BFY deber\u00EDan ser validadas?
|
||||
force-post-binding=Forzar enlaces POST
|
||||
force-post-binding.tooltip=Usar siempre POST para las respuestas
|
||||
front-channel-logout=Desonexi\u00F3n en primer plano (Front Channel)
|
||||
front-channel-logout.tooltip=Cuando est\u00E1 activado, la desconexi\u00F3n require una redirecci\u00F3n del navegador hacia el cliente. Cuando no est\u00E1 activado, el servidor realiza una invovaci\u00F3n de desconexi\u00F3n en segundo plano.
|
||||
force-name-id-format=Forzar formato NameID
|
||||
force-name-id-format.tooltip=Ignorar la petici\u00F3n de sujeto NameID y usar la configurada en la consola de administraci\u00F3n.
|
||||
name-id-format=Formato de NameID
|
||||
name-id-format.tooltip=El formato de NameID que se usar\u00E1 para el t\u00EDtulo
|
||||
root-url=URL ra\u00EDz
|
||||
root-url.tooltip=URL ra\u00EDz a\u00F1adida a las URLs relativas
|
||||
valid-redirect-uris=URIs de redirecci\u00F3n v\u00E1lidas
|
||||
valid-redirect-uris.tooltip=Patr\u00F3n de URI v\u00E1lida para la cual un navegador puede solicitar la redirecci\u00F3n tras un inicio o cierre de sesi\u00F3n completado. Se permiten comodines simples p.ej. 'http://example.com/*'. Tambi\u00E9n se pueden indicar rutas relativas p.ej. '/my/relative/path/*'. Las rutas relativas generar\u00E1n una URI de redirecci\u00F3n usando el host y puerto de la petici\u00F3n. Para SAML, se deben fijar patrones de URI v\u00E1lidos si quieres confiar en la URL del servicio del consumidor indicada en la petici\u00F3n de inicio de sesi\u00F3n.
|
||||
base-url.tooltip=URL por defecto para usar cuando el servidor de autorizaci\u00F3n necesita redirigir o enviar de vuelta al cliente.
|
||||
admin-url=URL de administraci\u00F3n
|
||||
admin-url.tooltip=URL a la interfaz de administraci\u00F3n del cliente. Fija este valor si el cliente soporta el adaptador de REST. Esta API REST permite al servidor de autenticaci\u00F3n enviar al cliente pol\u00EDticas de revocaci\u00F3n y otras tareas administrativas. Normalment se fija a la URL base del cliente.
|
||||
master-saml-processing-url=URL principal de procesamiento SAML
|
||||
master-saml-processing-url.tooltip=Si est\u00E1 configurada, esta URL se usar\u00E1 para cada enlace al proveedor del servicio del consumidor de aserciones y servicios de desconexi\u00F3n \u00FAnicos. Puede ser sobreescrito de forma individual para cada enlace y servicio en el punto final de configuraci\u00F3n fina de SAML.
|
||||
idp-sso-url-ref=Nombre de la URL de un SSO iniciado por el IDP
|
||||
idp-sso-url-ref.tooltip=Nombre del fragmento de la URL para referenciar al cliente cuando quieres un SSO iniciado por el IDP. Dejanto esto vac\u00EDo deshabilita los SSO iniciados por el IDP. La URL referenciada desde el navegador ser\u00E1: {server-root}/realms/{realm}/protocol/saml/clients/{client-url-name}
|
||||
idp-sso-relay-state=Estado de retransmisi\u00F3n de un SSO iniciado por el IDP
|
||||
idp-sso-relay-state.tooltip=Estado de retransmisi\u00F3n que quiees enviar con una petici\u00F3n SAML cuando se inicia un SSO inidicado por el IDP
|
||||
web-origins=Origenes web
|
||||
web-origins.tooltip=Origenes CORS permitidos. Para permitir todos los or\u00EDgenes de URIs de redirecci\u00F3n v\u00E1lidas a\u00F1ade '+'. Para permitir todos los or\u00EDgenes a\u00F1ade '*'.
|
||||
fine-saml-endpoint-conf=Fine Grain SAML Endpoint Configuration
|
||||
fine-saml-endpoint-conf.tooltip=Expande esta secci\u00F3n para configurar las URL exactas para Assertion Consumer y Single Logout Service.
|
||||
assertion-consumer-post-binding-url=Assertion Consumer Service POST Binding URL
|
||||
assertion-consumer-post-binding-url.tooltip=SAML POST Binding URL for the client's assertion consumer service (login responses). You can leave this blank if you do not have a URL for this binding.
|
||||
assertion-consumer-redirect-binding-url=Assertion Consumer Service Redirect Binding URL
|
||||
assertion-consumer-redirect-binding-url.tooltip=Assertion Consumer Service Redirect Binding URL
|
||||
logout-service-binding-post-url=URL de enlace SAML POST para la desconexi\u00F3n
|
||||
logout-service-binding-post-url.tooltip=URL de enlace SAML POST para la desconexi\u00F3n \u00FAnica del cliente. Puedes dejar esto en blanco si est\u00E1s usando un enlace distinto.
|
||||
logout-service-redir-binding-url=URL de enlace SAML de redirecci\u00F3n para la desconexi\u00F3n
|
||||
logout-service-redir-binding-url.tooltip=URL de enlace SAML de redirecci\u00F3n para la desconexi\u00F3n \u00FAnica del cliente. Puedes dejar esto en blanco si est\u00E1s usando un enlace distinto.
|
||||
|
||||
# client import
|
||||
import-client=Importar Cliente
|
||||
format-option=Formato
|
||||
select-format=Selecciona un formato
|
||||
import-file=Archivo de Importaci\u00F3n
|
||||
|
||||
# client tabs
|
||||
settings=Ajustes
|
||||
credentials=Credenciales
|
||||
saml-keys=Claves SAML
|
||||
roles=Roles
|
||||
mappers=Asignadores
|
||||
mappers.tootip=Los asignadores de protocolos realizan transformaciones en tokens y documentos. Pueden hacer cosas como asignar datos de usuario en peticiones de protocolo, o simplemente transformar cualquier petici\u00F3n entre el cliente y el servidor de autenticaci\u00F3n.
|
||||
scope=\u00C1mbito
|
||||
scope.tooltip=Las asignaciones de \u00E1mbito te permiten restringir que asignaciones de roles de usuario se incluyen en el token de acceso solicitado por el cliente.
|
||||
sessions.tooltip=Ver sesiones activas para este cliente. Permite ver qu\u00E9 usuarios est\u00E1n activos y cuando se identificaron.
|
||||
offline-access=Acceso sin conexi\u00F3n
|
||||
offline-access.tooltip=Ver sesiones sin conexi\u00F3n para este cliente. Te permite ver que usuarios han solicitado tokens sin conexi\u00F3n y cuando los solicitaron. Para revocar todos los tokens del cliente, accede a la pesta\u00F1a de Revocaci\u00F3n y fija el valor \"No antes de\" a \"now\".
|
||||
clustering=Clustering
|
||||
installation=Instalaci\u00F3n
|
||||
installation.tooltip=Herramienta de ayuda para generar la configuraci\u00F3n de varios formatos de adaptadores de cliente que puedes descargar o copiar y pegar para configurar tus clientes.
|
||||
service-account-roles=Roles de cuenta de servicio
|
||||
service-account-roles.tooltip=Permitir autenticar asignaciones de rol para la cuenta de servicio dedicada a este cliente.
|
||||
|
||||
# client credentials
|
||||
client-authenticator=Cliente autenticador
|
||||
client-authenticator.tooltip=Cliente autenticador usado para autenticar este cliente contra el servidor Keycloak
|
||||
certificate.tooltip=Certificado de clinete para validar los JWT emitidos por este cliente y firmados con la clave privada del cliente de tu almac\u00E9n de claves.
|
||||
no-client-certificate-configured=No se ha configurado el certificado de cliente
|
||||
gen-new-keys-and-cert=Genearr nuevas claves y certificado
|
||||
import-certificate=Importar Certificado
|
||||
gen-client-private-key=Generar clave privada de cliente
|
||||
generate-private-key=Generar clave privada
|
||||
archive-format=Formato de Archivo
|
||||
archive-format.tooltip=Formato de archivo Java keystore o PKCS12
|
||||
key-alias=Alias de clave
|
||||
key-alias.tooltip=Alias del archivo de tu clave privada y certificado.
|
||||
key-password=Contrase\u00F1a de la clave
|
||||
key-password.tooltip=Contrase\u00F1a para acceder a la clave privada contenida en el archivo
|
||||
store-password=Contrase\u00F1a del almac\u00E9n
|
||||
store-password.tooltip=Contrase\u00F1a para acceder al archivo
|
||||
generate-and-download=Generar y descargar
|
||||
client-certificate-import=Importaci\u00F3n de certificado de cliente
|
||||
import-client-certificate=Importar Certificado de Cliente
|
||||
jwt-import.key-alias.tooltip=Alias del archivo de tu certificado.
|
||||
secret=Secreto
|
||||
regenerate-secret=Regenerar secreto
|
||||
add-role=A\u00F1adir rol
|
||||
role-name=Nombre de rol
|
||||
composite=Compuesto
|
||||
description=Descripci\u00F3n
|
||||
no-client-roles-available=No hay roles de cliente disponibles
|
||||
scope-param-required=Par\u00E1metro de \u00E1mbito obligatorio
|
||||
scope-param-required.tooltip=Este rol solo ser\u00E1 concedido si el par\u00E1metro de \u00E1mbito con el nombre del rol es usado durante la petici\u00F3n de autorizaci\u00F3n/obtenci\u00F3n de token.
|
||||
composite-roles=Roles compuestos
|
||||
composite-roles.tooltip=Cuanto este rol es asignado/desasignado a un usuario cualquier rol asociado con \u00E9l ser\u00E1 asignado/desasignado de forma impl\u00EDcita.
|
||||
realm-roles=Roles de dominio
|
||||
available-roles=Roles Disponibles
|
||||
add-selected=A\u00F1adir seleccionado
|
||||
associated-roles=Roles Asociados
|
||||
composite.associated-realm-roles.tooltip=Roles a nivel de dominio asociados con este rol compuesto.
|
||||
composite.available-realm-roles.tooltip=Roles a nivel de dominio disponibles en este rol compuesto.
|
||||
remove-selected=Borrar seleccionados
|
||||
client-roles=Roles de Cliente
|
||||
select-client-to-view-roles=Selecciona el cliente para ver sus roles
|
||||
available-roles.tooltip=Roles de este cliente que puedes asociar a este rol compuesto.
|
||||
client.associated-roles.tooltip=Roles de cliente asociados con este rol compuesto.
|
||||
add-builtin=A\u00F1adir Builtin
|
||||
category=Categor\u00EDa
|
||||
type=Tipo
|
||||
no-mappers-available=No hay asignadores disponibles
|
||||
add-builtin-protocol-mappers=A\u00F1adir Builtin Protocol Mappers
|
||||
add-builtin-protocol-mapper=A\u00F1adir Builtin Protocol Mapper
|
||||
scope-mappings=Asignaciones de \u00E1mbito
|
||||
full-scope-allowed=Permitir todos los \u00E1mbitos
|
||||
full-scope-allowed.tooltip=Permite deshabilitar todas las restricciones.
|
||||
scope.available-roles.tooltip=Roles de dominio que pueden ser asignados al \u00E1mbito
|
||||
assigned-roles=Roles Asignados
|
||||
assigned-roles.tooltip=Roles a nivel de dominio asignados a este \u00E1mbito.
|
||||
effective-roles=Roles Efectivos
|
||||
realm.effective-roles.tooltip=Roles de dominio asignados que pueden haber sido heredados de un rol compuesto.
|
||||
select-client-roles.tooltip=Selecciona el cliente para ver sus roles
|
||||
assign.available-roles.tooltip=Roles de clientes disponibles para ser asignados.
|
||||
client.assigned-roles.tooltip=Roles de cliente asignados
|
||||
client.effective-roles.tooltip=Roles de cliente asignados que pueden haber sido heredados desde un rol compuesto.
|
||||
basic-configuration=Configuraci\u00F3n b\u00E1sica
|
||||
node-reregistration-timeout=Tiempo de espera de re-registro de nodo
|
||||
node-reregistration-timeout.tooltip=Indica el m\u00E1ximo intervalo de tiempo para que los nodos del cluster registrados se vuelvan a registrar. Si el nodo del cluster no env\u00EDa una petici\u00F3n de re-registro a Keycloak dentro de este intervalo, ser\u00E1 desregistrado de Keycloak
|
||||
registered-cluster-nodes=Registrar nodos de cluster
|
||||
register-node-manually=Registrar nodo manualmente
|
||||
test-cluster-availability=Probar disponibilidad del cluster
|
||||
last-registration=\u00DAltimo registro
|
||||
node-host=Host del nodo
|
||||
no-registered-cluster-nodes=No hay nodos de cluster registrados disponibles
|
||||
cluster-nodes=Nodos de cluster
|
||||
add-node=A\u00F1adir Nodo
|
||||
active-sessions.tooltip=N\u00FAmero total de sesiones activas para este cliente.
|
||||
show-sessions=Mostrar sesiones
|
||||
show-sessions.tooltip=Advertencia, esta es una operaci\u00F3n potencialmente costosa dependiendo del n\u00FAmero de sesiones activas.
|
||||
user=Usuario
|
||||
from-ip=Desde IP
|
||||
session-start=Inicio de sesi\u00F3n
|
||||
first-page=Primera p\u00E1gina
|
||||
previous-page=P\u00E1gina Anterior
|
||||
next-page=P\u00E1gina siguiente
|
||||
client-revoke.not-before.tooltip=Revocar todos los tokens emitidos antes de esta fecha para este cliente.
|
||||
client-revoke.push.tooltip=Si la URL de administraci\u00F3n est\u00E1 configurada para este cliente, env\u00EDa esta pol\u00EDtica a este cliente.
|
||||
select-a-format=Selecciona un formato
|
||||
download=Descargar
|
||||
offline-tokens=Tokens sin conexi\u00F3n
|
||||
offline-tokens.tooltip=N\u00FAmero total de tokens sin conexi\u00F3n de este cliente.
|
||||
show-offline-tokens=Mostrar tokens sin conexi\u00F3n
|
||||
show-offline-tokens.tooltip=Advertencia, esta es una operaci\u00F3n potencialmente costosa dependiendo del n\u00FAmero de tokens sin conexi\u00F3n.
|
||||
token-issued=Token expedido
|
||||
last-access=\u00DAltimo Acceso
|
||||
last-refresh=\u00DAltima actualizaci\u00F3n
|
||||
key-export=Exportar clave
|
||||
key-import=Importar clave
|
||||
export-saml-key=Exportar clave SAML
|
||||
import-saml-key=Importar clave SAML
|
||||
realm-certificate-alias=Alias del certificado del dominio
|
||||
realm-certificate-alias.tooltip=El certificado del dominio es almacenado en archivo. Este es el alias al mismo.
|
||||
signing-key=Clave de firma
|
||||
saml-signing-key=Clave de firma SAML.
|
||||
private-key=Clave Privada
|
||||
generate-new-keys=Generar nuevas claves
|
||||
export=Exportar
|
||||
encryption-key=Clave de cifrado
|
||||
saml-encryption-key.tooltip=Clave de cifrado de SAML
|
||||
service-accounts=Cuentas de servicio
|
||||
service-account.available-roles.tooltip=Roles de dominio que pueden ser asignados a la cuenta del servicio.
|
||||
service-account.assigned-roles.tooltip=Roles de dominio asignados a la cuenta del servicio.
|
||||
service-account-is-not-enabled-for=La cuenta del servicio no est\u00E1 habilitada para {{client}}
|
||||
create-protocol-mappers=Crear asignadores de protocolo
|
||||
create-protocol-mapper=Crear asignador de protocolo
|
||||
protocol=Protocolo
|
||||
protocol.tooltip=Protocolo.
|
||||
id=ID
|
||||
mapper.name.tooltip=Nombre del asignador.
|
||||
mapper.consent-required.tooltip=Cuando se concede acceso temporal, \u00BFes necesario el consentimiento del usuario para proporcinar estos datos al cliente cliente?
|
||||
consent-text=Texto del consentimiento
|
||||
consent-text.tooltip=Texto para mostrar en la p\u00E1gina de consentimiento.
|
||||
mapper-type=Tipo de asignador
|
||||
|
||||
# realm identity providers
|
||||
identity-providers=Proveedores de identidad
|
||||
table-of-identity-providers=Tabla de proveedores de identidad
|
||||
add-provider.placeholder=A\u00F1adir proveedor...
|
||||
provider=Proveedor
|
||||
gui-order=Orden en la interfaz gr\u00E1fica (GUI)
|
||||
redirect-uri=URI de redirecci\u00F3n
|
||||
redirect-uri.tooltip=La URI de redirecci\u00F3n usada para configurar el proveedor de identidad.
|
||||
alias=Alias
|
||||
identity-provider.alias.tooltip=El alias que identifica de forma \u00FAnica un proveedor de identidad, se usa tambi\u00E9n para construir la URI de redirecci\u00F3n.
|
||||
identity-provider.enabled.tooltip=Habilita/deshabilita este proveedor de identidad.
|
||||
authenticate-by-default=Autenticar por defecto
|
||||
identity-provider.authenticate-by-default.tooltip=Indica si este proveedor deber\u00EDa ser probado por defecto para autenticacaci\u00F3n incluso antes de mostrar la p\u00E1gina de inicio de sesi\u00F3n.
|
||||
store-tokens=Almacenar tokens
|
||||
identity-provider.store-tokens.tooltip=Hablitar/deshabilitar si los tokens deben ser almacenados despu\u00E9s de autenticar a los usuarios.
|
||||
stored-tokens-readable=Tokens almacenados legibles
|
||||
identity-provider.stored-tokens-readable.tooltip=Habilitar/deshabilitar is los nuevos usuarios pueden leear los tokens almacenados. Esto asigna el rol 'broker.read-token'.
|
||||
update-profile-on-first-login=Actualizar perfil en el primer inicio de sesi\u00F3n
|
||||
on=Activado
|
||||
on-missing-info=Si falta informaci\u00F3n
|
||||
off=Desactivado
|
||||
update-profile-on-first-login.tooltip=Define condiciones bajos las cuales un usuario tiene que actualizar su perfil durante el primer inicio de sesi\u00F3n.
|
||||
trust-email=Confiar en el email
|
||||
trust-email.tooltip=Si est\u00E1 habilitado, el email recibido de este proveedor no se verificar\u00E1 aunque la verificaci\u00F3n est\u00E9 habilitada para el dominio.
|
||||
gui-order.tooltip=N\u00FAmero que define el orden del proveedor en la interfaz gr\u00E1fica (GUI) (ej. en la p\u00E1gina de inicio de sesi\u00F3n)
|
||||
openid-connect-config=Configuraci\u00F3n de OpenID Connect
|
||||
openid-connect-config.tooltip=Configuraci\u00F3n de OIDC SP e IDP externos
|
||||
authorization-url=URL de autorizaci\u00F3n
|
||||
authorization-url.tooltip=La URL de autorizaci\u00F3n.
|
||||
token-url=Token URL
|
||||
token-url.tooltip=La URL del token.
|
||||
logout-url=URL de desconexi\u00F3n
|
||||
identity-provider.logout-url.tooltip=Punto de cierre de sesi\u00F3n para usar en la desconexi\u00F3n de usuarios desde un proveedor de identidad (IDP) externo.
|
||||
backchannel-logout=Backchannel Logout
|
||||
backchannel-logout.tooltip=Does the external IDP support backchannel logout?
|
||||
user-info-url=URL de informaci\u00F3n de usuario
|
||||
user-info-url.tooltip=.La URL de informaci\u00F3n de usuario. Opcional
|
||||
identity-provider.client-id.tooltip=El cliente o identificador de cliente registrado en el proveedor de identidad.
|
||||
client-secret=Secreto de Cliente
|
||||
show-secret=Mostrar secreto
|
||||
hide-secret=Ocultar secreto
|
||||
client-secret.tooltip=El cliente o el secreto de cliente registrado en el proveedor de identidad.
|
||||
issuer=Emisor
|
||||
issuer.tooltip=El identificador del emisor para el emisor de la respuesta. Si no se indica, no se realizar\u00E1 ninguna validaci\u00F3n.
|
||||
default-scopes=\u00C1mbitos por defecto
|
||||
identity-provider.default-scopes.tooltip=Los \u00E1mbitos que se enviar\u00E1n cuando se solicite autorizaci\u00F3n. Puede ser una lista de \u00E1mbitos separados por espacios. El valor por defecto es 'openid'.
|
||||
prompt=Prompt
|
||||
unspecified.option=no especificado
|
||||
none.option=ninguno
|
||||
consent.option=consentimiento
|
||||
login.option=login
|
||||
select-account.option=select_account
|
||||
prompt.tooltip=Indica si el servidor de autorizaci\u00F3n solicita al usuario final para reautenticaci\u00F3n y consentimiento.
|
||||
validate-signatures=Validar firmas
|
||||
identity-provider.validate-signatures.tooltip=Habilitar/deshabilitar la validaci\u00F3n de firmas de proveedores de identidad (IDP) externos
|
||||
validating-public-key=Validando clave p\u00FAblica
|
||||
identity-provider.validating-public-key.tooltip=La clave p\u00FAblica en formato PEM que debe usarse para verificar las firmas de proveedores de identidad (IDP) externos.
|
||||
import-external-idp-config=Importar configuraci\u00F3n externa de IDP
|
||||
import-external-idp-config.tooltip=Te permite cargar metadatos de un proveedor de identidad (IDP) externo de un archivo de coniguraci\u00F3n o descargarlo desde una URL.
|
||||
import-from-url=Importar desde URL
|
||||
identity-provider.import-from-url.tooltip=Importar metadatos desde un descriptor de un proveedor de identidad (IDP) remoto.
|
||||
import-from-file=Importar desde archivo
|
||||
identity-provider.import-from-file.tooltip=Importar metadatos desde un descriptor de un proveedor de identidad (IDP) descargado.
|
||||
saml-config=Configuraci\u00F3n SAML
|
||||
identity-provider.saml-config.tooltip=Configurci\u00F3n de proveedor SAML e IDP externo
|
||||
single-signon-service-url=URL de servicio de conexi\u00F3n \u00FAnico (SSO)
|
||||
saml.single-signon-service-url.tooltip=La URL que debe ser usada para enviar peticiones de autenticaci\u00F3n (SAML AuthnRequest).
|
||||
single-logout-service-url=URL de servicio de desconexi\u00F3n \u00FAnico
|
||||
saml.single-logout-service-url.tooltip=La URL que debe usarse para enviar peticiones de desconexi\u00F3n.
|
||||
nameid-policy-format=Formato de pol\u00EDtica NameID
|
||||
nameid-policy-format.tooltip=Indica la referencia a la URI correspondiente a un formato de NameID. El valor por defecto es urn:oasis:names:tc:SAML:2.0:nameid-format:persistent.
|
||||
http-post-binding-response=HTTP-POST enlace de respuesta
|
||||
http-post-binding-response.tooltip=Indica si se reponde a las peticiones usando HTTP-POST. Si no est\u00E1 activado, se usa HTTP-REDIRECT.
|
||||
http-post-binding-for-authn-request=HTTP-POST para AuthnRequest
|
||||
http-post-binding-for-authn-request.tooltip=Indica si AuthnRequest debe ser envianda usando HTTP-POST. Si no est\u00E1 activado se hace HTTP-REDIRECT.
|
||||
want-authn-requests-signed=Firmar AuthnRequests
|
||||
want-authn-requests-signed.tooltip=Indica si el proveedor de identidad espera recibir firmadas las AuthnRequest.
|
||||
force-authentication=Forzar autenticaci\u00F3n
|
||||
identity-provider.force-authentication.tooltip=Indica si el proveedor de identidad debe autenticar al presentar directamente las credenciales en lugar de depender de un contexto de seguridad previo.
|
||||
validate-signature=Validar firma
|
||||
saml.validate-signature.tooltip=Habilitar/deshabilitar la validaci\u00F3n de firma en respuestas SAML.
|
||||
validating-x509-certificate=Validando certificado X509
|
||||
validating-x509-certificate.tooltip=El certificado en formato PEM que debe usarse para comprobar las firmas.
|
||||
saml.import-from-url.tooltip=Importar metadatos desde un descriptor de entidad remoto de un IDP de SAML
|
||||
social.client-id.tooltip=El identificador del cliente registrado con el proveedor de identidad.
|
||||
social.client-secret.tooltip=El secreto del cliente registrado con el proveedor de identidad.
|
||||
social.default-scopes.tooltip=\u00C1mbitos que se enviar\u00E1n cuando se solicite autorizaci\u00F3n. Ver la documentaci\u00F3n para los posibles valores, separador y valor por defecto.
|
||||
key=Clave
|
||||
stackoverflow.key.tooltip=La clave obtenida en el registro del cliente de Stack Overflow.
|
||||
|
||||
realms=Dominios
|
||||
realm=Dominio
|
||||
|
||||
identity-provider-mappers=Asignadores de proveedores de identidad (IDP)
|
||||
create-identity-provider-mapper=Crear asignador de proveedor de identidad (IDP)
|
||||
add-identity-provider-mapper=A\u00F1adir asignador de proveedor de identidad
|
||||
client.description.tooltip=Indica la descripci\u00F3n del cliente. Por ejemplo 'My Client for TimeSheets'. Tambi\u00E9n soporta claves para valores localizzados. Por ejemplo: ${my_client_description}
|
|
@ -0,0 +1,8 @@
|
|||
invalidPasswordMinLengthMessage=Contrase\u00F1a incorrecta: longitud m\u00EDnima {0}.
|
||||
invalidPasswordMinLowerCaseCharsMessage=Contrase\u00F1a incorrecta: debe contener al menos {0} letras min\u00FAsculas.
|
||||
invalidPasswordMinDigitsMessage=Contrase\u00F1a incorrecta: debe contaner al menos {0} caracteres num\u00E9ricos.
|
||||
invalidPasswordMinUpperCaseCharsMessage=Contrase\u00F1a incorrecta: debe contener al menos {0} letras may\u00FAsculas.
|
||||
invalidPasswordMinSpecialCharsMessage=Contrase\u00F1a incorrecta: debe contener al menos {0} caracteres especiales.
|
||||
invalidPasswordNotUsernameMessage=Contrase\u00F1a incorrecta: no puede ser igual al nombre de usuario.
|
||||
invalidPasswordRegexPatternMessage=Contrase\u00F1a incorrecta: no cumple la expresi\u00F3n regular.
|
||||
invalidPasswordHistoryMessage=Contrase\u00F1a incorrecta: no puede ser igual a ninguna de las \u00FAltimas {0} contrase\u00F1as.
|
|
@ -199,6 +199,9 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
providerFactory : function(IdentityProviderFactoryLoader) {
|
||||
return {};
|
||||
},
|
||||
authFlows : function(AuthenticationFlowsLoader) {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
controller : 'RealmIdentityProviderCtrl'
|
||||
|
@ -217,6 +220,9 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
providerFactory : function(IdentityProviderFactoryLoader) {
|
||||
return new IdentityProviderFactoryLoader();
|
||||
},
|
||||
authFlows : function(AuthenticationFlowsLoader) {
|
||||
return AuthenticationFlowsLoader();
|
||||
}
|
||||
},
|
||||
controller : 'RealmIdentityProviderCtrl'
|
||||
|
@ -235,6 +241,9 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
providerFactory : function(IdentityProviderFactoryLoader) {
|
||||
return IdentityProviderFactoryLoader();
|
||||
},
|
||||
authFlows : function(AuthenticationFlowsLoader) {
|
||||
return AuthenticationFlowsLoader();
|
||||
}
|
||||
},
|
||||
controller : 'RealmIdentityProviderCtrl'
|
||||
|
@ -1406,12 +1415,15 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
controller : 'RealmOtpPolicyCtrl'
|
||||
})
|
||||
.when('/realms/:realm/authentication/config/:provider/:config', {
|
||||
.when('/realms/:realm/authentication/flows/:flow/config/:provider/:config', {
|
||||
templateUrl : resourceUrl + '/partials/authenticator-config.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
flow : function(AuthenticationFlowLoader) {
|
||||
return AuthenticationFlowLoader();
|
||||
},
|
||||
configType : function(AuthenticationConfigDescriptionLoader) {
|
||||
return AuthenticationConfigDescriptionLoader();
|
||||
},
|
||||
|
@ -1421,12 +1433,15 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
controller : 'AuthenticationConfigCtrl'
|
||||
})
|
||||
.when('/create/authentication/:realm/execution/:executionId/provider/:provider', {
|
||||
.when('/create/authentication/:realm/flows/:flow/execution/:executionId/provider/:provider', {
|
||||
templateUrl : resourceUrl + '/partials/authenticator-config.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
flow : function(AuthenticationFlowLoader) {
|
||||
return AuthenticationFlowLoader();
|
||||
},
|
||||
configType : function(AuthenticationConfigDescriptionLoader) {
|
||||
return AuthenticationConfigDescriptionLoader();
|
||||
},
|
||||
|
|
|
@ -337,7 +337,7 @@ module.controller('RealmThemeCtrl', function($scope, Current, Realm, realm, serv
|
|||
$scope.supportedLocalesOptions = {
|
||||
'multiple' : true,
|
||||
'simple_tags' : true,
|
||||
'tags' : ['en', 'de', 'pt-BR', 'it']
|
||||
'tags' : ['en', 'de', 'pt-BR', 'it', 'es']
|
||||
};
|
||||
|
||||
$scope.$watch('realm.supportedLocales', function(oldVal, newVal) {
|
||||
|
@ -594,20 +594,11 @@ module.controller('IdentityProviderTabCtrl', function(Dialog, $scope, Current, N
|
|||
};
|
||||
});
|
||||
|
||||
module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload, $http, $route, realm, instance, providerFactory, IdentityProvider, serverInfo, $location, Notifications, Dialog) {
|
||||
module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload, $http, $route, realm, instance, providerFactory, IdentityProvider, serverInfo, authFlows, $location, Notifications, Dialog) {
|
||||
console.log('RealmIdentityProviderCtrl');
|
||||
|
||||
$scope.realm = angular.copy(realm);
|
||||
|
||||
$scope.initProvider = function() {
|
||||
if (instance && instance.alias) {
|
||||
|
||||
} else {
|
||||
$scope.identityProvider.updateProfileFirstLoginMode = "on";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$scope.initSamlProvider = function() {
|
||||
$scope.nameIdFormats = [
|
||||
/*
|
||||
|
@ -658,7 +649,6 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
|
|||
} else {
|
||||
$scope.identityProvider.config.nameIDPolicyFormat = $scope.nameIdFormats[0].format;
|
||||
$scope.identityProvider.config.signatureAlgorithm = $scope.signatureAlgorithms[1];
|
||||
$scope.identityProvider.updateProfileFirstLoginMode = "off";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -676,8 +666,8 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
|
|||
$scope.identityProvider.alias = providerFactory.id;
|
||||
$scope.identityProvider.providerId = providerFactory.id;
|
||||
$scope.identityProvider.enabled = true;
|
||||
$scope.identityProvider.updateProfileFirstLoginMode = "off";
|
||||
$scope.identityProvider.authenticateByDefault = false;
|
||||
$scope.identityProvider.firstBrokerLoginFlowAlias = 'first broker login';
|
||||
$scope.newIdentityProvider = true;
|
||||
}
|
||||
|
||||
|
@ -696,6 +686,13 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
|
|||
|
||||
$scope.configuredProviders = angular.copy(realm.identityProviders);
|
||||
|
||||
$scope.authFlows = [];
|
||||
for (var i=0 ; i<authFlows.length ; i++) {
|
||||
if (authFlows[i].providerId == 'basic-flow') {
|
||||
$scope.authFlows.push(authFlows[i]);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
}, function() {
|
||||
|
@ -1901,8 +1898,9 @@ module.controller('RequiredActionsCtrl', function($scope, realm, unregisteredReq
|
|||
|
||||
});
|
||||
|
||||
module.controller('AuthenticationConfigCtrl', function($scope, realm, configType, config, AuthenticationConfig, Notifications, Dialog, $location) {
|
||||
module.controller('AuthenticationConfigCtrl', function($scope, realm, flow, configType, config, AuthenticationConfig, Notifications, Dialog, $location) {
|
||||
$scope.realm = realm;
|
||||
$scope.flow = flow;
|
||||
$scope.configType = configType;
|
||||
$scope.create = false;
|
||||
$scope.config = angular.copy(config);
|
||||
|
@ -1927,7 +1925,7 @@ module.controller('AuthenticationConfigCtrl', function($scope, realm, configType
|
|||
}, $scope.config, function() {
|
||||
$scope.changed = false;
|
||||
config = angular.copy($scope.config);
|
||||
$location.url("/realms/" + realm.realm + '/authentication/config/' + configType.providerId + "/" + config.id);
|
||||
$location.url("/realms/" + realm.realm + '/authentication/flows/' + flow.id + '/config/' + configType.providerId + "/" + config.id);
|
||||
Notifications.success("Your changes have been saved.");
|
||||
});
|
||||
};
|
||||
|
@ -1946,15 +1944,16 @@ module.controller('AuthenticationConfigCtrl', function($scope, realm, configType
|
|||
Dialog.confirmDelete($scope.config.alias, 'config', function() {
|
||||
AuthenticationConfig.remove({ realm: realm.realm, config : $scope.config.id }, function() {
|
||||
Notifications.success("The config has been deleted.");
|
||||
$location.url("/realms/" + realm.realm + '/authentication/flows');
|
||||
$location.url("/realms/" + realm.realm + '/authentication/flows/' + flow.id);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
module.controller('AuthenticationConfigCreateCtrl', function($scope, realm, configType, execution, AuthenticationExecutionConfig, Notifications, Dialog, $location) {
|
||||
module.controller('AuthenticationConfigCreateCtrl', function($scope, realm, flow, configType, execution, AuthenticationExecutionConfig, Notifications, Dialog, $location) {
|
||||
$scope.realm = realm;
|
||||
$scope.flow = flow;
|
||||
$scope.create = true;
|
||||
$scope.config = { config: {}};
|
||||
$scope.configType = configType;
|
||||
|
@ -1972,7 +1971,7 @@ module.controller('AuthenticationConfigCreateCtrl', function($scope, realm, conf
|
|||
}, $scope.config, function(data, headers) {
|
||||
var l = headers().location;
|
||||
var id = l.substring(l.lastIndexOf("/") + 1);
|
||||
var url = "/realms/" + realm.realm + '/authentication/config/' + configType.providerId + "/" + id;
|
||||
var url = "/realms/" + realm.realm + '/authentication/flows/' + flow.id + '/config/' + configType.providerId + "/" + id;
|
||||
console.log('redirect url: ' + url);
|
||||
$location.url(url);
|
||||
Notifications.success("Config has been created.");
|
||||
|
|
|
@ -53,8 +53,8 @@
|
|||
<li data-ng-hide="flow.builtIn"><a href="" ng-click="removeExecution(execution)">Delete</a></li>
|
||||
<li data-ng-hide="flow.builtIn || !execution.authenticationFlow"><a href="" ng-click="addSubFlowExecution(execution)">Add Execution</a></li>
|
||||
<li data-ng-hide="flow.builtIn || !execution.authenticationFlow"><a href="" ng-click="addSubFlow(execution)">Add Flow</a></li>
|
||||
<li data-ng-show="execution.configurable && execution.authenticationConfig == null"><a href="#/create/authentication/{{realm.realm}}/execution/{{execution.id}}/provider/{{execution.providerId}}">Config</a></li>
|
||||
<li data-ng-show="execution.configurable && execution.authenticationConfig != null"><a href="#/realms/{{realm.realm}}/authentication/config/{{execution.providerId}}/{{execution.authenticationConfig}}">Config</a></li>
|
||||
<li data-ng-show="execution.configurable && execution.authenticationConfig == null"><a href="#/create/authentication/{{realm.realm}}/flows/{{flow.id}}/execution/{{execution.id}}/provider/{{execution.providerId}}">Config</a></li>
|
||||
<li data-ng-show="execution.configurable && execution.authenticationConfig != null"><a href="#/realms/{{realm.realm}}/authentication/flows/{{flow.id}}/config/{{execution.providerId}}/{{execution.authenticationConfig}}">Config</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="#/realms/{{realm.realm}}/authentication/flows">Authentication Flows</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/authentication/flows/{{flow.alias}}">{{flow.alias | capitalize}}</a></li>
|
||||
<li class="active" data-ng-show="create">Create Authenticator Config</li>
|
||||
<li class="active" data-ng-hide="create">{{config.alias}}</li>
|
||||
</ol>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<div>
|
||||
<select class="form-control" id="provider"
|
||||
ng-model="provider"
|
||||
ng-options="provider.id for provider in providers">
|
||||
ng-options="provider.displayName|capitalize for provider in providers">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="alias">Alias </label>
|
||||
<div class="col-sm-6">
|
||||
<input class="form-control" type="text" id="alias" name="alias" data-ng-model="flow.alias" autofocus>
|
||||
<input class="form-control" type="text" id="alias" name="alias" data-ng-model="flow.alias" autofocus required>
|
||||
</div>
|
||||
<kc-tooltip>Specifies display name for the flow.</kc-tooltip>
|
||||
</div>
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="lookAhead">Look ahead window</label>
|
||||
<div class="col-md-6">
|
||||
<input class="form-control" type="text" id="lookAhead" name="lookAhead" data-ng-model="realm.otpPolicyLookAheadWindow" autofocus>
|
||||
<input class="form-control" type="number" required min="1" max="120" id="lookAhead" name="lookAhead" data-ng-model="realm.otpPolicyLookAheadWindow" autofocus>
|
||||
</div>
|
||||
<kc-tooltip>How far ahead should the server look just in case the token generator and server are out of time sync or counter sync?</kc-tooltip>
|
||||
</div>
|
||||
|
@ -62,7 +62,7 @@
|
|||
<div class="form-group" data-ng-show="realm.otpPolicyType == 'totp'">
|
||||
<label class="col-md-2 control-label" for="counter">OTP Token Period</label>
|
||||
<div class="col-md-6">
|
||||
<input class="form-control" type="text" id="period" name="period" data-ng-model="realm.otpPolicyPeriod">
|
||||
<input class="form-control" type="number" required min="1" max="120" id="period" name="period" data-ng-model="realm.otpPolicyPeriod">
|
||||
</div>
|
||||
<kc-tooltip>How many seconds should an OTP token be valid? Defaults to 30 seconds.</kc-tooltip>
|
||||
</div>
|
||||
|
@ -79,4 +79,4 @@
|
|||
</div>
|
||||
|
||||
|
||||
<kc-menu></kc-menu>
|
||||
<kc-menu></kc-menu>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2" data-ng-init="initProvider()">
|
||||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="#/realms/{{realm.realm}}/identity-provider-settings">{{:: 'identity-providers' | translate}}</a></li>
|
||||
<li>{{identityProvider.alias}}</li>
|
||||
|
@ -52,19 +52,6 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'identity-provider.stored-tokens-readable.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="updateProfileFirstLoginMode">{{:: 'update-profile-on-first-login' | translate}}</label>
|
||||
<div class="col-md-2">
|
||||
<div>
|
||||
<select id="updateProfileFirstLoginMode" ng-model="identityProvider.updateProfileFirstLoginMode" class="form-control">
|
||||
<option value="on">{{:: 'on' | translate}}</option>
|
||||
<option value="missing">{{:: 'on-missing-info' | translate}}</option>
|
||||
<option value="off">{{:: 'off' | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'update-profile-on-first-login.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="trustEmail">{{:: 'trust-email' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
|
@ -79,6 +66,19 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'gui-order.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="firstBrokerLoginFlowAlias">{{:: 'first-broker-login-flow' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<div>
|
||||
<select class="form-control" id="firstBrokerLoginFlowAlias"
|
||||
ng-model="identityProvider.firstBrokerLoginFlowAlias"
|
||||
ng-options="flow.alias as flow.alias for flow in authFlows"
|
||||
required>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'first-broker-login-flow.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend uncollapsed><span class="text">{{:: 'openid-connect-config' | translate}}</span> <kc-tooltip>{{:: 'openid-connect-config.tooltip' | translate}}</kc-tooltip></legend>
|
||||
|
|
|
@ -52,19 +52,6 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'identity-provider.stored-tokens-readable.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="updateProfileFirstLoginMode">{{:: 'update-profile-on-first-login' | translate}}</label>
|
||||
<div class="col-md-2">
|
||||
<div>
|
||||
<select id="updateProfileFirstLoginMode" ng-model="identityProvider.updateProfileFirstLoginMode" class="form-control">
|
||||
<option value="on">{{:: 'on' | translate}}</option>
|
||||
<option value="missing">{{:: 'on-missing-info' | translate}}</option>
|
||||
<option value="off">{{:: 'off' | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'update-profile-on-first-login.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="trustEmail">{{:: 'trust-email' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
|
@ -79,6 +66,19 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'gui-order.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="firstBrokerLoginFlowAlias">{{:: 'first-broker-login-flow' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<div>
|
||||
<select class="form-control" id="firstBrokerLoginFlowAlias"
|
||||
ng-model="identityProvider.firstBrokerLoginFlowAlias"
|
||||
ng-options="flow.alias as flow.alias for flow in authFlows"
|
||||
required>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'first-broker-login-flow.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend uncollapsed><span class="text">{{:: 'saml-config' | translate}}</span> <kc-tooltip>{{:: 'identity-provider.saml-config.tooltip' | translate}}</kc-tooltip></legend>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2" data-ng-init="initProvider()">
|
||||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="#/realms/{{realm.realm}}/identity-provider-settings">{{:: 'identity-providers' | translate}}</a></li>
|
||||
<li>{{identityProvider.alias}}</li>
|
||||
|
@ -63,19 +63,6 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'identity-provider.enabled.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="updateProfileFirstLoginMode">{{:: 'update-profile-on-first-login' | translate}}</label>
|
||||
<div class="col-md-2">
|
||||
<div>
|
||||
<select id="updateProfileFirstLoginMode" ng-model="identityProvider.updateProfileFirstLoginMode" class="form-control">
|
||||
<option value="on">{{:: 'on' | translate}}</option>
|
||||
<option value="missing">{{:: 'on-missing-info' | translate}}</option>
|
||||
<option value="off">{{:: 'off' | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'update-profile-on-first-login.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="trustEmail">{{:: 'trust-email' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
|
@ -97,6 +84,19 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'gui-order.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="firstBrokerLoginFlowAlias">{{:: 'first-broker-login-flow' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<div>
|
||||
<select class="form-control" id="firstBrokerLoginFlowAlias"
|
||||
ng-model="identityProvider.firstBrokerLoginFlowAlias"
|
||||
ng-options="flow.alias as flow.alias for flow in authFlows"
|
||||
required>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'first-broker-login-flow.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-group">
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<#import "template.ftl" as layout>
|
||||
<@layout.registrationLayout displayMessage=false; section>
|
||||
<#if section = "title">
|
||||
${msg("confirmLinkIdpTitle")}
|
||||
<#elseif section = "header">
|
||||
${msg("confirmLinkIdpTitle")}
|
||||
<#elseif section = "form">
|
||||
<div id="kc-error-message">
|
||||
<p class="instruction">${message.summary}</p>
|
||||
</div>
|
||||
|
||||
<form id="kc-register-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
||||
|
||||
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
|
||||
<button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="updateProfile">${msg("confirmLinkIdpReviewProfile")}</button>
|
||||
<button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="linkAccount">${msg("confirmLinkIdpContinue", idpAlias)}</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</#if>
|
||||
</@layout.registrationLayout>
|
|
@ -0,0 +1,15 @@
|
|||
<#import "template.ftl" as layout>
|
||||
<@layout.registrationLayout; section>
|
||||
<#if section = "title">
|
||||
${msg("emailLinkIdpTitle", idpAlias)}
|
||||
<#elseif section = "header">
|
||||
${msg("emailLinkIdpTitle", idpAlias)}
|
||||
<#elseif section = "form">
|
||||
<p class="instruction">
|
||||
${msg("emailLinkIdp1", idpAlias, brokerContext.username, realm.name)}
|
||||
</p>
|
||||
<p class="instruction">
|
||||
${msg("emailLinkIdp2")} <a href="${url.firstBrokerLoginUrl}">${msg("doClickHere")}</a> ${msg("emailLinkIdp3")}
|
||||
</p>
|
||||
</#if>
|
||||
</@layout.registrationLayout>
|
|
@ -6,7 +6,7 @@
|
|||
${msg("loginProfileTitle")}
|
||||
<#elseif section = "form">
|
||||
<form id="kc-update-profile-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
||||
<#if realm.editUsernameAllowed>
|
||||
<#if user.editUsernameAllowed>
|
||||
<div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('username',properties.kcFormGroupErrorClass!)}">
|
||||
<div class="${properties.kcLabelWrapperClass!}">
|
||||
<label for="username" class="${properties.kcLabelClass!}">${msg("username")}</label>
|
||||
|
|
|
@ -13,7 +13,11 @@
|
|||
</div>
|
||||
|
||||
<div class="${properties.kcInputWrapperClass!}">
|
||||
<input id="username" class="${properties.kcInputClass!}" name="username" value="${(login.username!'')?html}" type="text" autofocus />
|
||||
<#if usernameEditDisabled??>
|
||||
<input id="username" class="${properties.kcInputClass!}" name="username" value="${(login.username!'')?html}" type="text" disabled />
|
||||
<#else>
|
||||
<input id="username" class="${properties.kcInputClass!}" name="username" value="${(login.username!'')?html}" type="text" autofocus />
|
||||
</#if>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -29,7 +33,7 @@
|
|||
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
||||
<#if realm.rememberMe>
|
||||
<#if realm.rememberMe && !usernameEditDisabled??>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<#if login.rememberMe??>
|
||||
|
@ -56,7 +60,7 @@
|
|||
</form>
|
||||
</#if>
|
||||
<#elseif section = "info" >
|
||||
<#if realm.password && realm.registrationAllowed>
|
||||
<#if realm.password && realm.registrationAllowed && !usernameEditDisabled??>
|
||||
<div id="kc-registration">
|
||||
<span>${msg("noAccount")} <a href="${url.registrationUrl}">${msg("doRegister")}</a></span>
|
||||
</div>
|
||||
|
|
|
@ -197,4 +197,5 @@ locale_de=Deutsch
|
|||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
locale_fr=Français
|
||||
locale_fr=Fran\u00e7ais
|
||||
locale_es=Espa\u00F1ol
|
||||
|
|
|
@ -79,6 +79,11 @@ emailVerifyInstruction1=An email with instructions to verify your email address
|
|||
emailVerifyInstruction2=Haven''t received a verification code in your email?
|
||||
emailVerifyInstruction3=to re-send the email.
|
||||
|
||||
emailLinkIdpTitle=Link {0}
|
||||
emailLinkIdp1=An email with instructions to link {0} account {1} with your {2} account has been sent to you.
|
||||
emailLinkIdp2=Haven''t received a verification code in your email?
|
||||
emailLinkIdp3=to re-send the email.
|
||||
|
||||
backToLogin=« Back to Login
|
||||
|
||||
emailInstruction=Enter your username or email address and we will send you instructions on how to create a new password.
|
||||
|
@ -132,13 +137,19 @@ invalidTotpMessage=Invalid authenticator code.
|
|||
usernameExistsMessage=Username already exists.
|
||||
emailExistsMessage=Email already exists.
|
||||
|
||||
federatedIdentityEmailExistsMessage=User with email already exists. Please login to account management to link the account.
|
||||
federatedIdentityUsernameExistsMessage=User with username already exists. Please login to account management to link the account.
|
||||
federatedIdentityExistsMessage=User with {0} {1} already exists. Please login to account management to link the account.
|
||||
|
||||
confirmLinkIdpTitle=Account already exists
|
||||
federatedIdentityConfirmLinkMessage=User with {0} {1} already exists. How do you want to continue?
|
||||
federatedIdentityConfirmReauthenticateMessage=Authenticate as {0} to link your account with {1}
|
||||
confirmLinkIdpReviewProfile=Review profile info
|
||||
confirmLinkIdpContinue=Link {0} with existing account
|
||||
|
||||
configureTotpMessage=You need to set up Mobile Authenticator to activate your account.
|
||||
updateProfileMessage=You need to update your user profile to activate your account.
|
||||
updatePasswordMessage=You need to change your password to activate your account.
|
||||
verifyEmailMessage=You need to verify your email address to activate your account.
|
||||
linkIdpMessage=You need to verify your email address to link your account with {0}.
|
||||
|
||||
emailSentMessage=You should receive an email shortly with further instructions.
|
||||
emailSendErrorMessage=Failed to send email, please try again later.
|
||||
|
@ -181,6 +192,7 @@ couldNotObtainTokenMessage=Could not obtain token from identity provider.
|
|||
unexpectedErrorRetrievingTokenMessage=Unexpected error when retrieving token from identity provider.
|
||||
unexpectedErrorHandlingResponseMessage=Unexpected error when handling response from identity provider.
|
||||
identityProviderAuthenticationFailedMessage=Authentication failed. Could not authenticate with identity provider.
|
||||
identityProviderDifferentUserMessage=Authenticated as {0}, but expected to be authenticated as {1}
|
||||
couldNotSendAuthenticationRequestMessage=Could not send authentication request to identity provider.
|
||||
unexpectedErrorHandlingRequestMessage=Unexpected error when handling authentication request to identity provider.
|
||||
invalidAccessCodeMessage=Invalid access code.
|
||||
|
@ -188,6 +200,7 @@ sessionNotActiveMessage=Session not active.
|
|||
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.
|
||||
identityProviderLinkSuccess=Your account was successfully linked with {0} account {1} .
|
||||
realmSupportsNoCredentialsMessage=Realm does not support any credential type.
|
||||
identityProviderNotUniqueMessage=Realm supports multiple identity providers. Could not determine which identity provider should be used to authenticate with.
|
||||
emailVerifiedMessage=Your email address has been verified.
|
||||
|
@ -196,7 +209,8 @@ locale_de=German
|
|||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
locale_fr=Français
|
||||
locale_fr=Fran\u00e7ais
|
||||
locale_es=Espa\u00F1ol
|
||||
|
||||
backToApplication=« Back to Application
|
||||
missingParameterMessage=Missing parameters\: {0}
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
doLogIn=Iniciar sesi\u00F3n
|
||||
doRegister=Reg\u00EDstrate
|
||||
doCancel=Cancelar
|
||||
doSubmit=Enviar
|
||||
doYes=S\u00ED
|
||||
doNo=No
|
||||
doContinue=Continuar
|
||||
doAccept=Aceptar
|
||||
doDecline=Declinar
|
||||
doForgotPassword=\u00BFHas olvidado tu contrase\u00F1a?
|
||||
doClickHere=Haz clic aqu\u00ED
|
||||
doImpersonate=Personificar
|
||||
kerberosNotConfigured=Kerberos no configurado
|
||||
kerberosNotConfiguredTitle=Kerberos no configurado
|
||||
bypassKerberosDetail=O bien no est\u00E1s identificado mediante Kerberos o tu navegador no est\u00E1 configurado para identificarse mediante Kerberos. Por favor haz clic para identificarte por otro medio.
|
||||
kerberosNotSetUp=Kerberos no est\u00E1 configurado. No puedes identificarte.
|
||||
registerWithTitle=Reg\u00EDstrate con {0}
|
||||
registerWithTitleHtml=Reg\u00EDstrate con <strong>{0}</strong>
|
||||
loginTitle=Inicia sesi\u00F3n en {0}
|
||||
loginTitleHtml=Inicia sesi\u00F3n en {0}
|
||||
impersonateTitle={0}\u00A0Personificar Usuario
|
||||
impersonateTitleHtml=<strong>{0}</strong> Personificar Usuario</strong>
|
||||
realmChoice=Dominio
|
||||
unknownUser=Usuario desconocido
|
||||
loginTotpTitle=Configura tu aplicaci\u00F3n de identificaci\u00F3n m\u00F3vil
|
||||
loginProfileTitle=Actualiza la informaci\u00F3n de tu cuenta
|
||||
loginTimeout=Has tardado demasiado en identificarte. Inicia de nuevo la identificaci\u00F3n.
|
||||
oauthGrantTitle=Concesi\u00F3n OAuth
|
||||
oauthGrantTitleHtml=Acceso temporal para <strong>{0}</strong> solicitado por
|
||||
errorTitle=Lo sentimos...
|
||||
errorTitleHtml=Lo <strong>sentimos</strong>...
|
||||
emailVerifyTitle=Verificaci\u00F3n del email
|
||||
emailForgotTitle=\u00BFHas olvidado tu contrase\u00F1a?
|
||||
updatePasswordTitle=Modificaci\u00F3n de contrase\u00F1a
|
||||
codeSuccessTitle=C\u00F3digo de \u00E9xito
|
||||
codeErrorTitle=C\u00F3digo de error: {0}
|
||||
|
||||
termsTitle=T\u00E9rminos y Condiciones
|
||||
termsTitleHtml=T\u00E9rminos y Condiciones
|
||||
termsText=<p>T\u00E9rmines y condiciones a definir</p>
|
||||
|
||||
recaptchaFailed=Reconocimiento de texto inv\u00E1lido
|
||||
recaptchaNotConfigured=El reconocimiento de texto es obligatorio pero no est\u00E1 configurado
|
||||
consentDenied=Consentimiento rechazado.
|
||||
|
||||
noAccount=\u00BFUsuario nuevo?
|
||||
username=Usuario
|
||||
usernameOrEmail=Usuario o email
|
||||
firstName=Nombre
|
||||
givenName=Nombre de pila
|
||||
fullName=Nombre completo
|
||||
lastName=Apellidos
|
||||
familyName=Apellidos
|
||||
email=Email
|
||||
password=Contrase\u00F1a
|
||||
passwordConfirm=Confirma la contrase\u00F1a
|
||||
passwordNew=Nueva contrase\u00F1a
|
||||
passwordNewConfirm=Confirma la nueva contrase\u00F1a
|
||||
rememberMe=Seguir conectado
|
||||
authenticatorCode=C\u00F3digo de identificaci\u00F3n
|
||||
address=Direcci\u00F3n
|
||||
street=Calle
|
||||
locality=Ciudad o Municipio
|
||||
region=Estado, Provincia, o Regi\u00F3n
|
||||
postal_code=C\u00F3digo Postal
|
||||
country=Pa\u00EDs
|
||||
emailVerified=Email verificado
|
||||
gssDelegationCredential=GSS Delegation Credential
|
||||
|
||||
loginTotpStep1=Instala <a href=\"https://fedorahosted.org/freeotp/\" target=\"_blank\">FreeOTP</a> o Google Authenticator en tu tel\u00E9fono m\u00F3vil. Ambas aplicaciones est\u00E1n disponibles en <a href=\"https://play.google.com\">Google Play</a> y en la App Store de Apple.
|
||||
loginTotpStep2=Abre la aplicacvi\u00F3n y escanea el c\u00F3digo o introduce la clave.
|
||||
loginTotpStep3=Introduce el c\u00F3digo \u00FAnico que te muestra la aplicaci\u00F3n de autenticaci\u00F3n y haz clic en Enviar para finalizar la configuraci\u00F3n
|
||||
loginTotpOneTime=C\u00F3digo de un solo uso
|
||||
|
||||
oauthGrantRequest=\u00BFQuieres permitir estos privilegios de acceso?
|
||||
inResource=en
|
||||
|
||||
emailVerifyInstruction1=Te hemos enviado un email con instrucciones para verificar tu email.
|
||||
emailVerifyInstruction2=\u00BFNo has recibido un c\u00F3digo de verificaci\u00F3n en tu email?
|
||||
emailVerifyInstruction3=para reenviar el email.
|
||||
|
||||
backToLogin=« Volver a la identificaci\u00F3n
|
||||
|
||||
emailInstruction=Indica tu usuario o email y te enviaremos instruciones indicando como generar una nueva contrase\u00F1a.
|
||||
|
||||
copyCodeInstruction=Por favor, copia y pega este c\u00F3digo en tu aplicaci\u00F3n:
|
||||
|
||||
personalInfo=Informaci\u00F3n personal:
|
||||
role_admin=Admin
|
||||
role_realm-admin=Administrador del dominio
|
||||
role_create-realm=Crear dominio
|
||||
role_create-client=Crear cliente
|
||||
role_view-realm=Ver dominio
|
||||
role_view-users=Ver usuarios
|
||||
role_view-applications=Ver aplicaciones
|
||||
role_view-clients=Ver clientes
|
||||
role_view-events=Ver eventos
|
||||
role_view-identity-providers=Ver proveedores de identidad
|
||||
role_manage-realm=Gestionar dominio
|
||||
role_manage-users=Gestionar usuarios
|
||||
role_manage-applications=Gestionar aplicaciones
|
||||
role_manage-identity-providers=Gestionar proveedores de identidad
|
||||
role_manage-clients=Gestionar clientes
|
||||
role_manage-events=Gestionar eventos
|
||||
role_view-profile=Ver perfil
|
||||
role_manage-account=Gestionar cuenta
|
||||
role_read-token=Leer token
|
||||
role_offline-access=Acceso sin conexi\u00F3n
|
||||
client_account=Cuenta
|
||||
client_security-admin-console=Consola de Administraci\u00F3n de Seguridad
|
||||
client_realm-management=Gesti\u00F3n del dominio
|
||||
client_broker=Broker
|
||||
|
||||
invalidUserMessage=Usuario o contrase\u00F1a incorrectos.
|
||||
invalidEmailMessage=Email no v\u00E1lido
|
||||
accountDisabledMessage=La cuenta est\u00E1 desactivada, contacta con el administrador.
|
||||
accountTemporarilyDisabledMessage=La cuenta est\u00E1 temporalmente desactivada, contacta con el administrador o int\u00E9ntalo de nuevo m\u00E1s tarde.
|
||||
expiredCodeMessage=Se agot\u00F3 el tiempo m\u00E1ximo para la identificaci\u00F3n. Por favor identificate de nuevo.
|
||||
|
||||
missingFirstNameMessage=Por favor indica tu nombre.
|
||||
missingLastNameMessage=Por favor indica tus apellidos.
|
||||
missingEmailMessage=Por favor indica tu email.
|
||||
missingUsernameMessage=Por favor indica tu usuario.
|
||||
missingPasswordMessage=Por favor indica tu contrase\u00F1a.
|
||||
missingTotpMessage=Por favor indica tu c\u00F3digo de autenticaci\u00F3n
|
||||
notMatchPasswordMessage=Las contrase\u00F1as no coinciden.
|
||||
|
||||
invalidPasswordExistingMessage=La contrase\u00F1a actual no es correcta.
|
||||
invalidPasswordConfirmMessage=La confirmaci\u00F3n de contrase\u00F1a no coincide.
|
||||
invalidTotpMessage=El c\u00F3digo de autenticaci\u00F3n no es v\u00E1lido.
|
||||
|
||||
usernameExistsMessage=El nombre de usuario ya existe
|
||||
emailExistsMessage=El email ya existe
|
||||
|
||||
federatedIdentityEmailExistsMessage=Ya existe un usuario con este email. Por favor accede a la gesti\u00F3n de tu cuenta para enlazarlo.
|
||||
federatedIdentityUsernameExistsMessage=Ya existe un usuario con este nombre de usuario. Por favor accede a la gesti\u00F3n de tu cuenta para enlazarlo.
|
||||
|
||||
configureTotpMessage=Tienes que configurar la aplicaci\u00F3n m\u00F3vil de identificaci\u00F3n para activar tu cuenta.
|
||||
updateProfileMessage=Tienes que actualizar tu perfil de usuario para activar tu cuenta.
|
||||
updatePasswordMessage=Tienes que cambiar tu contrase\u00F1a para activar tu cuenta.
|
||||
verifyEmailMessage=Tienes que verificar tu email para activar tu cuenta.
|
||||
|
||||
emailSentMessage=En breve deber\u00EDas recibir un mensaje con m\u00E1s instrucciones
|
||||
emailSendErrorMessage=Fall\u00F3 el env\u00EDo del email, por favor int\u00E9ntalo de nuevo m\u00E1s tarde.
|
||||
|
||||
accountUpdatedMessage=Tu cuenta se ha actualizado.
|
||||
accountPasswordUpdatedMessage=Tu contrase\u00F1a se ha actualizado.
|
||||
|
||||
noAccessMessage=Sin acceso
|
||||
|
||||
invalidPasswordMinLengthMessage=Contrase\u00F1a incorrecta: longitud m\u00EDnima {0}.
|
||||
invalidPasswordMinDigitsMessage=Contrase\u00F1a incorrecta: debe contaner al menos {0} caracteres num\u00E9ricos.
|
||||
invalidPasswordMinLowerCaseCharsMessage=Contrase\u00F1a incorrecta: debe contener al menos {0} letras min\u00FAsculas.
|
||||
invalidPasswordMinUpperCaseCharsMessage=Contrase\u00F1a incorrecta: debe contener al menos {0} letras may\u00FAsculas.
|
||||
invalidPasswordMinSpecialCharsMessage=Contrase\u00F1a incorrecta: debe contener al menos {0} caracteres especiales.
|
||||
invalidPasswordNotUsernameMessage=Contrase\u00F1a incorrecta: no puede ser igual al nombre de usuario.
|
||||
invalidPasswordRegexPatternMessage=Contrase\u00F1a incorrecta: no cumple la expresi\u00F3n regular.
|
||||
invalidPasswordHistoryMessage=Contrase\u00F1a incorrecta: no puede ser igual a ninguna de las \u00FAltimas {0} contrase\u00F1as.
|
||||
|
||||
failedToProcessResponseMessage=Fallo al procesar la respuesta
|
||||
httpsRequiredMessage=HTTPS obligatorio
|
||||
realmNotEnabledMessage=El dominio no est\u00E1 activado
|
||||
invalidRequestMessage=Petici\u00F3n incorrecta
|
||||
failedLogout=Fall\u00F3 la desconexi\u00F3n.
|
||||
unknownLoginRequesterMessage=Solicitante de identificaci\u00F3n desconocido
|
||||
loginRequesterNotEnabledMessage=El solicitante de inicio de sesi\u00F3n est\u00E1 desactivado
|
||||
bearerOnlyMessage=Las aplicaciones Bearer-only no pueden iniciar sesi\u00F3n desde el navegador.
|
||||
directGrantsOnlyMessage=Los clientes de tipo Direct-grants-only no pueden iniciar sesi\u00F3n desde el navegador.
|
||||
invalidRedirectUriMessage=La URI de redirecci\u00F3n no es correcta
|
||||
unsupportedNameIdFormatMessage=NameIDFormat no soportado
|
||||
invlidRequesterMessage=Solicitante no v\u00E1lido
|
||||
registrationNotAllowedMessage=El registro no est\u00E1 permitido
|
||||
resetCredentialNotAllowedMessage=El renicio de las credenciales no est\u00E1 permitido
|
||||
|
||||
permissionNotApprovedMessage=Permiso no aprobado.
|
||||
noRelayStateInResponseMessage=Sin estado de retransmisi\u00F3n en la respuesta del proveedor de identidad.
|
||||
identityProviderAlreadyLinkedMessage=La identidad devuelta por el proveedor de identidad ya est\u00E1 asociada a otro usuario.
|
||||
insufficientPermissionMessage=Permisos insuficientes para enlazar identidades.
|
||||
couldNotProceedWithAuthenticationRequestMessage=No se pudo continuar con la petici\u00F3n de autenticaci\u00F3n al proveedor de identidad.
|
||||
couldNotObtainTokenMessage=.No se pudo obtener el c\u00F3digo del proveedor de identidad
|
||||
unexpectedErrorRetrievingTokenMessage=Error inesperado obteniendo el token del proveedor de identidad
|
||||
unexpectedErrorHandlingResponseMessage=Error inesperado procesando la respuesta del proveedor de identidad.
|
||||
identityProviderAuthenticationFailedMessage=Fall\u00F3 la autenticaci\u00F3n. No fue posible autenticarse en el proveedor de identidad.
|
||||
couldNotSendAuthenticationRequestMessage=No se pudo enviar la petici\u00F3n de identificaci\u00F3n al proveedor de identidad.
|
||||
unexpectedErrorHandlingRequestMessage=Error inesperado durante la petici\u00F3n de identificaci\u00F3n al proveedor de identidad.
|
||||
invalidAccessCodeMessage=C\u00F3digo de acceso no v\u00E1lido.
|
||||
sessionNotActiveMessage=La sesi\u00F3n no est\u00E1 activa
|
||||
invalidCodeMessage=Ha ocurrido un error, por favor identificate de nuevo desde tu aplicaci\u00F3n.
|
||||
identityProviderUnexpectedErrorMessage=Error no esperado intentado autenticar en el proveedor de identidad.
|
||||
identityProviderNotFoundMessage=No se encontr\u00F3 un proveedor de identidad.
|
||||
realmSupportsNoCredentialsMessage=El dominio no soporta ning\u00FAn tipo de credenciales.
|
||||
identityProviderNotUniqueMessage=El dominio soporta m\u00FAltiples proveedores de identidad. No se pudo determinar el proveedor de identidad que deber\u00EDa ser utilizado para identificarse.
|
||||
emailVerifiedMessage=Tu email ha sido verificado.
|
||||
|
||||
locale_de=German
|
||||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
locale_fr=Fran\u00C3\u00A7ais
|
||||
locale_es=Espa\u00F1ol
|
||||
|
||||
backToApplication=« Volver a la aplicaci\u00F3n
|
||||
missingParameterMessage=Par\u00E1metros que faltan: {0}
|
||||
clientNotFoundMessage=Cliente no encontrado
|
||||
invalidParameterMessage=Par\u00E1metro no v\u00E1lido: {0}
|
|
@ -6,48 +6,48 @@ doYes=Oui
|
|||
doNo=Non
|
||||
doContinue=Continuer
|
||||
doAccept=Accepter
|
||||
doDecline=Decliner
|
||||
doForgotPassword=Mot de passe oublié ?
|
||||
doDecline=D\u00e9cliner
|
||||
doForgotPassword=Mot de passe oubli\u00e9 ?
|
||||
doClickHere=Cliquez ici
|
||||
doImpersonate=Impersonate
|
||||
kerberosNotConfigured=Kerberos non configuré
|
||||
kerberosNotConfiguredTitle=Kerberos non configuré
|
||||
bypassKerberosDetail=Si vous n''êtes pas connecté via Kerberos ou bie que votre navigateur n''est opas configurer pour la connexion via Kerberos. Veuillez cliquer pour vous connecter via un autre moyen.
|
||||
kerberosNotSetUp=Kerberos n''est pas configuré. Connexion impossible.
|
||||
kerberosNotConfigured=Kerberos non configur\u00e9
|
||||
kerberosNotConfiguredTitle=Kerberos non configur\u00e9
|
||||
bypassKerberosDetail=Si vous n''\u00eates pas connect\u00e9 via Kerberos ou bien que votre navigateur n''est pas configur\u00e9 pour la connexion via Kerberos. Veuillez cliquer pour vous connecter via un autre moyen.
|
||||
kerberosNotSetUp=Kerberos n''est pas configur\u00e9. Connexion impossible.
|
||||
registerWithTitle=Enregistrement avec {0}
|
||||
registerWithTitleHtml=Enregistrement avec<strong>{0}</strong>
|
||||
loginTitle=Connecté {0}
|
||||
loginTitleHtml=Connecté <strong>{0}</strong>
|
||||
registerWithTitleHtml=Enregistrement avec <strong>{0}</strong>
|
||||
loginTitle=Se connecter \u00e0 {0}
|
||||
loginTitleHtml=Se connecter \u00e0 <strong>{0}</strong>
|
||||
impersonateTitle={0} utilisateur impersonate
|
||||
impersonateTitleHtml=<strong>{0}</strong> utilisateur impersonate</strong>
|
||||
realmChoice=Domaine
|
||||
unknownUser=Utilisateur inconnu
|
||||
loginTotpTitle=Configuration de l''authentification par mobile
|
||||
loginProfileTitle=Mise à jour du compte
|
||||
loginTimeout=Le temps imparti pour la connexion est écoulé. Le processus de connexion redémarre depuis le debut.
|
||||
loginProfileTitle=Mise \u00e0 jour du compte
|
||||
loginTimeout=Le temps imparti pour la connexion est \u00e9coul\u00e9. Le processus de connexion red\u00e9marre depuis le d\u00e9but.
|
||||
oauthGrantTitle=OAuth Grant
|
||||
oauthGrantTitleHtml=Accès temporaire pour <strong>{0}</strong> demandé par
|
||||
errorTitle=Nous sommes désolé ...
|
||||
errorTitleHtml=Nous sommes <strong>désolé</strong> ...
|
||||
emailVerifyTitle=Vérification du courriel
|
||||
emailForgotTitle=Mot de passe oublié ?
|
||||
updatePasswordTitle=Mise à jour du mot de passe
|
||||
codeSuccessTitle=Code succès
|
||||
codeErrorTitle=Code Erreur\: {0}
|
||||
oauthGrantTitleHtml=Acc\u00e8s temporaire pour <strong>{0}</strong> demand\u00e9 par
|
||||
errorTitle=Nous sommes d\u00e9sol\u00e9 ...
|
||||
errorTitleHtml=Nous sommes <strong>d\u00e9sol\u00e9</strong> ...
|
||||
emailVerifyTitle=V\u00e9rification du courriel
|
||||
emailForgotTitle=Mot de passe oubli\u00e9 ?
|
||||
updatePasswordTitle=Mise \u00e0 jour du mot de passe
|
||||
codeSuccessTitle=Code succ\u00e8s
|
||||
codeErrorTitle=Code d''erreur\: {0}
|
||||
|
||||
termsTitle=Termes et Conditions
|
||||
termsTitleHtml=Termes et Conditions
|
||||
termsText=<p>Termes et conditions à définir</p>
|
||||
termsText=<p>Termes et conditions \u00e0 d\u00e9finir</p>
|
||||
|
||||
recaptchaFailed=Re-captcha invalide
|
||||
recaptchaNotConfigured=Re-captcha est requis, mais il n''est pas configuré
|
||||
consentDenied=Consentement refusé.
|
||||
recaptchaNotConfigured=Re-captcha est requis, mais il n''est pas configur\u00e9
|
||||
consentDenied=Consentement refus\u00e9.
|
||||
|
||||
noAccount=Nouvel utilisateur?
|
||||
username=Nom d''utilisateur
|
||||
usernameOrEmail=Nom d''utilisateur ou courriel
|
||||
firstName=Prénom
|
||||
givenName=Prénom
|
||||
firstName=Pr\u00e9nom
|
||||
givenName=Pr\u00e9nom
|
||||
fullName=Nom complet
|
||||
lastName=Nom
|
||||
familyName=Nom de famille
|
||||
|
@ -55,38 +55,38 @@ email=Courriel
|
|||
password=Mot de passe
|
||||
passwordConfirm=Confirmation du mot de passe
|
||||
passwordNew=Nouveau mot de passe
|
||||
passwordNewConfirm=Confirmation du nouveau not de passe
|
||||
passwordNewConfirm=Confirmation du nouveau mot de passe
|
||||
rememberMe=Se souvenir de moi
|
||||
authenticatorCode=Code à usage unique
|
||||
authenticatorCode=Code \u00e0 usage unique
|
||||
address=Adresse
|
||||
street=Rue
|
||||
locality=Ville ou Localité
|
||||
region=État, Province, ou Région
|
||||
locality=Ville ou Localit\u00e9
|
||||
region=\u00c9tat, Province ou R\u00e9gion
|
||||
postal_code=Code postal
|
||||
country=Pays
|
||||
emailVerified=Courriel vérifié
|
||||
gssDelegationCredential=GSS Delegation Credential
|
||||
emailVerified=Courriel v\u00e9rifi\u00e9
|
||||
gssDelegationCredential=Accr\u00e9ditation de d\u00e9l\u00e9gation GSS
|
||||
|
||||
loginTotpStep1=Installez <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> ou bien Google Authenticator sur votre mobile. Ces deux applications sont disponibles sur <a href="https://play.google.com">Google Play</a> et Apple App Store.
|
||||
loginTotpStep2=Ouvrez l''application et scanner le code bar ou entrez la clef.
|
||||
loginTotpStep2=Ouvrez l''application et scanner le code barre ou entrez la cl\u00e9.
|
||||
loginTotpStep3=Entrez le code \u00e0 usage unique fourni par l''application et cliquez sur Sauvegarder pour terminer.
|
||||
loginTotpOneTime=Code à usage unique
|
||||
loginTotpOneTime=Code \u00e0 usage unique
|
||||
|
||||
oauthGrantRequest=Voulez-vous accorder ces privileges d''accès ?
|
||||
oauthGrantRequest=Voulez-vous accorder ces privil\u00e8ges d''acc\u00e8s ?
|
||||
inResource=dans
|
||||
|
||||
emailVerifyInstruction1=Un courriel avec des instructions à suivre vous a été envoyé.
|
||||
emailVerifyInstruction2=Vous n''avez pas reçu de code dans le courriel ?
|
||||
emailVerifyInstruction1=Un courriel avec des instructions \u00e0 suivre vous a \u00e9t\u00e9 envoy\u00e9.
|
||||
emailVerifyInstruction2=Vous n''avez pas re\u00e7u de code dans le courriel?
|
||||
emailVerifyInstruction3=Pour renvoyer le courriel.
|
||||
|
||||
backToLogin=« Retour à la connexion
|
||||
backToLogin=« Retour \u00e0 la connexion
|
||||
|
||||
emailInstruction=Entrez votre nom d''utilisateur ou votre courriel, un email va vous \u00eatre envoyer vous permettant de créer un nouveau mot de passe.
|
||||
emailInstruction=Entrez votre nom d''utilisateur ou votre courriel, un courriel va vous \u00eatre envoy\u00e9 vous permettant de cr\u00e9er un nouveau mot de passe.
|
||||
|
||||
copyCodeInstruction=Copiez le code et recopiez le dans votre application :
|
||||
copyCodeInstruction=Copiez le code et recopiez le dans votre application:
|
||||
|
||||
personalInfo=Information personnelle:
|
||||
role_admin=Adminitrateur
|
||||
role_admin=Administrateur
|
||||
role_realm-admin=Administrateur du domaine
|
||||
role_create-realm=Cr\u00e9er un domaine
|
||||
role_view-realm=Voir un domaine
|
||||
|
@ -99,8 +99,8 @@ role_manage-realm=G\u00e9rer le domaine
|
|||
role_manage-users=G\u00e9rer les utilisateurs
|
||||
role_manage-applications=G\u00e9rer les applications
|
||||
role_manage-identity-providers=G\u00e9rer les fournisseurs d''identit\u00e9s
|
||||
role_manage-clients=G\u00e9rer les clients
|
||||
role_manage-events=G\u00e9rer les \u00e9v\u00e9nements
|
||||
role_manage-clients=G\u00e9rer les clients
|
||||
role_manage-events=G\u00e9rer les \u00e9v\u00e9nements
|
||||
role_view-profile=Voir le profile
|
||||
role_manage-account=G\u00e9rer le compte
|
||||
role_read-token=Lire le jeton d''authentification
|
||||
|
@ -111,9 +111,9 @@ client_realm-management=Gestion du domaine
|
|||
client_broker=Broker
|
||||
|
||||
invalidUserMessage=Nom d''utilisateur ou mot de passe invalide.
|
||||
invalidEmailMessage=Adresse courriel invalide.
|
||||
accountDisabledMessage=Compte désactivé, contactez votre administrateur.
|
||||
accountTemporarilyDisabledMessage=Ce compte est temporairement désactivé, contactez votre administrateur ou bien réassayer plus tard.
|
||||
invalidEmailMessage=Courriel invalide.
|
||||
accountDisabledMessage=Compte d\u00e9sactiv\u00e9, contactez votre administrateur.
|
||||
accountTemporarilyDisabledMessage=Ce compte est temporairement d\u00e9sactiv\u00e9, contactez votre administrateur ou bien r\u00e9assayer plus tard.
|
||||
expiredCodeMessage=Fin de connexion. Veuillez vous reconnecter.
|
||||
|
||||
missingFirstNameMessage=Veuillez entrer votre pr\u00e9nom.
|
||||
|
@ -131,75 +131,76 @@ invalidTotpMessage=Le code d''authentification est invalide.
|
|||
usernameExistsMessage=Le nom d''utilisateur existe d\u00e9j\u00e0.
|
||||
emailExistsMessage=Le courriel existe d\u00e9j\u00e0.
|
||||
|
||||
federatedIdentityEmailExistsMessage=Cet utilisateur avec ce courriel existe déjà. Veuillez vous connecté au gestionnaire de compte pour lier le compte.
|
||||
federatedIdentityUsernameExistsMessage=Cet utilisateur avec ce nom d''utilisateur existe déjà. Veuillez vous connecté au gestionnaire de compte pour lier le compte.
|
||||
federatedIdentityEmailExistsMessage=Cet utilisateur avec ce courriel existe d\u00e9j\u00e0. Veuillez vous connect\u00e9 au gestionnaire de compte pour lier le compte.
|
||||
federatedIdentityUsernameExistsMessage=Cet utilisateur avec ce nom d''utilisateur existe d\u00e9j\u00e0. Veuillez vous connect\u00e9 au gestionnaire de compte pour lier le compte.
|
||||
|
||||
configureTotpMessage=Vous devez configurer l''authentification par mobile pour activer votre compte.
|
||||
updateProfileMessage=Vous devez mettre à jour votre profile pour activer votre compte.
|
||||
updateProfileMessage=Vous devez mettre \u00e0 jour votre profile pour activer votre compte.
|
||||
updatePasswordMessage=Vous devez changer votre mot de passe pour activer votre compte.
|
||||
verifyEmailMessage=Vous devez vérifier votre courriel pour activer votre compte.
|
||||
verifyEmailMessage=Vous devez v\u00e9rifier votre courriel pour activer votre compte.
|
||||
|
||||
emailSentMessage=Vous devriez recevoir rapidement un courriel avec de plus ample instructions.
|
||||
emailSendErrorMessage=Erreur lors de l''envoie du courriel, veuillez essayer plus tard.
|
||||
|
||||
accountUpdatedMessage=Votre compte a été mis à jour.
|
||||
accountPasswordUpdatedMessage=Votre mot de passe a été mis à jour.
|
||||
accountUpdatedMessage=Votre compte a \u00e9t\u00e9 mis \u00e0 jour.
|
||||
accountPasswordUpdatedMessage=Votre mot de passe a \u00e9t\u00e9 mis \u00e0 jour.
|
||||
|
||||
noAccessMessage=Aucun accès
|
||||
noAccessMessage=Aucun acc\u00e8s
|
||||
|
||||
invalidPasswordMinLengthMessage=Mot de passe invalide : longueur minimale {0}.
|
||||
invalidPasswordMinDigitsMessage=Mot de passe invalide : doit contenir au moins {0} chiffre(s).
|
||||
invalidPasswordMinLowerCaseCharsMessage=Mot de passe invalide : doit contenir au moins {0} lettre(s) en minuscule.
|
||||
invalidPasswordMinUpperCaseCharsMessage=Mot de passe invalide : doit contenir au moins {0} lettre(s) en majuscule.
|
||||
invalidPasswordMinSpecialCharsMessage=Mot de passe invalide : doit contenir au moins {0} caract\u00e8re(s) sp\u00e9ciaux.
|
||||
invalidPasswordNotUsernameMessage=Mot de passe invalide : ne doit pas etre identique au nom d''utilisateur.
|
||||
invalidPasswordRegexPatternMessage=Mot de passe invalide : ne valide pas l''expression rationnelle.
|
||||
invalidPasswordHistoryMessage=Mot de passe invalide : ne doit pas \u00eatre \u00e9gal aux {0} derniers mot de passe.
|
||||
invalidPasswordMinLengthMessage=Mot de passe invalide: longueur minimale {0}.
|
||||
invalidPasswordMinDigitsMessage=Mot de passe invalide: doit contenir au moins {0} chiffre(s).
|
||||
invalidPasswordMinLowerCaseCharsMessage=Mot de passe invalide: doit contenir au moins {0} lettre(s) en minuscule.
|
||||
invalidPasswordMinUpperCaseCharsMessage=Mot de passe invalide: doit contenir au moins {0} lettre(s) en majuscule.
|
||||
invalidPasswordMinSpecialCharsMessage=Mot de passe invalide: doit contenir au moins {0} caract\u00e8re(s) sp\u00e9ciaux.
|
||||
invalidPasswordNotUsernameMessage=Mot de passe invalide: ne doit pas \u00eatre identique au nom d''utilisateur.
|
||||
invalidPasswordRegexPatternMessage=Mot de passe invalide: ne valide pas l''expression rationnelle.
|
||||
invalidPasswordHistoryMessage=Mot de passe invalide: ne doit pas \u00eatre \u00e9gal aux {0} derniers mot de passe.
|
||||
|
||||
|
||||
failedToProcessResponseMessage=Erreur lors du traitement de la réponse
|
||||
failedToProcessResponseMessage=Erreur lors du traitement de la r\u00e9ponse
|
||||
httpsRequiredMessage=Le protocole HTTPS est requis
|
||||
realmNotEnabledMessage=Le domaine n''est pas activé
|
||||
invalidRequestMessage=Requete invalide
|
||||
failedLogout=La déconnexion a échouée
|
||||
realmNotEnabledMessage=Le domaine n''est pas activ\u00e9
|
||||
invalidRequestMessage=Requ\u00eate invalide
|
||||
failedLogout=La d\u00e9connexion a \u00e9chou\u00e9e
|
||||
unknownLoginRequesterMessage=Compte inconnu du demandeur
|
||||
loginRequesterNotEnabledMessage=La connexion du demandeur n''est pas active
|
||||
bearerOnlyMessage=Les applications Bearer-only ne sont pas autorisées à initier la connexion par navigateur.
|
||||
directGrantsOnlyMessage=Les clients Direct-grants-only ne sont pas autorisés à initier la connexion par navigateur.
|
||||
bearerOnlyMessage=Les applications Bearer-only ne sont pas autoris\u00e9es \u00e0 initier la connexion par navigateur.
|
||||
directGrantsOnlyMessage=Les clients Direct-grants-only ne sont pas autoris\u00e9s \u00e0 initier la connexion par navigateur.
|
||||
invalidRedirectUriMessage=L''uri de redirection est invalide
|
||||
unsupportedNameIdFormatMessage=NameIDFormat non supporté
|
||||
unsupportedNameIdFormatMessage=NameIDFormat non support\u00e9
|
||||
invlidRequesterMessage=Demandeur invalide
|
||||
registrationNotAllowedMessage=L''enregistrement n''est pas autorisé
|
||||
resetCredentialNotAllowedMessage=La remise \u00e0 z\u00e9ro n''est pas autorisé
|
||||
registrationNotAllowedMessage=L''enregistrement n''est pas autoris\u00e9
|
||||
resetCredentialNotAllowedMessage=La remise \u00e0 z\u00e9ro n''est pas autoris\u00e9
|
||||
|
||||
permissionNotApprovedMessage=La permission n''est pas approuvée.
|
||||
noRelayStateInResponseMessage=Aucun état de relais dans la réponse du fournisseur d''identité.
|
||||
identityProviderAlreadyLinkedMessage=L''identité retournée par le fournisseur d''identité est déjà liée avec un autre utilisateur.
|
||||
insufficientPermissionMessage=Permissions insuffisantes pour lier les identités.
|
||||
couldNotProceedWithAuthenticationRequestMessage=Impossible de continuer avec la requête d''authentification vers le fournisseur d''identité.
|
||||
couldNotObtainTokenMessage=Impossible de récuperer le jeton du fournisseur d''identité.
|
||||
unexpectedErrorRetrievingTokenMessage=Erreur inattendue lors de la récupération du jeton provenant du fournisseur d''identité.
|
||||
unexpectedErrorHandlingResponseMessage=Erreur inattendue lors du traitement de la réponse provenant du fournisseur d''identité.
|
||||
identityProviderAuthenticationFailedMessage=L''authentification a échouée. Impossible de s''authentifier avec le fournisseur d''identité.
|
||||
couldNotSendAuthenticationRequestMessage=Impossible d''envoyer la requête d''authentification vers le fournisseur d''identité.
|
||||
unexpectedErrorHandlingRequestMessage=Erreur inattendue lors du traitement de la requête vers le fournisseur d''identité.
|
||||
invalidAccessCodeMessage=Code d''accès invalide.
|
||||
permissionNotApprovedMessage=La permission n''est pas approuv\u00e9e.
|
||||
noRelayStateInResponseMessage=Aucun \u00e9tat de relais dans la r\u00e9ponse du fournisseur d''identit\u00e9.
|
||||
identityProviderAlreadyLinkedMessage=L''identit\u00e9 retourn\u00e9e par le fournisseur d''identit\u00e9 est d\u00e9j\u00e0 li\u00e9e avec un autre utilisateur.
|
||||
insufficientPermissionMessage=Permissions insuffisantes pour lier les identit\u00e9s.
|
||||
couldNotProceedWithAuthenticationRequestMessage=Impossible de continuer avec la requ\u00eate d''authentification vers le fournisseur d''identit\u00e9.
|
||||
couldNotObtainTokenMessage=Impossible de r\u00e9cup\u00e9rer le jeton du fournisseur d''identit\u00e9.
|
||||
unexpectedErrorRetrievingTokenMessage=Erreur inattendue lors de la r\u00e9cup\u00e9ration du jeton provenant du fournisseur d''identit\u00e9.
|
||||
unexpectedErrorHandlingResponseMessage=Erreur inattendue lors du traitement de la r\u00e9ponse provenant du fournisseur d''identit\u00e9.
|
||||
identityProviderAuthenticationFailedMessage=L''authentification a \u00e9chou\u00e9e. Impossible de s''authentifier avec le fournisseur d''identit\u00e9.
|
||||
couldNotSendAuthenticationRequestMessage=Impossible d''envoyer la requ\u00eate d''authentification vers le fournisseur d''identit\u00e9.
|
||||
unexpectedErrorHandlingRequestMessage=Erreur inattendue lors du traitement de la requ\u00eate vers le fournisseur d''identit\u00e9.
|
||||
invalidAccessCodeMessage=Code d''acc\u00e8s invalide.
|
||||
sessionNotActiveMessage=La session n''est pas active.
|
||||
invalidCodeMessage=Une erreur est survenue, veuillez vous reconnecter à votre application.
|
||||
identityProviderUnexpectedErrorMessage=Erreur inattendue lors de l''authentification avec fournisseur d''identité.
|
||||
identityProviderNotFoundMessage=Impossible de trouver le fournisseur d''identité avec ce identifiant.
|
||||
realmSupportsNoCredentialsMessage=Ce domaine ne supporte aucun type d''accréditation.
|
||||
identityProviderNotUniqueMessage=Ce domaine autorise plusieurs fournisseurs d''identité. Impossible de déterminer fournisseurs d''identité avec lequel s''authentifier.
|
||||
emailVerifiedMessage=Votre adresse courriel a été vérifiée.
|
||||
invalidCodeMessage=Une erreur est survenue, veuillez vous reconnecter \u00e0 votre application.
|
||||
identityProviderUnexpectedErrorMessage=Erreur inattendue lors de l''authentification avec fournisseur d''identit\u00e9.
|
||||
identityProviderNotFoundMessage=Impossible de trouver le fournisseur d''identit\u00e9 avec cet identifiant.
|
||||
realmSupportsNoCredentialsMessage=Ce domaine ne supporte aucun type d''accr\u00e9ditation.
|
||||
identityProviderNotUniqueMessage=Ce domaine autorise plusieurs fournisseurs d''identit\u00e9. Impossible de d\u00e9terminer le fournisseur d''identit\u00e9 avec lequel s''authentifier.
|
||||
emailVerifiedMessage=Votre courriel a \u00e9t\u00e9 v\u00e9rifi\u00e9.
|
||||
|
||||
locale_de=German
|
||||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
locale_fr=Français
|
||||
locale_fr=Fran\u00e7ais
|
||||
locale_es=Espa\u00F1ol
|
||||
|
||||
|
||||
backToApplication=« Revenir à l''application
|
||||
missingParameterMessage=Paramètres manquants \: {0}
|
||||
backToApplication=« Revenir \u00e0 l''application
|
||||
missingParameterMessage=Param\u00e8tres manquants\: {0}
|
||||
clientNotFoundMessage=Client inconnu.
|
||||
invalidParameterMessage=Paramètres invalide \: {0}
|
||||
invalidParameterMessage=Param\u00e8tre invalide\: {0}
|
||||
|
|
|
@ -189,7 +189,8 @@ locale_de=German
|
|||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
locale_fr=Français
|
||||
locale_fr=Fran\u00e7ais
|
||||
locale_es=Espa\u00F1ol
|
||||
|
||||
backToApplication=« Torna all''Applicazione
|
||||
missingParameterMessage=Parametri Mancanti\: {0}
|
||||
|
|
|
@ -194,7 +194,8 @@ locale_de=Deutsch
|
|||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (BR)
|
||||
locale_fr=Français
|
||||
locale_fr=Fran\u00e7ais
|
||||
locale_es=Espa\u00F1ol
|
||||
|
||||
backToApplication=« Voltar para o aplicativo
|
||||
missingParameterMessage=Par\u00E2metros que faltam\: {0}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
${msg("identityProviderLinkBodyHtml", identityProviderAlias, realmName, identityProviderContext.username, link, linkExpiration)}
|
||||
</body>
|
||||
</html>
|
|
@ -1,6 +1,9 @@
|
|||
emailVerificationSubject=Verify email
|
||||
emailVerificationBody=Someone has created a {2} account with this email address. If this was you, click the link below to verify your email address\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you didn''t create this account, just ignore this message.
|
||||
emailVerificationBodyHtml=<p>Someone has created a {2} account with this email address. If this was you, click the link below to verify your email address</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you didn''t create this account, just ignore this message.</p>
|
||||
identityProviderLinkSubject=Link {0}
|
||||
identityProviderLinkBody=Someone wants to link your "{1}" account with "{0}" account of user {2} . If this was you, click the link below to link accounts\n\n{3}\n\nThis link will expire within {4} minutes.\n\nIf you don''t want to link account, just ignore this message. If you link accounts, you will be able to login to {1} through {0}.
|
||||
identityProviderLinkBodyHtml=<p>Someone wants to link your <b>{1}</b> account with <b>{0}</b> account of user {2} . If this was you, click the link below to link accounts</p><p><a href="{3}">{3}</a></p><p>This link will expire within {4} minutes.</p><p>If you don''t want to link account, just ignore this message. If you link accounts, you will be able to login to {1} through {0}.</p>
|
||||
passwordResetSubject=Reset password
|
||||
passwordResetBody=Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.\n\n{0}\n\nThis link and code will expire within {1} minutes.\n\nIf you don''t want to reset your credentials, just ignore this message and nothing will be changed.
|
||||
passwordResetBodyHtml=<p>Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your credentials, just ignore this message and nothing will be changed.</p>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
emailVerificationSubject=Verificaci\u00F3n de email
|
||||
emailVerificationBody=Alguien ha creado una cuenta de {2} con esta direcci\u00F3n de email. Si has sido t\u00FA, haz click en el enlace siguiente para verificar tu direcci\u00F3n de email.\n\n{0}\n\nEste enlace expirar\u00E1 en {1} minutos.\n\nSi t\u00FA no has creado esta cuenta, simplemente ignora este mensaje.
|
||||
emailVerificationBodyHtml=<p>Alguien ha creado una cuenta de {2} con esta direcci\u00F3n de email. Si has sido t\u00FA, haz click en el enlace siguiente para verificar tu direcci\u00F3n de email.</p><p><a href=\"{0}\">{0}</a></p><p>Este enlace expirar\u00E1 en {1} minutos.</p><p>Si t\u00FA no has creado esta cuenta, simplemente ignora este mensaje.</p>
|
||||
passwordResetSubject=Reiniciar contrase\u00F1a
|
||||
passwordResetBody=Alguien ha solicitado cambiar las credenciales de tu cuenta de {2}. Si has sido t\u00FA, haz clic en el enlace siguiente para reiniciarlas.\n\n{0}\n\nEste enlace expirar\u00E1 en {1} minutos.\n\nSi no quieres reiniciar tus credenciales, simplemente ignora este mensaje y no se realizar\u00E1 ning\u00FAn cambio.
|
||||
passwordResetBodyHtml=<p>Alguien ha solicitado cambiar las credenciales de tu cuenta de {2}. Si has sido t\u00FA, haz clic en el enlace siguiente para reiniciarlas.</p><p><a href=\"{0}\">{0}</a></p><p>Este enlace expirar\u00E1 en {1} minutos.</p><p>Si no quieres reiniciar tus credenciales, simplemente ignora este mensaje y no se realizar\u00E1 ning\u00FAn cambio.</p>
|
||||
executeActionsSubject=Actualiza tu cuenta
|
||||
executeActionsBody=El administrador ha solicitado que actualices tu cuenta de {2}. Haz clic en el enlace inferior para iniciar este proceso.\n\n{0}\n\nEste enlace expirar\u00E1 en {1} minutes.\n\nSi no est\u00E1s al tanto de que el administrador haya solicitado esto, simplemente ignora este mensaje y no se realizar\u00E1 ning\u00FAn cambio.
|
||||
executeActionsBodyHtml=<p>El administrador ha solicitado que actualices tu cuenta de {2}. Haz clic en el enlace inferior para iniciar este proceso.</p><p><a href=\"{0}\">{0}</a></p><p>Este enlace expirar\u00E1 en {1} minutes.</p><p>Si no est\u00E1s al tanto de que el administrador haya solicitado esto, simplemente ignora este mensaje y no se realizar\u00E1 ning\u00FAn cambio.</p>
|
||||
eventLoginErrorSubject=Fallo en el inicio de sesi\u00F3n
|
||||
eventLoginErrorBody=Se ha detectado un intento de acceso fallido a tu cuenta el {0} desde {1}. Si no has sido t\u00FA, por favor contacta con el administrador.
|
||||
eventLoginErrorBodyHtml=<p>Se ha detectado un intento de acceso fallido a tu cuenta el {0} desde {1}. Si no has sido t\u00FA, por favor contacta con el administrador.</p>
|
||||
eventRemoveTotpSubject=Borrado TOTP
|
||||
eventRemoveTotpBody=TOTP fue eliminado de tu cuenta el {0} desde {1}. Si no has sido t\u00FA, por favor contacta con el administrador.
|
||||
eventRemoveTotpBodyHtml=<p>TOTP fue eliminado de tu cuenta el {0} desde {1}. Si no has sido t\u00FA, por favor contacta con el administrador.</p>
|
||||
eventUpdatePasswordSubject=Actualizaci\u00F3n de contrase\u00F1a
|
||||
eventUpdatePasswordBody=Tu contrase\u00F1a se ha actualizado el {0} desde {1}. Si no has sido t\u00FA, por favor contacta con el administrador.
|
||||
eventUpdatePasswordBodyHtml=<p>Tu contrase\u00F1a se ha actualizado el {0} desde {1}. Si no has sido t\u00FA, por favor contacta con el administrador.</p>
|
||||
eventUpdateTotpSubject=Actualizaci\u00F3n de TOTP
|
||||
eventUpdateTotpBody=TOTP se ha actualizado en tu cuenta el {0} desde {1}. Si no has sido t\u00FA, por favor contacta con el administrador.
|
||||
eventUpdateTotpBodyHtml=<p>TOTP se ha actualizado en tu cuenta el {0} desde {1}. Si no has sido t\u00FA, por favor contacta con el administrador.</p>
|
|
@ -0,0 +1 @@
|
|||
${msg("identityProviderLinkBody", identityProviderAlias, realmName, identityProviderContext.username, link, linkExpiration)}
|
|
@ -10,10 +10,14 @@ import org.keycloak.provider.Provider;
|
|||
*/
|
||||
public interface EmailProvider extends Provider {
|
||||
|
||||
String IDENTITY_PROVIDER_BROKER_CONTEXT = "identityProviderBrokerCtx";
|
||||
|
||||
public EmailProvider setRealm(RealmModel realm);
|
||||
|
||||
public EmailProvider setUser(UserModel user);
|
||||
|
||||
public EmailProvider setAttribute(String name, Object value);
|
||||
|
||||
public void sendEvent(Event event) throws EmailException;
|
||||
|
||||
/**
|
||||
|
@ -25,6 +29,11 @@ public interface EmailProvider extends Provider {
|
|||
*/
|
||||
public void sendPasswordReset(String link, long expirationInMinutes) throws EmailException;
|
||||
|
||||
/**
|
||||
* Send to confirm that user wants to link his account with identity broker link
|
||||
*/
|
||||
void sendConfirmIdentityBrokerLink(String link, long expirationInMinutes) throws EmailException;
|
||||
|
||||
/**
|
||||
* Change password email requested by admin
|
||||
*
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package org.keycloak.email.freemarker;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
@ -17,6 +20,7 @@ import javax.mail.internet.MimeMessage;
|
|||
import javax.mail.internet.MimeMultipart;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.email.EmailException;
|
||||
import org.keycloak.email.EmailProvider;
|
||||
import org.keycloak.email.freemarker.beans.EventBean;
|
||||
|
@ -28,6 +32,7 @@ import org.keycloak.freemarker.FreeMarkerUtil;
|
|||
import org.keycloak.freemarker.Theme;
|
||||
import org.keycloak.freemarker.ThemeProvider;
|
||||
import org.keycloak.freemarker.beans.MessageFormatterMethod;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -43,6 +48,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
|
|||
private FreeMarkerUtil freeMarker;
|
||||
private RealmModel realm;
|
||||
private UserModel user;
|
||||
private final Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
|
||||
public FreeMarkerEmailProvider(KeycloakSession session, FreeMarkerUtil freeMarker) {
|
||||
this.session = session;
|
||||
|
@ -61,6 +67,12 @@ public class FreeMarkerEmailProvider implements EmailProvider {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailProvider setAttribute(String name, Object value) {
|
||||
attributes.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEvent(Event event) throws EmailException {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
|
@ -83,6 +95,27 @@ public class FreeMarkerEmailProvider implements EmailProvider {
|
|||
send("passwordResetSubject", "password-reset.ftl", attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendConfirmIdentityBrokerLink(String link, long expirationInMinutes) throws EmailException {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
attributes.put("user", new ProfileBean(user));
|
||||
attributes.put("link", link);
|
||||
attributes.put("linkExpiration", expirationInMinutes);
|
||||
|
||||
String realmName = realm.getName().substring(0, 1).toUpperCase() + realm.getName().substring(1);
|
||||
attributes.put("realmName", realmName);
|
||||
|
||||
BrokeredIdentityContext brokerContext = (BrokeredIdentityContext) this.attributes.get(IDENTITY_PROVIDER_BROKER_CONTEXT);
|
||||
String idpAlias = brokerContext.getIdpConfig().getAlias();
|
||||
idpAlias = idpAlias.substring(0, 1).toUpperCase() + idpAlias.substring(1);
|
||||
|
||||
attributes.put("identityProviderContext", brokerContext);
|
||||
attributes.put("identityProviderAlias", idpAlias);
|
||||
|
||||
List<Object> subjectAttrs = Arrays.<Object>asList(idpAlias);
|
||||
send("identityProviderLinkSubject", subjectAttrs, "identity-provider-link.ftl", attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendExecuteActions(String link, long expirationInMinutes) throws EmailException {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
|
@ -111,6 +144,10 @@ public class FreeMarkerEmailProvider implements EmailProvider {
|
|||
}
|
||||
|
||||
private void send(String subjectKey, String template, Map<String, Object> attributes) throws EmailException {
|
||||
send(subjectKey, Collections.emptyList(), template, attributes);
|
||||
}
|
||||
|
||||
private void send(String subjectKey, List<Object> subjectAttributes, String template, Map<String, Object> attributes) throws EmailException {
|
||||
try {
|
||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
||||
Theme theme = themeProvider.getTheme(realm.getEmailTheme(), Theme.Type.EMAIL);
|
||||
|
@ -118,7 +155,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
|
|||
attributes.put("locale", locale);
|
||||
Properties rb = theme.getMessages(locale);
|
||||
attributes.put("msg", new MessageFormatterMethod(locale, rb));
|
||||
String subject = new MessageFormat(rb.getProperty(subjectKey,subjectKey),locale).format(new Object[0]);
|
||||
String subject = new MessageFormat(rb.getProperty(subjectKey,subjectKey),locale).format(subjectAttributes.toArray());
|
||||
String textTemplate = String.format("text/%s", template);
|
||||
String textBody;
|
||||
try {
|
||||
|
|
|
@ -5,6 +5,8 @@ package org.keycloak.login;
|
|||
*/
|
||||
public enum LoginFormsPages {
|
||||
|
||||
LOGIN, LOGIN_TOTP, LOGIN_CONFIG_TOTP, LOGIN_VERIFY_EMAIL, OAUTH_GRANT, LOGIN_RESET_PASSWORD, LOGIN_UPDATE_PASSWORD, REGISTER, INFO, ERROR, LOGIN_UPDATE_PROFILE, CODE;
|
||||
LOGIN, LOGIN_TOTP, LOGIN_CONFIG_TOTP, LOGIN_VERIFY_EMAIL,
|
||||
LOGIN_IDP_LINK_CONFIRM, LOGIN_IDP_LINK_EMAIL,
|
||||
OAUTH_GRANT, LOGIN_RESET_PASSWORD, LOGIN_UPDATE_PASSWORD, REGISTER, INFO, ERROR, LOGIN_UPDATE_PROFILE, CODE;
|
||||
|
||||
}
|
||||
|
|
|
@ -23,6 +23,13 @@ import org.keycloak.provider.Provider;
|
|||
*/
|
||||
public interface LoginFormsProvider extends Provider {
|
||||
|
||||
String UPDATE_PROFILE_CONTEXT_ATTR = "updateProfileCtx";
|
||||
|
||||
String IDENTITY_PROVIDER_BROKER_CONTEXT = "identityProviderBrokerCtx";
|
||||
|
||||
String USERNAME_EDIT_DISABLED = "usernameEditDisabled";
|
||||
|
||||
|
||||
/**
|
||||
* Adds a script to the html header
|
||||
*
|
||||
|
@ -44,6 +51,12 @@ public interface LoginFormsProvider extends Provider {
|
|||
|
||||
public Response createInfoPage();
|
||||
|
||||
public Response createUpdateProfilePage();
|
||||
|
||||
public Response createIdpLinkConfirmLinkPage();
|
||||
|
||||
public Response createIdpLinkEmailPage();
|
||||
|
||||
public Response createErrorPage();
|
||||
|
||||
public Response createOAuthGrant(ClientSessionModel clientSessionModel);
|
||||
|
|
|
@ -19,6 +19,9 @@ package org.keycloak.login.freemarker;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.authentication.requiredactions.util.UpdateProfileContext;
|
||||
import org.keycloak.authentication.requiredactions.util.UserUpdateProfileContext;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.email.EmailException;
|
||||
import org.keycloak.email.EmailProvider;
|
||||
import org.keycloak.freemarker.BrowserSecurityHeaderSetup;
|
||||
|
@ -48,6 +51,7 @@ import org.keycloak.login.freemarker.model.UrlBean;
|
|||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -129,6 +133,9 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
page = LoginFormsPages.LOGIN_CONFIG_TOTP;
|
||||
break;
|
||||
case UPDATE_PROFILE:
|
||||
UpdateProfileContext userBasedContext = new UserUpdateProfileContext(realm, user);
|
||||
this.attributes.put(UPDATE_PROFILE_CONTEXT_ATTR, userBasedContext);
|
||||
|
||||
actionMessage = Messages.UPDATE_PROFILE;
|
||||
page = LoginFormsPages.LOGIN_UPDATE_PROFILE;
|
||||
break;
|
||||
|
@ -140,7 +147,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
try {
|
||||
UriBuilder builder = Urls.loginActionEmailVerificationBuilder(uriInfo.getBaseUri());
|
||||
builder.queryParam(OAuth2Constants.CODE, accessCode);
|
||||
builder.queryParam("key", clientSession.getNote(Constants.VERIFY_EMAIL_KEY));
|
||||
builder.queryParam(Constants.KEY, clientSession.getNote(Constants.VERIFY_EMAIL_KEY));
|
||||
|
||||
String link = builder.build(realm.getName()).toString();
|
||||
long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
|
||||
|
@ -222,6 +229,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
}
|
||||
}
|
||||
attributes.put("message", wholeMessage);
|
||||
} else {
|
||||
attributes.put("message", null);
|
||||
}
|
||||
attributes.put("messagesPerField", messagesPerField);
|
||||
|
||||
|
@ -237,7 +246,11 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
|
||||
if (realm != null) {
|
||||
attributes.put("realm", new RealmBean(realm));
|
||||
attributes.put("social", new IdentityProviderBean(realm, baseUri, uriInfo));
|
||||
|
||||
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
|
||||
identityProviders = LoginFormsUtil.filterIdentityProviders(identityProviders, session, realm, attributes, formData);
|
||||
attributes.put("social", new IdentityProviderBean(realm, identityProviders, baseUri, uriInfo));
|
||||
|
||||
attributes.put("url", new UrlBean(realm, theme, baseUri, this.actionUri));
|
||||
|
||||
if (realm.isInternationalizationEnabled()) {
|
||||
|
@ -268,7 +281,17 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
attributes.put("totp", new TotpBean(realm, user, baseUri));
|
||||
break;
|
||||
case LOGIN_UPDATE_PROFILE:
|
||||
attributes.put("user", new ProfileBean(user, formData));
|
||||
UpdateProfileContext userCtx = (UpdateProfileContext) attributes.get(LoginFormsProvider.UPDATE_PROFILE_CONTEXT_ATTR);
|
||||
attributes.put("user", new ProfileBean(userCtx, formData));
|
||||
break;
|
||||
case LOGIN_IDP_LINK_CONFIRM:
|
||||
case LOGIN_IDP_LINK_EMAIL:
|
||||
BrokeredIdentityContext brokerContext = (BrokeredIdentityContext) this.attributes.get(IDENTITY_PROVIDER_BROKER_CONTEXT);
|
||||
String idpAlias = brokerContext.getIdpConfig().getAlias();
|
||||
idpAlias = idpAlias.substring(0, 1).toUpperCase() + idpAlias.substring(1);
|
||||
|
||||
attributes.put("brokerContext", brokerContext);
|
||||
attributes.put("idpAlias", idpAlias);
|
||||
break;
|
||||
case REGISTER:
|
||||
attributes.put("register", new RegisterBean(formData));
|
||||
|
@ -371,7 +394,11 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
|
||||
if (realm != null) {
|
||||
attributes.put("realm", new RealmBean(realm));
|
||||
attributes.put("social", new IdentityProviderBean(realm, baseUri, uriInfo));
|
||||
|
||||
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
|
||||
identityProviders = LoginFormsUtil.filterIdentityProviders(identityProviders, session, realm, attributes, formData);
|
||||
attributes.put("social", new IdentityProviderBean(realm, identityProviders, baseUri, uriInfo));
|
||||
|
||||
attributes.put("url", new UrlBean(realm, theme, baseUri, this.actionUri));
|
||||
attributes.put("requiredActionUrl", new RequiredActionUrlFormatterMethod(realm, baseUri));
|
||||
|
||||
|
@ -423,6 +450,32 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
return createResponse(LoginFormsPages.INFO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response createUpdateProfilePage() {
|
||||
// Don't display initial message if we already have some errors
|
||||
if (messageType != MessageType.ERROR) {
|
||||
setMessage(MessageType.WARNING, Messages.UPDATE_PROFILE);
|
||||
}
|
||||
|
||||
return createResponse(LoginFormsPages.LOGIN_UPDATE_PROFILE);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Response createIdpLinkConfirmLinkPage() {
|
||||
return createResponse(LoginFormsPages.LOGIN_IDP_LINK_CONFIRM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response createIdpLinkEmailPage() {
|
||||
BrokeredIdentityContext brokerContext = (BrokeredIdentityContext) this.attributes.get(IDENTITY_PROVIDER_BROKER_CONTEXT);
|
||||
String idpAlias = brokerContext.getIdpConfig().getAlias();
|
||||
idpAlias = idpAlias.substring(0, 1).toUpperCase() + idpAlias.substring(1);
|
||||
setMessage(MessageType.WARNING, Messages.LINK_IDP, idpAlias);
|
||||
|
||||
return createResponse(LoginFormsPages.LOGIN_IDP_LINK_EMAIL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response createErrorPage() {
|
||||
if (status == null) {
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package org.keycloak.login.freemarker;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
/**
|
||||
* Various util methods, so the logic is not hardcoded in freemarker beans
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class LoginFormsUtil {
|
||||
|
||||
// Display just those identityProviders on login screen, which are already linked to "known" established user
|
||||
public static List<IdentityProviderModel> filterIdentityProviders(List<IdentityProviderModel> providers, KeycloakSession session, RealmModel realm,
|
||||
Map<String, Object> attributes, MultivaluedMap<String, String> formData) {
|
||||
|
||||
Boolean usernameEditDisabled = (Boolean) attributes.get(LoginFormsProvider.USERNAME_EDIT_DISABLED);
|
||||
if (usernameEditDisabled != null && usernameEditDisabled) {
|
||||
String username = formData.getFirst(UserModel.USERNAME);
|
||||
if (username == null) {
|
||||
throw new IllegalStateException("USERNAME_EDIT_DISABLED but username not known");
|
||||
}
|
||||
|
||||
UserModel user = session.users().getUserByUsername(username, realm);
|
||||
if (user == null || !user.isEnabled()) {
|
||||
throw new IllegalStateException("User " + username + " not found or disabled");
|
||||
}
|
||||
|
||||
Set<FederatedIdentityModel> fedLinks = session.users().getFederatedIdentities(user, realm);
|
||||
Set<String> federatedIdentities = new HashSet<>();
|
||||
for (FederatedIdentityModel fedLink : fedLinks) {
|
||||
federatedIdentities.add(fedLink.getIdentityProvider());
|
||||
}
|
||||
|
||||
List<IdentityProviderModel> result = new LinkedList<>();
|
||||
for (IdentityProviderModel idp : providers) {
|
||||
if (federatedIdentities.contains(idp.getAlias())) {
|
||||
result.add(idp);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return providers;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,6 +17,10 @@ public class Templates {
|
|||
return "login-config-totp.ftl";
|
||||
case LOGIN_VERIFY_EMAIL:
|
||||
return "login-verify-email.ftl";
|
||||
case LOGIN_IDP_LINK_CONFIRM:
|
||||
return "login-idp-link-confirm.ftl";
|
||||
case LOGIN_IDP_LINK_EMAIL:
|
||||
return "login-idp-link-email.ftl";
|
||||
case OAUTH_GRANT:
|
||||
return "login-oauth-grant.ftl";
|
||||
case LOGIN_RESET_PASSWORD:
|
||||
|
|
|
@ -44,9 +44,8 @@ public class IdentityProviderBean {
|
|||
private List<IdentityProvider> providers;
|
||||
private RealmModel realm;
|
||||
|
||||
public IdentityProviderBean(RealmModel realm, URI baseURI, UriInfo uriInfo) {
|
||||
public IdentityProviderBean(RealmModel realm, List<IdentityProviderModel> identityProviders, URI baseURI, UriInfo uriInfo) {
|
||||
this.realm = realm;
|
||||
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
|
||||
|
||||
if (!identityProviders.isEmpty()) {
|
||||
Set<IdentityProvider> orderedSet = new TreeSet<>(IdentityProviderComparator.INSTANCE);
|
||||
|
@ -57,7 +56,7 @@ public class IdentityProviderBean {
|
|||
}
|
||||
|
||||
if (!orderedSet.isEmpty()) {
|
||||
providers = new LinkedList<IdentityProvider>(orderedSet);
|
||||
providers = new LinkedList<>(orderedSet);
|
||||
displaySocial = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import java.util.Map;
|
|||
import javax.ws.rs.core.MultivaluedMap;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.authentication.requiredactions.util.UpdateProfileContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -38,12 +38,12 @@ public class ProfileBean {
|
|||
|
||||
private static final Logger logger = Logger.getLogger(ProfileBean.class);
|
||||
|
||||
private UserModel user;
|
||||
private UpdateProfileContext user;
|
||||
private MultivaluedMap<String, String> formData;
|
||||
|
||||
private final Map<String, String> attributes = new HashMap<>();
|
||||
|
||||
public ProfileBean(UserModel user, MultivaluedMap<String, String> formData) {
|
||||
public ProfileBean(UpdateProfileContext user, MultivaluedMap<String, String> formData) {
|
||||
this.user = user;
|
||||
this.formData = formData;
|
||||
|
||||
|
@ -70,6 +70,10 @@ public class ProfileBean {
|
|||
|
||||
}
|
||||
|
||||
public boolean isEditUsernameAllowed() {
|
||||
return user.isEditUsernameAllowed();
|
||||
}
|
||||
|
||||
public String getUsername() { return formData != null ? formData.getFirst("username") : user.getUsername(); }
|
||||
|
||||
public String getFirstName() {
|
||||
|
|
|
@ -90,6 +90,10 @@ public class UrlBean {
|
|||
return Urls.loginActionEmailVerification(baseURI, realm).toString();
|
||||
}
|
||||
|
||||
public String getFirstBrokerLoginUrl() {
|
||||
return Urls.firstBrokerLoginProcessor(baseURI, realm).toString();
|
||||
}
|
||||
|
||||
public String getOauthAction() {
|
||||
if (this.actionuri != null) {
|
||||
return this.actionuri.getPath();
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<artifactId>keycloak-jetty-adapter-spi</artifactId>
|
||||
<name>Keycloak Jetty Adapter SPI</name>
|
||||
<properties>
|
||||
<jetty9.version>8.1.16.v20140903</jetty9.version>
|
||||
<jetty9.version>8.1.17.v20150415</jetty9.version>
|
||||
<keycloak.osgi.export>
|
||||
org.keycloak.adapters.jetty.spi.*
|
||||
</keycloak.osgi.export>
|
||||
|
|
|
@ -3,10 +3,7 @@ package org.keycloak.adapters.springsecurity;
|
|||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
|
@ -20,16 +17,17 @@ import java.io.IOException;
|
|||
* @author <a href="mailto:srossillo@smartling.com">Scott Rossillo</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class AdapterDeploymentContextBean implements ApplicationContextAware, InitializingBean {
|
||||
public class AdapterDeploymentContextBean implements InitializingBean {
|
||||
|
||||
private static final String KEYCLOAK_CONFIG_FILE = "keycloak.json";
|
||||
private static final String KEYCLOAK_CONFIG_WEB_RESOURCE = "WEB-INF/" + KEYCLOAK_CONFIG_FILE;
|
||||
private static final String KEYCLOAK_CONFIG_CLASSPATH_RESOURCE = "classpath:" + KEYCLOAK_CONFIG_FILE;
|
||||
private final Resource keycloakConfigFileResource;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
private AdapterDeploymentContext deploymentContext;
|
||||
private KeycloakDeployment deployment;
|
||||
|
||||
public AdapterDeploymentContextBean(Resource keycloakConfigFileResource) {
|
||||
this.keycloakConfigFileResource = keycloakConfigFileResource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.deployment = loadKeycloakDeployment();
|
||||
|
@ -38,17 +36,12 @@ public class AdapterDeploymentContextBean implements ApplicationContextAware, In
|
|||
|
||||
private KeycloakDeployment loadKeycloakDeployment() throws IOException {
|
||||
|
||||
Resource resource = applicationContext.getResource(KEYCLOAK_CONFIG_WEB_RESOURCE);
|
||||
|
||||
if (!resource.isReadable()) {
|
||||
resource= applicationContext.getResource(KEYCLOAK_CONFIG_CLASSPATH_RESOURCE);
|
||||
if (!keycloakConfigFileResource.isReadable()) {
|
||||
throw new FileNotFoundException(String.format("Unable to locate Keycloak configuration file: %s",
|
||||
keycloakConfigFileResource.getFilename()));
|
||||
}
|
||||
|
||||
if (!resource.isReadable()) {
|
||||
throw new FileNotFoundException(String.format("Unable to locate Keycloak from %s or %s", KEYCLOAK_CONFIG_WEB_RESOURCE, KEYCLOAK_CONFIG_CLASSPATH_RESOURCE));
|
||||
}
|
||||
|
||||
return KeycloakDeploymentBuilder.build(resource.getInputStream());
|
||||
return KeycloakDeploymentBuilder.build(keycloakConfigFileResource.getInputStream());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,9 +61,4 @@ public class AdapterDeploymentContextBean implements ApplicationContextAware, In
|
|||
public KeycloakDeployment getDeployment() {
|
||||
return deployment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,9 @@ import org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcess
|
|||
import org.keycloak.adapters.springsecurity.filter.KeycloakCsrfRequestMatcher;
|
||||
import org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter;
|
||||
import org.keycloak.adapters.springsecurity.management.HttpSessionManager;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
|
@ -26,19 +28,20 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFi
|
|||
*
|
||||
* @author <a href="mailto:srossillo@smartling.com">Scott Rossillo</a>
|
||||
* @version $Revision: 1 $
|
||||
*
|
||||
* @see EnableWebSecurity
|
||||
* @see EnableWebMvcSecurity
|
||||
*/
|
||||
public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {
|
||||
|
||||
@Value("${keycloak.configurationFile:WEB-INF/keycloak.json}")
|
||||
private Resource keycloakConfigFileResource;
|
||||
|
||||
@Bean
|
||||
protected AdapterDeploymentContextBean adapterDeploymentContextBean() {
|
||||
return new AdapterDeploymentContextBean();
|
||||
return new AdapterDeploymentContextBean(keycloakConfigFileResource);
|
||||
}
|
||||
|
||||
protected AuthenticationEntryPoint authenticationEntryPoint()
|
||||
{
|
||||
protected AuthenticationEntryPoint authenticationEntryPoint() {
|
||||
return new KeycloakAuthenticationEntryPoint();
|
||||
}
|
||||
|
||||
|
@ -48,7 +51,7 @@ public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityCo
|
|||
|
||||
@Bean
|
||||
protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessingFilter() throws Exception {
|
||||
KeycloakAuthenticationProcessingFilter filter = new KeycloakAuthenticationProcessingFilter(authenticationManagerBean());
|
||||
KeycloakAuthenticationProcessingFilter filter = new KeycloakAuthenticationProcessingFilter(authenticationManagerBean());
|
||||
filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
|
||||
return filter;
|
||||
}
|
||||
|
@ -64,7 +67,7 @@ public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityCo
|
|||
|
||||
@Bean
|
||||
protected HttpSessionManager httpSessionManager() {
|
||||
return new HttpSessionManager();
|
||||
return new HttpSessionManager();
|
||||
}
|
||||
|
||||
protected KeycloakLogoutHandler keycloakLogoutHandler() {
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package org.keycloak.adapters.springsecurity;
|
||||
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class AdapterDeploymentContextBeanTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
private AdapterDeploymentContextBean adapterDeploymentContextBean;
|
||||
|
||||
@Test
|
||||
public void should_create_deployment_and_deployment_context() throws Exception {
|
||||
|
||||
//given:
|
||||
adapterDeploymentContextBean = new AdapterDeploymentContextBean(getCorrectResource());
|
||||
|
||||
//when:
|
||||
adapterDeploymentContextBean.afterPropertiesSet();
|
||||
|
||||
//then
|
||||
assertNotNull(adapterDeploymentContextBean.getDeployment());
|
||||
assertNotNull(adapterDeploymentContextBean.getDeploymentContext());
|
||||
}
|
||||
|
||||
private Resource getCorrectResource() {
|
||||
return new ClassPathResource("keycloak.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_throw_exception_when_configuration_file_was_not_found() throws Exception {
|
||||
|
||||
//given:
|
||||
adapterDeploymentContextBean = new AdapterDeploymentContextBean(getEmptyResource());
|
||||
|
||||
//then:
|
||||
expectedException.expect(FileNotFoundException.class);
|
||||
expectedException.expectMessage("Unable to locate Keycloak configuration file: no-file.json");
|
||||
|
||||
//when:
|
||||
adapterDeploymentContextBean.afterPropertiesSet();
|
||||
}
|
||||
|
||||
private Resource getEmptyResource() {
|
||||
return new ClassPathResource("no-file.json");
|
||||
}
|
||||
}
|
10
integration/spring-security/src/test/resources/keycloak.json
Normal file
10
integration/spring-security/src/test/resources/keycloak.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"realm": "spring-security",
|
||||
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCh65Gqi3BSaVe12JHlqChWm8WscICrj46MVqmRoO9FCmqbxEpCQhE1RLjW+GDyc3YdXW3xqUQ3AZxDkTmN1h6BWkhdxPLzA4EnwgWmGurhyJlUF9Id2tKns0jbC+Z7kIb2LcOiKHKL7mRb3q7EtWubNnrvunv8fx+WeXGaQoGEVQIDAQAB",
|
||||
"auth-server-url": "http://localhost:8080/auth",
|
||||
"ssl-required": "external",
|
||||
"resource": "some-resource",
|
||||
"credentials": {
|
||||
"secret": "a9c3501e-20dd-4277-8a7b-351063848446"
|
||||
}
|
||||
}
|
|
@ -23,5 +23,6 @@ public interface Constants {
|
|||
// 30 days
|
||||
int DEFAULT_OFFLINE_SESSION_IDLE_TIMEOUT = 2592000;
|
||||
|
||||
public static final String VERIFY_EMAIL_KEY = "VERIFY_EMAIL_KEY";
|
||||
String VERIFY_EMAIL_KEY = "VERIFY_EMAIL_KEY";
|
||||
String KEY = "key";
|
||||
}
|
||||
|
|
|
@ -45,14 +45,6 @@ public class IdentityProviderModel implements Serializable {
|
|||
private String providerId;
|
||||
|
||||
private boolean enabled;
|
||||
|
||||
/**
|
||||
* For possible values see {@link IdentityProviderRepresentation#getUpdateProfileFirstLoginMode()}
|
||||
* @see IdentityProviderRepresentation#UPFLM_ON
|
||||
* @see IdentityProviderRepresentation#UPFLM_MISSING
|
||||
* @see IdentityProviderRepresentation#UPFLM_OFF
|
||||
*/
|
||||
protected String updateProfileFirstLoginMode = IdentityProviderRepresentation.UPFLM_ON;
|
||||
|
||||
private boolean trustEmail;
|
||||
|
||||
|
@ -64,6 +56,8 @@ public class IdentityProviderModel implements Serializable {
|
|||
*/
|
||||
private boolean authenticateByDefault;
|
||||
|
||||
private String firstBrokerLoginFlowId;
|
||||
|
||||
/**
|
||||
* <p>A map containing the configuration and properties for a specific identity provider instance and implementation. The items
|
||||
* in the map are understood by the identity provider implementation.</p>
|
||||
|
@ -79,11 +73,11 @@ public class IdentityProviderModel implements Serializable {
|
|||
this.alias = model.getAlias();
|
||||
this.config = new HashMap<String, String>(model.getConfig());
|
||||
this.enabled = model.isEnabled();
|
||||
this.updateProfileFirstLoginMode = model.getUpdateProfileFirstLoginMode();
|
||||
this.trustEmail = model.isTrustEmail();
|
||||
this.storeToken = model.isStoreToken();
|
||||
this.authenticateByDefault = model.isAuthenticateByDefault();
|
||||
this.addReadTokenRoleOnCreate = model.addReadTokenRoleOnCreate;
|
||||
this.firstBrokerLoginFlowId = model.getFirstBrokerLoginFlowId();
|
||||
}
|
||||
|
||||
public String getInternalId() {
|
||||
|
@ -118,20 +112,6 @@ public class IdentityProviderModel implements Serializable {
|
|||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IdentityProviderRepresentation#getUpdateProfileFirstLoginMode()
|
||||
*/
|
||||
public String getUpdateProfileFirstLoginMode() {
|
||||
return updateProfileFirstLoginMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IdentityProviderRepresentation#setUpdateProfileFirstLoginMode(String)
|
||||
*/
|
||||
public void setUpdateProfileFirstLoginMode(String updateProfileFirstLoginMode) {
|
||||
this.updateProfileFirstLoginMode = updateProfileFirstLoginMode;
|
||||
}
|
||||
|
||||
public boolean isStoreToken() {
|
||||
return this.storeToken;
|
||||
}
|
||||
|
@ -148,6 +128,14 @@ public class IdentityProviderModel implements Serializable {
|
|||
this.authenticateByDefault = authenticateByDefault;
|
||||
}
|
||||
|
||||
public String getFirstBrokerLoginFlowId() {
|
||||
return firstBrokerLoginFlowId;
|
||||
}
|
||||
|
||||
public void setFirstBrokerLoginFlowId(String firstBrokerLoginFlowId) {
|
||||
this.firstBrokerLoginFlowId = firstBrokerLoginFlowId;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return this.config;
|
||||
}
|
||||
|
|
|
@ -30,11 +30,11 @@ public class IdentityProviderEntity {
|
|||
private String providerId;
|
||||
private String name;
|
||||
private boolean enabled;
|
||||
private String updateProfileFirstLoginMode;
|
||||
private boolean trustEmail;
|
||||
private boolean storeToken;
|
||||
protected boolean addReadTokenRoleOnCreate;
|
||||
private boolean authenticateByDefault;
|
||||
private String firstBrokerLoginFlowId;
|
||||
|
||||
private Map<String, String> config = new HashMap<String, String>();
|
||||
|
||||
|
@ -62,14 +62,6 @@ public class IdentityProviderEntity {
|
|||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getUpdateProfileFirstLoginMode() {
|
||||
return updateProfileFirstLoginMode;
|
||||
}
|
||||
|
||||
public void setUpdateProfileFirstLoginMode(String updateProfileFirstLoginMode) {
|
||||
this.updateProfileFirstLoginMode = updateProfileFirstLoginMode;
|
||||
}
|
||||
|
||||
public boolean isAuthenticateByDefault() {
|
||||
return authenticateByDefault;
|
||||
}
|
||||
|
@ -78,6 +70,14 @@ public class IdentityProviderEntity {
|
|||
this.authenticateByDefault = authenticateByDefault;
|
||||
}
|
||||
|
||||
public String getFirstBrokerLoginFlowId() {
|
||||
return firstBrokerLoginFlowId;
|
||||
}
|
||||
|
||||
public void setFirstBrokerLoginFlowId(String firstBrokerLoginFlowId) {
|
||||
this.firstBrokerLoginFlowId = firstBrokerLoginFlowId;
|
||||
}
|
||||
|
||||
public boolean isStoreToken() {
|
||||
return this.storeToken;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
package org.keycloak.models.utils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -19,6 +24,9 @@ public class DefaultAuthenticationFlows {
|
|||
public static final String LOGIN_FORMS_FLOW = "forms";
|
||||
|
||||
public static final String CLIENT_AUTHENTICATION_FLOW = "clients";
|
||||
public static final String FIRST_BROKER_LOGIN_FLOW = "first broker login";
|
||||
|
||||
public static final String IDP_REVIEW_PROFILE_CONFIG_ALIAS = "review profile config";
|
||||
|
||||
public static void addFlows(RealmModel realm) {
|
||||
if (realm.getFlowByAlias(BROWSER_FLOW) == null) browserFlow(realm);
|
||||
|
@ -26,6 +34,7 @@ public class DefaultAuthenticationFlows {
|
|||
if (realm.getFlowByAlias(REGISTRATION_FLOW) == null) registrationFlow(realm);
|
||||
if (realm.getFlowByAlias(RESET_CREDENTIALS_FLOW) == null) resetCredentialsFlow(realm);
|
||||
if (realm.getFlowByAlias(CLIENT_AUTHENTICATION_FLOW) == null) clientAuthFlow(realm);
|
||||
if (realm.getFlowByAlias(FIRST_BROKER_LOGIN_FLOW) == null) firstBrokerLoginFlow(realm);
|
||||
}
|
||||
public static void migrateFlows(RealmModel realm) {
|
||||
if (realm.getFlowByAlias(BROWSER_FLOW) == null) browserFlow(realm, true);
|
||||
|
@ -33,6 +42,7 @@ public class DefaultAuthenticationFlows {
|
|||
if (realm.getFlowByAlias(REGISTRATION_FLOW) == null) registrationFlow(realm);
|
||||
if (realm.getFlowByAlias(RESET_CREDENTIALS_FLOW) == null) resetCredentialsFlow(realm);
|
||||
if (realm.getFlowByAlias(CLIENT_AUTHENTICATION_FLOW) == null) clientAuthFlow(realm);
|
||||
if (realm.getFlowByAlias(FIRST_BROKER_LOGIN_FLOW) == null) firstBrokerLoginFlow(realm);
|
||||
}
|
||||
|
||||
public static void registrationFlow(RealmModel realm) {
|
||||
|
@ -309,4 +319,115 @@ public class DefaultAuthenticationFlows {
|
|||
execution.setAuthenticatorFlow(false);
|
||||
realm.addAuthenticatorExecution(execution);
|
||||
}
|
||||
|
||||
public static void firstBrokerLoginFlow(RealmModel realm) {
|
||||
AuthenticationFlowModel firstBrokerLogin = new AuthenticationFlowModel();
|
||||
firstBrokerLogin.setAlias(FIRST_BROKER_LOGIN_FLOW);
|
||||
firstBrokerLogin.setDescription("Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account");
|
||||
firstBrokerLogin.setProviderId("basic-flow");
|
||||
firstBrokerLogin.setTopLevel(true);
|
||||
firstBrokerLogin.setBuiltIn(true);
|
||||
firstBrokerLogin = realm.addAuthenticationFlow(firstBrokerLogin);
|
||||
|
||||
AuthenticatorConfigModel reviewProfileConfig = new AuthenticatorConfigModel();
|
||||
reviewProfileConfig.setAlias(IDP_REVIEW_PROFILE_CONFIG_ALIAS);
|
||||
Map<String, String> config = new HashMap<>();
|
||||
config.put("update.profile.on.first.login", IdentityProviderRepresentation.UPFLM_MISSING);
|
||||
reviewProfileConfig.setConfig(config);
|
||||
reviewProfileConfig = realm.addAuthenticatorConfig(reviewProfileConfig);
|
||||
|
||||
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
|
||||
execution.setParentFlow(firstBrokerLogin.getId());
|
||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
|
||||
execution.setAuthenticator("idp-review-profile");
|
||||
execution.setPriority(10);
|
||||
execution.setAuthenticatorFlow(false);
|
||||
execution.setAuthenticatorConfig(reviewProfileConfig.getId());
|
||||
realm.addAuthenticatorExecution(execution);
|
||||
|
||||
|
||||
AuthenticatorConfigModel createUserIfUniqueConfig = new AuthenticatorConfigModel();
|
||||
createUserIfUniqueConfig.setAlias("create unique user config");
|
||||
config = new HashMap<>();
|
||||
config.put("require.password.update.after.registration", "false");
|
||||
createUserIfUniqueConfig.setConfig(config);
|
||||
createUserIfUniqueConfig = realm.addAuthenticatorConfig(createUserIfUniqueConfig);
|
||||
|
||||
execution = new AuthenticationExecutionModel();
|
||||
execution.setParentFlow(firstBrokerLogin.getId());
|
||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
|
||||
execution.setAuthenticator("idp-create-user-if-unique");
|
||||
execution.setPriority(20);
|
||||
execution.setAuthenticatorFlow(false);
|
||||
execution.setAuthenticatorConfig(createUserIfUniqueConfig.getId());
|
||||
realm.addAuthenticatorExecution(execution);
|
||||
|
||||
|
||||
AuthenticationFlowModel linkExistingAccountFlow = new AuthenticationFlowModel();
|
||||
linkExistingAccountFlow.setTopLevel(false);
|
||||
linkExistingAccountFlow.setBuiltIn(true);
|
||||
linkExistingAccountFlow.setAlias("Handle Existing Account");
|
||||
linkExistingAccountFlow.setDescription("Handle what to do if there is existing account with same email/username like authenticated identity provider");
|
||||
linkExistingAccountFlow.setProviderId("basic-flow");
|
||||
linkExistingAccountFlow = realm.addAuthenticationFlow(linkExistingAccountFlow);
|
||||
execution = new AuthenticationExecutionModel();
|
||||
execution.setParentFlow(firstBrokerLogin.getId());
|
||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
|
||||
execution.setFlowId(linkExistingAccountFlow.getId());
|
||||
execution.setPriority(30);
|
||||
execution.setAuthenticatorFlow(true);
|
||||
realm.addAuthenticatorExecution(execution);
|
||||
|
||||
execution = new AuthenticationExecutionModel();
|
||||
execution.setParentFlow(linkExistingAccountFlow.getId());
|
||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
|
||||
execution.setAuthenticator("idp-confirm-link");
|
||||
execution.setPriority(10);
|
||||
execution.setAuthenticatorFlow(false);
|
||||
realm.addAuthenticatorExecution(execution);
|
||||
|
||||
execution = new AuthenticationExecutionModel();
|
||||
execution.setParentFlow(linkExistingAccountFlow.getId());
|
||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
|
||||
execution.setAuthenticator("idp-email-verification");
|
||||
execution.setPriority(20);
|
||||
execution.setAuthenticatorFlow(false);
|
||||
realm.addAuthenticatorExecution(execution);
|
||||
|
||||
AuthenticationFlowModel verifyByReauthenticationAccountFlow = new AuthenticationFlowModel();
|
||||
verifyByReauthenticationAccountFlow.setTopLevel(false);
|
||||
verifyByReauthenticationAccountFlow.setBuiltIn(true);
|
||||
verifyByReauthenticationAccountFlow.setAlias("Verify Existing Account by Re-authentication");
|
||||
verifyByReauthenticationAccountFlow.setDescription("Reauthentication of existing account");
|
||||
verifyByReauthenticationAccountFlow.setProviderId("basic-flow");
|
||||
verifyByReauthenticationAccountFlow = realm.addAuthenticationFlow(verifyByReauthenticationAccountFlow);
|
||||
execution = new AuthenticationExecutionModel();
|
||||
execution.setParentFlow(linkExistingAccountFlow.getId());
|
||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
|
||||
execution.setFlowId(verifyByReauthenticationAccountFlow.getId());
|
||||
execution.setPriority(30);
|
||||
execution.setAuthenticatorFlow(true);
|
||||
realm.addAuthenticatorExecution(execution);
|
||||
|
||||
// password + otp
|
||||
execution = new AuthenticationExecutionModel();
|
||||
execution.setParentFlow(verifyByReauthenticationAccountFlow.getId());
|
||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
|
||||
execution.setAuthenticator("idp-username-password-form");
|
||||
execution.setPriority(10);
|
||||
execution.setAuthenticatorFlow(false);
|
||||
realm.addAuthenticatorExecution(execution);
|
||||
|
||||
execution = new AuthenticationExecutionModel();
|
||||
execution.setParentFlow(verifyByReauthenticationAccountFlow.getId());
|
||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.OPTIONAL);
|
||||
// TODO: read the requirement from browser authenticator
|
||||
// if (migrate && hasCredentialType(realm, RequiredCredentialModel.TOTP.getType())) {
|
||||
// execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
|
||||
// }
|
||||
execution.setAuthenticator("auth-otp-form");
|
||||
execution.setPriority(20);
|
||||
execution.setAuthenticatorFlow(false);
|
||||
realm.addAuthenticatorExecution(execution);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.bouncycastle.openssl.PEMWriter;
|
|||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.KeycloakSessionTask;
|
||||
|
@ -199,9 +200,9 @@ public final class KeycloakModelUtils {
|
|||
/**
|
||||
* Deep search if given role is descendant of composite role
|
||||
*
|
||||
* @param role role to check
|
||||
* @param role role to check
|
||||
* @param composite composite role
|
||||
* @param visited set of already visited roles (used for recursion)
|
||||
* @param visited set of already visited roles (used for recursion)
|
||||
* @return true if "role" is descendant of "composite"
|
||||
*/
|
||||
public static boolean searchFor(RoleModel role, RoleModel composite, Set<RoleModel> visited) {
|
||||
|
@ -219,14 +220,14 @@ public final class KeycloakModelUtils {
|
|||
/**
|
||||
* Try to find user by given username. If it fails, then fallback to find him by email
|
||||
*
|
||||
* @param realm realm
|
||||
* @param realm realm
|
||||
* @param username username or email of user
|
||||
* @return found user
|
||||
*/
|
||||
public static UserModel findUserByNameOrEmail(KeycloakSession session, RealmModel realm, String username) {
|
||||
UserModel user = session.users().getUserByUsername(username, realm);
|
||||
if (user == null && username.contains("@")) {
|
||||
user = session.users().getUserByEmail(username, realm);
|
||||
user = session.users().getUserByEmail(username, realm);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
@ -266,7 +267,6 @@ public final class KeycloakModelUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param roles
|
||||
* @param targetRole
|
||||
* @return true if targetRole is in roles (directly or indirectly via composite role)
|
||||
|
@ -303,8 +303,8 @@ public final class KeycloakModelUtils {
|
|||
/**
|
||||
* Ensure that displayName of myProvider (if not null) is unique and there is no other provider with same displayName in the list.
|
||||
*
|
||||
* @param displayName to check for duplications
|
||||
* @param myProvider provider, which is excluded from the list (if present)
|
||||
* @param displayName to check for duplications
|
||||
* @param myProvider provider, which is excluded from the list (if present)
|
||||
* @param federationProviders
|
||||
* @throws ModelDuplicateException if there is other provider with same displayName
|
||||
*/
|
||||
|
|
|
@ -257,7 +257,7 @@ public class ModelToRepresentation {
|
|||
}
|
||||
|
||||
for (IdentityProviderModel provider : realm.getIdentityProviders()) {
|
||||
rep.addIdentityProvider(toRepresentation(provider));
|
||||
rep.addIdentityProvider(toRepresentation(realm, provider));
|
||||
}
|
||||
|
||||
for (IdentityProviderMapperModel mapper : realm.getIdentityProviderMappers()) {
|
||||
|
@ -436,7 +436,7 @@ public class ModelToRepresentation {
|
|||
return rep;
|
||||
}
|
||||
|
||||
public static IdentityProviderRepresentation toRepresentation(IdentityProviderModel identityProviderModel) {
|
||||
public static IdentityProviderRepresentation toRepresentation(RealmModel realm, IdentityProviderModel identityProviderModel) {
|
||||
IdentityProviderRepresentation providerRep = new IdentityProviderRepresentation();
|
||||
|
||||
providerRep.setInternalId(identityProviderModel.getInternalId());
|
||||
|
@ -444,12 +444,20 @@ public class ModelToRepresentation {
|
|||
providerRep.setAlias(identityProviderModel.getAlias());
|
||||
providerRep.setEnabled(identityProviderModel.isEnabled());
|
||||
providerRep.setStoreToken(identityProviderModel.isStoreToken());
|
||||
providerRep.setUpdateProfileFirstLoginMode(identityProviderModel.getUpdateProfileFirstLoginMode());
|
||||
providerRep.setTrustEmail(identityProviderModel.isTrustEmail());
|
||||
providerRep.setAuthenticateByDefault(identityProviderModel.isAuthenticateByDefault());
|
||||
providerRep.setConfig(identityProviderModel.getConfig());
|
||||
providerRep.setAddReadTokenRoleOnCreate(identityProviderModel.isAddReadTokenRoleOnCreate());
|
||||
|
||||
String firstBrokerLoginFlowId = identityProviderModel.getFirstBrokerLoginFlowId();
|
||||
if (firstBrokerLoginFlowId != null) {
|
||||
AuthenticationFlowModel flow = realm.getAuthenticationFlowById(firstBrokerLoginFlowId);
|
||||
if (flow == null) {
|
||||
throw new ModelException("Couldn't find authentication flow with id " + firstBrokerLoginFlowId);
|
||||
}
|
||||
providerRep.setFirstBrokerLoginFlowAlias(flow.getAlias());
|
||||
}
|
||||
|
||||
return providerRep;
|
||||
}
|
||||
|
||||
|
|
|
@ -164,6 +164,16 @@ public class RepresentationToModel {
|
|||
if (rep.getOtpPolicyType() != null) newRealm.setOTPPolicy(toPolicy(rep));
|
||||
else newRealm.setOTPPolicy(OTPPolicy.DEFAULT_POLICY);
|
||||
|
||||
importAuthenticationFlows(newRealm, rep);
|
||||
if (rep.getRequiredActions() != null) {
|
||||
for (RequiredActionProviderRepresentation action : rep.getRequiredActions()) {
|
||||
RequiredActionProviderModel model = toModel(action);
|
||||
newRealm.addRequiredActionProvider(model);
|
||||
}
|
||||
} else {
|
||||
DefaultRequiredActions.addActions(newRealm);
|
||||
}
|
||||
|
||||
importIdentityProviders(rep, newRealm);
|
||||
importIdentityProviderMappers(rep, newRealm);
|
||||
|
||||
|
@ -318,16 +328,6 @@ public class RepresentationToModel {
|
|||
if(rep.getDefaultLocale() != null){
|
||||
newRealm.setDefaultLocale(rep.getDefaultLocale());
|
||||
}
|
||||
|
||||
importAuthenticationFlows(newRealm, rep);
|
||||
if (rep.getRequiredActions() != null) {
|
||||
for (RequiredActionProviderRepresentation action : rep.getRequiredActions()) {
|
||||
RequiredActionProviderModel model = toModel(action);
|
||||
newRealm.addRequiredActionProvider(model);
|
||||
}
|
||||
} else {
|
||||
DefaultRequiredActions.addActions(newRealm);
|
||||
}
|
||||
}
|
||||
|
||||
public static void importAuthenticationFlows(RealmModel newRealm, RealmRepresentation rep) {
|
||||
|
@ -1062,7 +1062,7 @@ public class RepresentationToModel {
|
|||
private static void importIdentityProviders(RealmRepresentation rep, RealmModel newRealm) {
|
||||
if (rep.getIdentityProviders() != null) {
|
||||
for (IdentityProviderRepresentation representation : rep.getIdentityProviders()) {
|
||||
newRealm.addIdentityProvider(toModel(representation));
|
||||
newRealm.addIdentityProvider(toModel(newRealm, representation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1073,21 +1073,31 @@ public class RepresentationToModel {
|
|||
}
|
||||
}
|
||||
}
|
||||
public static IdentityProviderModel toModel(IdentityProviderRepresentation representation) {
|
||||
public static IdentityProviderModel toModel(RealmModel realm, IdentityProviderRepresentation representation) {
|
||||
IdentityProviderModel identityProviderModel = new IdentityProviderModel();
|
||||
|
||||
identityProviderModel.setInternalId(representation.getInternalId());
|
||||
identityProviderModel.setAlias(representation.getAlias());
|
||||
identityProviderModel.setProviderId(representation.getProviderId());
|
||||
identityProviderModel.setEnabled(representation.isEnabled());
|
||||
identityProviderModel.setUpdateProfileFirstLoginMode(representation.getUpdateProfileFirstLoginMode());
|
||||
identityProviderModel.setTrustEmail(representation.isTrustEmail());
|
||||
identityProviderModel.setAuthenticateByDefault(representation.isAuthenticateByDefault());
|
||||
identityProviderModel.setStoreToken(representation.isStoreToken());
|
||||
identityProviderModel.setAddReadTokenRoleOnCreate(representation.isAddReadTokenRoleOnCreate());
|
||||
identityProviderModel.setConfig(representation.getConfig());
|
||||
|
||||
return identityProviderModel;
|
||||
String flowAlias = representation.getFirstBrokerLoginFlowAlias();
|
||||
if (flowAlias == null) {
|
||||
flowAlias = DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW;
|
||||
}
|
||||
|
||||
AuthenticationFlowModel flowModel = realm.getFlowByAlias(flowAlias);
|
||||
if (flowModel == null) {
|
||||
throw new ModelException("No available authentication flow with alias: " + flowAlias);
|
||||
}
|
||||
identityProviderModel.setFirstBrokerLoginFlowId(flowModel.getId());
|
||||
|
||||
return identityProviderModel;
|
||||
}
|
||||
|
||||
public static ProtocolMapperModel toModel(ProtocolMapperRepresentation rep) {
|
||||
|
|
|
@ -1218,9 +1218,9 @@ public class RealmAdapter implements RealmModel {
|
|||
identityProviderModel.setInternalId(entity.getInternalId());
|
||||
identityProviderModel.setConfig(entity.getConfig());
|
||||
identityProviderModel.setEnabled(entity.isEnabled());
|
||||
identityProviderModel.setUpdateProfileFirstLoginMode(entity.getUpdateProfileFirstLoginMode());
|
||||
identityProviderModel.setTrustEmail(entity.isTrustEmail());
|
||||
identityProviderModel.setAuthenticateByDefault(entity.isAuthenticateByDefault());
|
||||
identityProviderModel.setFirstBrokerLoginFlowId(entity.getFirstBrokerLoginFlowId());
|
||||
identityProviderModel.setStoreToken(entity.isStoreToken());
|
||||
identityProviderModel.setAddReadTokenRoleOnCreate(entity.isAddReadTokenRoleOnCreate());
|
||||
|
||||
|
@ -1251,9 +1251,9 @@ public class RealmAdapter implements RealmModel {
|
|||
entity.setEnabled(identityProvider.isEnabled());
|
||||
entity.setStoreToken(identityProvider.isStoreToken());
|
||||
entity.setAddReadTokenRoleOnCreate(identityProvider.isAddReadTokenRoleOnCreate());
|
||||
entity.setUpdateProfileFirstLoginMode(identityProvider.getUpdateProfileFirstLoginMode());
|
||||
entity.setTrustEmail(identityProvider.isTrustEmail());
|
||||
entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
|
||||
entity.setFirstBrokerLoginFlowId(identityProvider.getFirstBrokerLoginFlowId());
|
||||
entity.setConfig(identityProvider.getConfig());
|
||||
|
||||
realm.addIdentityProvider(entity);
|
||||
|
@ -1278,9 +1278,9 @@ public class RealmAdapter implements RealmModel {
|
|||
if (entity.getInternalId().equals(identityProvider.getInternalId())) {
|
||||
entity.setAlias(identityProvider.getAlias());
|
||||
entity.setEnabled(identityProvider.isEnabled());
|
||||
entity.setUpdateProfileFirstLoginMode(identityProvider.getUpdateProfileFirstLoginMode());
|
||||
entity.setTrustEmail(identityProvider.isTrustEmail());
|
||||
entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
|
||||
entity.setFirstBrokerLoginFlowId(identityProvider.getFirstBrokerLoginFlowId());
|
||||
entity.setAddReadTokenRoleOnCreate(identityProvider.isAddReadTokenRoleOnCreate());
|
||||
entity.setStoreToken(identityProvider.isStoreToken());
|
||||
entity.setConfig(identityProvider.getConfig());
|
||||
|
|
|
@ -11,6 +11,7 @@ import javax.persistence.ManyToOne;
|
|||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -41,9 +42,6 @@ public class IdentityProviderEntity {
|
|||
@Column(name="ENABLED")
|
||||
private boolean enabled;
|
||||
|
||||
@Column(name = "UPDATE_PROFILE_FIRST_LGN_MD")
|
||||
private String updateProfileFirstLoginMode;
|
||||
|
||||
@Column(name = "TRUST_EMAIL")
|
||||
private boolean trustEmail;
|
||||
|
||||
|
@ -56,6 +54,9 @@ public class IdentityProviderEntity {
|
|||
@Column(name="AUTHENTICATE_BY_DEFAULT")
|
||||
private boolean authenticateByDefault;
|
||||
|
||||
@Column(name="FIRST_BROKER_LOGIN_FLOW_ID")
|
||||
private String firstBrokerLoginFlowId;
|
||||
|
||||
@ElementCollection
|
||||
@MapKeyColumn(name="NAME")
|
||||
@Column(name="VALUE", columnDefinition = "TEXT")
|
||||
|
@ -102,14 +103,6 @@ public class IdentityProviderEntity {
|
|||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getUpdateProfileFirstLoginMode() {
|
||||
return updateProfileFirstLoginMode;
|
||||
}
|
||||
|
||||
public void setUpdateProfileFirstLoginMode(String updateProfileFirstLoginMode) {
|
||||
this.updateProfileFirstLoginMode = updateProfileFirstLoginMode;
|
||||
}
|
||||
|
||||
public boolean isStoreToken() {
|
||||
return this.storeToken;
|
||||
}
|
||||
|
@ -126,6 +119,14 @@ public class IdentityProviderEntity {
|
|||
this.authenticateByDefault = authenticateByDefault;
|
||||
}
|
||||
|
||||
public String getFirstBrokerLoginFlowId() {
|
||||
return firstBrokerLoginFlowId;
|
||||
}
|
||||
|
||||
public void setFirstBrokerLoginFlowId(String firstBrokerLoginFlowId) {
|
||||
this.firstBrokerLoginFlowId = firstBrokerLoginFlowId;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return this.config;
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ public class RealmEntity {
|
|||
Collection<RequiredCredentialEntity> requiredCredentials = new ArrayList<RequiredCredentialEntity>();
|
||||
|
||||
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
|
||||
@JoinTable(name="FED_PROVIDERS", joinColumns={ @JoinColumn(name="REALM_ID") })
|
||||
@JoinTable(name="FED_PROVIDERS", joinColumns={ @JoinColumn(name="REALM_ID") }, inverseJoinColumns={ @JoinColumn(name = "USERFEDERATIONPROVIDERS_ID") })
|
||||
List<UserFederationProviderEntity> userFederationProviders = new ArrayList<UserFederationProviderEntity>();
|
||||
|
||||
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
|
||||
|
|
|
@ -30,7 +30,7 @@ import java.util.Collection;
|
|||
|
||||
public class RoleEntity {
|
||||
@Id
|
||||
@Column(name="id", length = 36)
|
||||
@Column(name="ID", length = 36)
|
||||
private String id;
|
||||
|
||||
@Column(name = "NAME")
|
||||
|
|
|
@ -77,7 +77,7 @@ public class UserEntity {
|
|||
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="user")
|
||||
protected Collection<CredentialEntity> credentials = new ArrayList<CredentialEntity>();
|
||||
|
||||
@Column(name="federation_link")
|
||||
@Column(name="FEDERATION_LINK")
|
||||
protected String federationLink;
|
||||
|
||||
@Column(name="SERVICE_ACCOUNT_CLIENT_LINK")
|
||||
|
|
|
@ -35,7 +35,7 @@ public class UserFederationProviderEntity {
|
|||
|
||||
@ElementCollection
|
||||
@MapKeyColumn(name="name")
|
||||
@Column(name="value")
|
||||
@Column(name="VALUE")
|
||||
@CollectionTable(name="USER_FEDERATION_CONFIG", joinColumns={ @JoinColumn(name="USER_FEDERATION_PROVIDER_ID") })
|
||||
private Map<String, String> config;
|
||||
|
||||
|
|
|
@ -898,9 +898,9 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
identityProviderModel.setInternalId(entity.getInternalId());
|
||||
identityProviderModel.setConfig(entity.getConfig());
|
||||
identityProviderModel.setEnabled(entity.isEnabled());
|
||||
identityProviderModel.setUpdateProfileFirstLoginMode(entity.getUpdateProfileFirstLoginMode());
|
||||
identityProviderModel.setTrustEmail(entity.isTrustEmail());
|
||||
identityProviderModel.setAuthenticateByDefault(entity.isAuthenticateByDefault());
|
||||
identityProviderModel.setFirstBrokerLoginFlowId(entity.getFirstBrokerLoginFlowId());
|
||||
identityProviderModel.setStoreToken(entity.isStoreToken());
|
||||
identityProviderModel.setAddReadTokenRoleOnCreate(entity.isAddReadTokenRoleOnCreate());
|
||||
|
||||
|
@ -929,11 +929,11 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
entity.setAlias(identityProvider.getAlias());
|
||||
entity.setProviderId(identityProvider.getProviderId());
|
||||
entity.setEnabled(identityProvider.isEnabled());
|
||||
entity.setUpdateProfileFirstLoginMode(identityProvider.getUpdateProfileFirstLoginMode());
|
||||
entity.setTrustEmail(identityProvider.isTrustEmail());
|
||||
entity.setAddReadTokenRoleOnCreate(identityProvider.isAddReadTokenRoleOnCreate());
|
||||
entity.setStoreToken(identityProvider.isStoreToken());
|
||||
entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
|
||||
entity.setFirstBrokerLoginFlowId(identityProvider.getFirstBrokerLoginFlowId());
|
||||
entity.setConfig(identityProvider.getConfig());
|
||||
|
||||
realm.getIdentityProviders().add(entity);
|
||||
|
@ -957,9 +957,9 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
if (entity.getInternalId().equals(identityProvider.getInternalId())) {
|
||||
entity.setAlias(identityProvider.getAlias());
|
||||
entity.setEnabled(identityProvider.isEnabled());
|
||||
entity.setUpdateProfileFirstLoginMode(identityProvider.getUpdateProfileFirstLoginMode());
|
||||
entity.setTrustEmail(identityProvider.isTrustEmail());
|
||||
entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
|
||||
entity.setFirstBrokerLoginFlowId(identityProvider.getFirstBrokerLoginFlowId());
|
||||
entity.setAddReadTokenRoleOnCreate(identityProvider.isAddReadTokenRoleOnCreate());
|
||||
entity.setStoreToken(identityProvider.isStoreToken());
|
||||
entity.setConfig(identityProvider.getConfig());
|
||||
|
|
|
@ -32,11 +32,6 @@
|
|||
<groupId>org.apache.santuario</groupId>
|
||||
<artifactId>xmlsec</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<resources>
|
||||
|
|
|
@ -76,6 +76,11 @@ public abstract class AbstractParser implements ParserNamespaceSupport {
|
|||
* @throws {@link IllegalArgumentException} when the configStream is null
|
||||
*/
|
||||
public Object parse(InputStream configStream) throws ParsingException {
|
||||
XMLEventReader xmlEventReader = createEventReader(configStream);
|
||||
return parse(xmlEventReader);
|
||||
}
|
||||
|
||||
public XMLEventReader createEventReader(InputStream configStream) throws ParsingException {
|
||||
if (configStream == null)
|
||||
throw logger.nullArgumentError("InputStream");
|
||||
|
||||
|
@ -105,7 +110,7 @@ public abstract class AbstractParser implements ParserNamespaceSupport {
|
|||
throw logger.parserException(e);
|
||||
}
|
||||
|
||||
return parse(xmlEventReader);
|
||||
return xmlEventReader;
|
||||
}
|
||||
|
||||
private ClassLoader getTCCL() {
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source
|
||||
*
|
||||
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.saml.common.util;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.PBEParameterSpec;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* Utility dealing with Password Based Encryption (Code is ripped off of the PBEUtils class in JBossSecurity/PicketBox)
|
||||
*
|
||||
* @author Scott.Stark@jboss.org
|
||||
* @author Anil.Saldhana@redhat.com
|
||||
* @since May 25, 2010
|
||||
*/
|
||||
public class PBEUtils {
|
||||
public static byte[] encode(byte[] secret, String cipherAlgorithm, SecretKey cipherKey, PBEParameterSpec cipherSpec)
|
||||
throws Exception {
|
||||
Cipher cipher = Cipher.getInstance(cipherAlgorithm);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, cipherKey, cipherSpec);
|
||||
byte[] encoding = cipher.doFinal(secret);
|
||||
return encoding;
|
||||
}
|
||||
|
||||
public static String encode64(byte[] secret, String cipherAlgorithm, SecretKey cipherKey, PBEParameterSpec cipherSpec)
|
||||
throws Exception {
|
||||
byte[] encoding = encode(secret, cipherAlgorithm, cipherKey, cipherSpec);
|
||||
String b64 = Base64.encodeBytes(encoding);
|
||||
return b64;
|
||||
}
|
||||
|
||||
public static byte[] decode(byte[] secret, String cipherAlgorithm, SecretKey cipherKey, PBEParameterSpec cipherSpec)
|
||||
throws GeneralSecurityException {
|
||||
Cipher cipher = Cipher.getInstance(cipherAlgorithm);
|
||||
cipher.init(Cipher.DECRYPT_MODE, cipherKey, cipherSpec);
|
||||
byte[] decode = cipher.doFinal(secret);
|
||||
return decode;
|
||||
}
|
||||
|
||||
public static String decode64(String secret, String cipherAlgorithm, SecretKey cipherKey, PBEParameterSpec cipherSpec)
|
||||
throws GeneralSecurityException, UnsupportedEncodingException {
|
||||
byte[] encoding = Base64.decode(secret);
|
||||
byte[] decode = decode(encoding, cipherAlgorithm, cipherKey, cipherSpec);
|
||||
return new String(decode, "UTF-8");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length != 3) {
|
||||
System.err.println("Encrypt a password" + "Usage: PBEUtils salt count domain-password password"
|
||||
+ " salt : the Salt " + " count : the IterationCount "
|
||||
+ " password : the plaintext password that should be encrypted");
|
||||
throw new RuntimeException(" ERROR: please see format above");
|
||||
}
|
||||
|
||||
byte[] salt = args[0].substring(0, 8).getBytes();
|
||||
int count = Integer.parseInt(args[1]);
|
||||
char[] password = "somearbitrarycrazystringthatdoesnotmatter".toCharArray();
|
||||
byte[] passwordToEncode = args[2].getBytes("UTF-8");
|
||||
PBEParameterSpec cipherSpec = new PBEParameterSpec(salt, count);
|
||||
PBEKeySpec keySpec = new PBEKeySpec(password);
|
||||
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEwithMD5andDES");
|
||||
SecretKey cipherKey = factory.generateSecret(keySpec);
|
||||
String encodedPassword = encode64(passwordToEncode, "PBEwithMD5andDES", cipherKey, cipherSpec);
|
||||
System.err.println("Encoded password: MASK-" + encodedPassword);
|
||||
}
|
||||
}
|
|
@ -180,36 +180,6 @@ public class StringUtil {
|
|||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a masked password {@link String}, decode it
|
||||
*
|
||||
* @param maskedString a password string that is masked
|
||||
* @param salt Salt
|
||||
* @param iterationCount Iteration Count
|
||||
*
|
||||
* @return Decoded String
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String decode(String maskedString, String salt, int iterationCount) throws Exception {
|
||||
String pbeAlgo = PicketLinkCommonConstants.PBE_ALGORITHM;
|
||||
if (maskedString.startsWith(PicketLinkCommonConstants.PASS_MASK_PREFIX)) {
|
||||
// Create the PBE secret key
|
||||
SecretKeyFactory factory = SecretKeyFactory.getInstance(pbeAlgo);
|
||||
|
||||
char[] password = "somearbitrarycrazystringthatdoesnotmatter".toCharArray();
|
||||
PBEParameterSpec cipherSpec = new PBEParameterSpec(salt.getBytes(), iterationCount);
|
||||
PBEKeySpec keySpec = new PBEKeySpec(password);
|
||||
SecretKey cipherKey = factory.generateSecret(keySpec);
|
||||
|
||||
maskedString = maskedString.substring(PicketLinkCommonConstants.PASS_MASK_PREFIX.length());
|
||||
String decodedValue = PBEUtils.decode64(maskedString, pbeAlgo, cipherKey, cipherSpec);
|
||||
|
||||
maskedString = decodedValue;
|
||||
}
|
||||
return maskedString;
|
||||
}
|
||||
|
||||
public static String[] split(String toSplit, String delimiter) {
|
||||
if (delimiter.length() != 1) {
|
||||
throw new IllegalArgumentException("Delimiter can only be one character in length");
|
||||
|
|
|
@ -136,7 +136,7 @@ public class SAMLAssertionWriter extends BaseWriter {
|
|||
if (statements != null) {
|
||||
for (StatementAbstractType statement : statements) {
|
||||
if (statement instanceof AuthnStatementType) {
|
||||
write((AuthnStatementType) statement);
|
||||
write((AuthnStatementType) statement, false);
|
||||
} else if (statement instanceof AttributeStatementType) {
|
||||
write((AttributeStatementType) statement);
|
||||
} else
|
||||
|
@ -188,8 +188,12 @@ public class SAMLAssertionWriter extends BaseWriter {
|
|||
*
|
||||
* @throws ProcessingException
|
||||
*/
|
||||
public void write(AuthnStatementType authnStatement) throws ProcessingException {
|
||||
public void write(AuthnStatementType authnStatement, boolean includeNamespace) throws ProcessingException {
|
||||
StaxUtil.writeStartElement(writer, ASSERTION_PREFIX, JBossSAMLConstants.AUTHN_STATEMENT.get(), ASSERTION_NSURI.get());
|
||||
if (includeNamespace) {
|
||||
StaxUtil.writeNameSpace(writer, ASSERTION_PREFIX, ASSERTION_NSURI.get());
|
||||
StaxUtil.writeDefaultNameSpace(writer, ASSERTION_NSURI.get());
|
||||
}
|
||||
|
||||
XMLGregorianCalendar authnInstant = authnStatement.getAuthnInstant();
|
||||
if (authnInstant != null) {
|
||||
|
|
|
@ -22,5 +22,9 @@ public enum AuthenticationFlowError {
|
|||
CLIENT_NOT_FOUND,
|
||||
CLIENT_DISABLED,
|
||||
CLIENT_CREDENTIALS_SETUP_REQUIRED,
|
||||
INVALID_CLIENT_CREDENTIALS
|
||||
INVALID_CLIENT_CREDENTIALS,
|
||||
|
||||
IDENTITY_PROVIDER_NOT_FOUND,
|
||||
IDENTITY_PROVIDER_DISABLED,
|
||||
IDENTITY_PROVIDER_ERROR
|
||||
}
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
package org.keycloak.authentication.authenticators.broker;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||
import org.keycloak.authentication.AuthenticationFlowError;
|
||||
import org.keycloak.authentication.AuthenticationFlowException;
|
||||
import org.keycloak.authentication.Authenticator;
|
||||
import org.keycloak.authentication.authenticators.broker.util.ExistingUserInfo;
|
||||
import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public abstract class AbstractIdpAuthenticator implements Authenticator {
|
||||
|
||||
// The clientSession note encapsulating all the BrokeredIdentityContext info. When this note is in clientSession, we know that firstBrokerLogin flow is in progress
|
||||
public static final String BROKERED_CONTEXT_NOTE = "BROKERED_CONTEXT";
|
||||
|
||||
// The clientSession note with all the info about existing user
|
||||
public static final String EXISTING_USER_INFO = "EXISTING_USER_INFO";
|
||||
|
||||
// The clientSession note flag to indicate that email provided by identityProvider was changed on updateProfile page
|
||||
public static final String UPDATE_PROFILE_EMAIL_CHANGED = "UPDATE_PROFILE_EMAIL_CHANGED";
|
||||
|
||||
// The clientSession note flag to indicate if re-authentication after first broker login happened in different browser window. This can happen for example during email verification
|
||||
public static final String IS_DIFFERENT_BROWSER = "IS_DIFFERENT_BROWSER";
|
||||
|
||||
// The clientSession note flag to indicate that updateProfile page will be always displayed even if "updateProfileOnFirstLogin" is off
|
||||
public static final String ENFORCE_UPDATE_PROFILE = "ENFORCE_UPDATE_PROFILE";
|
||||
|
||||
// clientSession.note flag specifies if we imported new user to keycloak (true) or we just linked to an existing keycloak user (false)
|
||||
public static final String BROKER_REGISTERED_NEW_USER = "BROKER_REGISTERED_NEW_USER";
|
||||
|
||||
|
||||
@Override
|
||||
public void authenticate(AuthenticationFlowContext context) {
|
||||
ClientSessionModel clientSession = context.getClientSession();
|
||||
|
||||
SerializedBrokeredIdentityContext serializedCtx = SerializedBrokeredIdentityContext.readFromClientSession(clientSession);
|
||||
if (serializedCtx == null) {
|
||||
throw new AuthenticationFlowException("Not found serialized context in clientSession", AuthenticationFlowError.IDENTITY_PROVIDER_ERROR);
|
||||
}
|
||||
BrokeredIdentityContext brokerContext = serializedCtx.deserialize(context.getSession(), clientSession);
|
||||
|
||||
if (!brokerContext.getIdpConfig().isEnabled()) {
|
||||
sendFailureChallenge(context, Errors.IDENTITY_PROVIDER_ERROR, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR, AuthenticationFlowError.IDENTITY_PROVIDER_ERROR);
|
||||
}
|
||||
|
||||
authenticateImpl(context, serializedCtx, brokerContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void action(AuthenticationFlowContext context) {
|
||||
ClientSessionModel clientSession = context.getClientSession();
|
||||
|
||||
SerializedBrokeredIdentityContext serializedCtx = SerializedBrokeredIdentityContext.readFromClientSession(clientSession);
|
||||
if (serializedCtx == null) {
|
||||
throw new AuthenticationFlowException("Not found serialized context in clientSession", AuthenticationFlowError.IDENTITY_PROVIDER_ERROR);
|
||||
}
|
||||
BrokeredIdentityContext brokerContext = serializedCtx.deserialize(context.getSession(), clientSession);
|
||||
|
||||
if (!brokerContext.getIdpConfig().isEnabled()) {
|
||||
sendFailureChallenge(context, Errors.IDENTITY_PROVIDER_ERROR, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR, AuthenticationFlowError.IDENTITY_PROVIDER_ERROR);
|
||||
}
|
||||
|
||||
actionImpl(context, serializedCtx, brokerContext);
|
||||
}
|
||||
|
||||
protected abstract void authenticateImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext serializedCtx, BrokeredIdentityContext brokerContext);
|
||||
protected abstract void actionImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext serializedCtx, BrokeredIdentityContext brokerContext);
|
||||
|
||||
protected void sendFailureChallenge(AuthenticationFlowContext context, String eventError, String errorMessage, AuthenticationFlowError flowError) {
|
||||
context.getEvent().user(context.getUser())
|
||||
.error(eventError);
|
||||
Response challengeResponse = context.form()
|
||||
.setError(errorMessage)
|
||||
.createErrorPage();
|
||||
context.failureChallenge(flowError, challengeResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
public static UserModel getExistingUser(KeycloakSession session, RealmModel realm, ClientSessionModel clientSession) {
|
||||
String existingUserId = clientSession.getNote(EXISTING_USER_INFO);
|
||||
if (existingUserId == null) {
|
||||
throw new AuthenticationFlowException("Unexpected state. There is no existing duplicated user identified in ClientSession",
|
||||
AuthenticationFlowError.INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
ExistingUserInfo duplication = ExistingUserInfo.deserialize(existingUserId);
|
||||
|
||||
UserModel existingUser = session.users().getUserById(duplication.getExistingUserId(), realm);
|
||||
if (existingUser == null) {
|
||||
throw new AuthenticationFlowException("User with ID '" + existingUserId + "' not found.", AuthenticationFlowError.INVALID_USER);
|
||||
}
|
||||
|
||||
if (!existingUser.isEnabled()) {
|
||||
throw new AuthenticationFlowException("User with ID '" + existingUserId + "', username '" + existingUser.getUsername() + "' disabled.", AuthenticationFlowError.USER_DISABLED);
|
||||
}
|
||||
|
||||
return existingUser;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package org.keycloak.authentication.authenticators.broker;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||
import org.keycloak.authentication.AuthenticationFlowError;
|
||||
import org.keycloak.authentication.AuthenticationFlowException;
|
||||
import org.keycloak.authentication.authenticators.broker.util.ExistingUserInfo;
|
||||
import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class IdpConfirmLinkAuthenticator extends AbstractIdpAuthenticator {
|
||||
|
||||
protected static Logger logger = Logger.getLogger(IdpConfirmLinkAuthenticator.class);
|
||||
|
||||
@Override
|
||||
protected void authenticateImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext serializedCtx, BrokeredIdentityContext brokerContext) {
|
||||
ClientSessionModel clientSession = context.getClientSession();
|
||||
|
||||
String existingUserInfo = clientSession.getNote(EXISTING_USER_INFO);
|
||||
if (existingUserInfo == null) {
|
||||
logger.warnf("No duplication detected.");
|
||||
context.attempted();
|
||||
return;
|
||||
}
|
||||
|
||||
ExistingUserInfo duplicationInfo = ExistingUserInfo.deserialize(existingUserInfo);
|
||||
Response challenge = context.form()
|
||||
.setStatus(Response.Status.OK)
|
||||
.setAttribute(LoginFormsProvider.IDENTITY_PROVIDER_BROKER_CONTEXT, brokerContext)
|
||||
.setError(Messages.FEDERATED_IDENTITY_CONFIRM_LINK_MESSAGE, duplicationInfo.getDuplicateAttributeName(), duplicationInfo.getDuplicateAttributeValue())
|
||||
.createIdpLinkConfirmLinkPage();
|
||||
context.challenge(challenge);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext serializedCtx, BrokeredIdentityContext brokerContext) {
|
||||
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
|
||||
|
||||
String action = formData.getFirst("submitAction");
|
||||
if (action != null && action.equals("updateProfile")) {
|
||||
context.getClientSession().setNote(ENFORCE_UPDATE_PROFILE, "true");
|
||||
context.getClientSession().removeNote(EXISTING_USER_INFO);
|
||||
context.resetFlow();
|
||||
} else if (action != null && action.equals("linkAccount")) {
|
||||
context.success();
|
||||
} else {
|
||||
throw new AuthenticationFlowException("Unknown action: " + action,
|
||||
AuthenticationFlowError.INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresUser() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package org.keycloak.authentication.authenticators.broker;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authentication.Authenticator;
|
||||
import org.keycloak.authentication.AuthenticatorFactory;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class IdpConfirmLinkAuthenticatorFactory implements AuthenticatorFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "idp-confirm-link";
|
||||
static IdpConfirmLinkAuthenticator SINGLETON = new IdpConfirmLinkAuthenticator();
|
||||
|
||||
@Override
|
||||
public Authenticator create(KeycloakSession session) {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReferenceCategory() {
|
||||
return "confirmLink";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfigurable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
|
||||
AuthenticationExecutionModel.Requirement.REQUIRED,
|
||||
AuthenticationExecutionModel.Requirement.DISABLED};
|
||||
|
||||
@Override
|
||||
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
|
||||
return REQUIREMENT_CHOICES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Confirm link existing account";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Show the form where user confirms if he wants to link identity provider with existing account or rather edit user profile data retrieved from identity provider to avoid conflict";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserSetupAllowed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
package org.keycloak.authentication.authenticators.broker;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||
import org.keycloak.authentication.authenticators.broker.util.ExistingUserInfo;
|
||||
import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.FormMessage;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class IdpCreateUserIfUniqueAuthenticator extends AbstractIdpAuthenticator {
|
||||
|
||||
protected static Logger logger = Logger.getLogger(IdpCreateUserIfUniqueAuthenticator.class);
|
||||
|
||||
|
||||
@Override
|
||||
protected void actionImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext serializedCtx, BrokeredIdentityContext brokerContext) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void authenticateImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext serializedCtx, BrokeredIdentityContext brokerContext) {
|
||||
|
||||
KeycloakSession session = context.getSession();
|
||||
RealmModel realm = context.getRealm();
|
||||
|
||||
if (context.getClientSession().getNote(EXISTING_USER_INFO) != null) {
|
||||
context.attempted();
|
||||
return;
|
||||
}
|
||||
|
||||
ExistingUserInfo duplication = checkExistingUser(context, serializedCtx, brokerContext);
|
||||
|
||||
if (duplication == null) {
|
||||
logger.debugf("No duplication detected. Creating account for user '%s' and linking with identity provider '%s' .",
|
||||
brokerContext.getModelUsername(), brokerContext.getIdpConfig().getAlias());
|
||||
|
||||
UserModel federatedUser = session.users().addUser(realm, brokerContext.getModelUsername());
|
||||
federatedUser.setEnabled(true);
|
||||
federatedUser.setEmail(brokerContext.getEmail());
|
||||
federatedUser.setFirstName(brokerContext.getFirstName());
|
||||
federatedUser.setLastName(brokerContext.getLastName());
|
||||
|
||||
for (Map.Entry<String, List<String>> attr : serializedCtx.getAttributes().entrySet()) {
|
||||
federatedUser.setAttribute(attr.getKey(), attr.getValue());
|
||||
}
|
||||
|
||||
AuthenticatorConfigModel config = context.getAuthenticatorConfig();
|
||||
if (config != null && Boolean.parseBoolean(config.getConfig().get(IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION))) {
|
||||
logger.debugf("User '%s' required to update password", federatedUser.getUsername());
|
||||
federatedUser.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
|
||||
}
|
||||
|
||||
// TODO: Event
|
||||
|
||||
context.setUser(federatedUser);
|
||||
context.getClientSession().setNote(BROKER_REGISTERED_NEW_USER, "true");
|
||||
context.success();
|
||||
} else {
|
||||
logger.debugf("Duplication detected. There is already existing user with %s '%s' .",
|
||||
duplication.getDuplicateAttributeName(), duplication.getDuplicateAttributeValue());
|
||||
|
||||
// Set duplicated user, so next authenticators can deal with it
|
||||
context.getClientSession().setNote(EXISTING_USER_INFO, duplication.serialize());
|
||||
|
||||
Response challengeResponse = context.form()
|
||||
.setError(Messages.FEDERATED_IDENTITY_EXISTS, duplication.getDuplicateAttributeName(), duplication.getDuplicateAttributeValue())
|
||||
.createErrorPage();
|
||||
context.challenge(challengeResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// Could be overriden to detect duplication based on other criterias (firstName, lastName, ...)
|
||||
protected ExistingUserInfo checkExistingUser(AuthenticationFlowContext context, SerializedBrokeredIdentityContext serializedCtx, BrokeredIdentityContext brokerContext) {
|
||||
|
||||
if (brokerContext.getEmail() != null) {
|
||||
UserModel existingUser = context.getSession().users().getUserByEmail(brokerContext.getEmail(), context.getRealm());
|
||||
if (existingUser != null) {
|
||||
return new ExistingUserInfo(existingUser.getId(), UserModel.EMAIL, existingUser.getEmail());
|
||||
}
|
||||
}
|
||||
|
||||
UserModel existingUser = context.getSession().users().getUserByUsername(brokerContext.getModelUsername(), context.getRealm());
|
||||
if (existingUser != null) {
|
||||
return new ExistingUserInfo(existingUser.getId(), UserModel.USERNAME, existingUser.getUsername());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean requiresUser() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package org.keycloak.authentication.authenticators.broker;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authentication.Authenticator;
|
||||
import org.keycloak.authentication.AuthenticatorFactory;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class IdpCreateUserIfUniqueAuthenticatorFactory implements AuthenticatorFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "idp-create-user-if-unique";
|
||||
static IdpCreateUserIfUniqueAuthenticator SINGLETON = new IdpCreateUserIfUniqueAuthenticator();
|
||||
|
||||
public static final String REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION = "require.password.update.after.registration";
|
||||
|
||||
@Override
|
||||
public Authenticator create(KeycloakSession session) {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReferenceCategory() {
|
||||
return "createUserIfUnique";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfigurable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
|
||||
AuthenticationExecutionModel.Requirement.ALTERNATIVE,
|
||||
AuthenticationExecutionModel.Requirement.REQUIRED,
|
||||
AuthenticationExecutionModel.Requirement.DISABLED};
|
||||
|
||||
@Override
|
||||
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
|
||||
return REQUIREMENT_CHOICES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Create User If Unique";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Detect if there is existing Keycloak account with same email like identity provider. If no, create new user";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserSetupAllowed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION);
|
||||
property.setLabel("Require Password Update After Registration");
|
||||
property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
|
||||
property.setHelpText("If this option is true and new user is successfully imported from Identity Provider to Keycloak (there is no duplicated email or username detected in Keycloak DB), then this user is required to update his password");
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
package org.keycloak.authentication.authenticators.broker;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||
import org.keycloak.authentication.AuthenticationFlowError;
|
||||
import org.keycloak.authentication.requiredactions.VerifyEmail;
|
||||
import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.email.EmailException;
|
||||
import org.keycloak.email.EmailProvider;
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.LoginActionsService;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator {
|
||||
|
||||
protected static Logger logger = Logger.getLogger(IdpEmailVerificationAuthenticator.class);
|
||||
|
||||
@Override
|
||||
protected void authenticateImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext serializedCtx, BrokeredIdentityContext brokerContext) {
|
||||
KeycloakSession session = context.getSession();
|
||||
RealmModel realm = context.getRealm();
|
||||
ClientSessionModel clientSession = context.getClientSession();
|
||||
|
||||
if (realm.getSmtpConfig().size() == 0) {
|
||||
logger.warnf("Smtp is not configured for the realm. Ignoring email verification authenticator");
|
||||
context.attempted();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create action cookie to detect if email verification happened in same browser
|
||||
LoginActionsService.createActionCookie(context.getRealm(), context.getUriInfo(), context.getConnection(), context.getClientSession().getId());
|
||||
|
||||
VerifyEmail.setupKey(clientSession);
|
||||
|
||||
UserModel existingUser = getExistingUser(session, realm, clientSession);
|
||||
|
||||
String link = UriBuilder.fromUri(context.getActionUrl())
|
||||
.queryParam(Constants.KEY, clientSession.getNote(Constants.VERIFY_EMAIL_KEY))
|
||||
.build().toString();
|
||||
long expiration = TimeUnit.SECONDS.toMinutes(context.getRealm().getAccessCodeLifespanUserAction());
|
||||
try {
|
||||
|
||||
context.getSession().getProvider(EmailProvider.class)
|
||||
.setRealm(realm)
|
||||
.setUser(existingUser)
|
||||
.setAttribute(EmailProvider.IDENTITY_PROVIDER_BROKER_CONTEXT, brokerContext)
|
||||
.sendConfirmIdentityBrokerLink(link, expiration);
|
||||
// event.clone().event(EventType.SEND_RESET_PASSWORD)
|
||||
// .user(user)
|
||||
// .detail(Details.USERNAME, username)
|
||||
// .detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, context.getClientSession().getId()).success();
|
||||
} catch (EmailException e) {
|
||||
// event.clone().event(EventType.SEND_RESET_PASSWORD)
|
||||
// .detail(Details.USERNAME, username)
|
||||
// .user(user)
|
||||
// .error(Errors.EMAIL_SEND_FAILED);
|
||||
logger.error("Failed to send email to confirm identity broker linking", e);
|
||||
Response challenge = context.form()
|
||||
.setError(Messages.EMAIL_SENT_ERROR)
|
||||
.createErrorPage();
|
||||
context.failure(AuthenticationFlowError.INTERNAL_ERROR, challenge);
|
||||
return;
|
||||
}
|
||||
|
||||
Response challenge = context.form()
|
||||
.setStatus(Response.Status.OK)
|
||||
.setAttribute(LoginFormsProvider.IDENTITY_PROVIDER_BROKER_CONTEXT, brokerContext)
|
||||
.createIdpLinkEmailPage();
|
||||
context.forceChallenge(challenge);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext serializedCtx, BrokeredIdentityContext brokerContext) {
|
||||
MultivaluedMap<String, String> queryParams = context.getSession().getContext().getUri().getQueryParameters();
|
||||
String key = queryParams.getFirst(Constants.KEY);
|
||||
ClientSessionModel clientSession = context.getClientSession();
|
||||
RealmModel realm = context.getRealm();
|
||||
KeycloakSession session = context.getSession();
|
||||
|
||||
if (key != null) {
|
||||
String keyFromSession = clientSession.getNote(Constants.VERIFY_EMAIL_KEY);
|
||||
clientSession.removeNote(Constants.VERIFY_EMAIL_KEY);
|
||||
if (key.equals(keyFromSession)) {
|
||||
UserModel existingUser = getExistingUser(session, realm, clientSession);
|
||||
|
||||
logger.debugf("User '%s' confirmed that wants to link with identity provider '%s' . Identity provider username is '%s' ", existingUser.getUsername(),
|
||||
brokerContext.getIdpConfig().getAlias(), brokerContext.getUsername());
|
||||
|
||||
String actionCookieValue = LoginActionsService.getActionCookie(session.getContext().getRequestHeaders(), realm, session.getContext().getUri(), context.getConnection());
|
||||
if (actionCookieValue == null || !actionCookieValue.equals(clientSession.getId())) {
|
||||
clientSession.setNote(IS_DIFFERENT_BROWSER, "true");
|
||||
}
|
||||
|
||||
context.setUser(existingUser);
|
||||
context.success();
|
||||
} else {
|
||||
logger.error("Key parameter don't match with the expected value from client session");
|
||||
Response challengeResponse = context.form()
|
||||
.setError(Messages.INVALID_ACCESS_CODE)
|
||||
.createErrorPage();
|
||||
context.failureChallenge(AuthenticationFlowError.IDENTITY_PROVIDER_ERROR, challengeResponse);
|
||||
}
|
||||
} else {
|
||||
Response challengeResponse = context.form()
|
||||
.setError(Messages.MISSING_PARAMETER, Constants.KEY)
|
||||
.createErrorPage();
|
||||
context.failureChallenge(AuthenticationFlowError.IDENTITY_PROVIDER_ERROR, challengeResponse);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresUser() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package org.keycloak.authentication.authenticators.broker;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authentication.Authenticator;
|
||||
import org.keycloak.authentication.AuthenticatorFactory;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class IdpEmailVerificationAuthenticatorFactory implements AuthenticatorFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "idp-email-verification";
|
||||
static IdpEmailVerificationAuthenticator SINGLETON = new IdpEmailVerificationAuthenticator();
|
||||
|
||||
@Override
|
||||
public Authenticator create(KeycloakSession session) {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReferenceCategory() {
|
||||
return "emailVerification";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfigurable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
|
||||
AuthenticationExecutionModel.Requirement.ALTERNATIVE,
|
||||
AuthenticationExecutionModel.Requirement.REQUIRED,
|
||||
AuthenticationExecutionModel.Requirement.DISABLED};
|
||||
|
||||
@Override
|
||||
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
|
||||
return REQUIREMENT_CHOICES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Verify existing account by Email";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Email verification of existing Keycloak user, that wants to link his user account with identity provider";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserSetupAllowed() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
package org.keycloak.authentication.authenticators.broker;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||
import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.common.util.ObjectUtil;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.FormMessage;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.services.resources.AttributeFormDataProcessor;
|
||||
import org.keycloak.services.validation.Validation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class IdpReviewProfileAuthenticator extends AbstractIdpAuthenticator {
|
||||
|
||||
protected static Logger logger = Logger.getLogger(IdpReviewProfileAuthenticator.class);
|
||||
|
||||
@Override
|
||||
public boolean requiresUser() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void authenticateImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext userCtx, BrokeredIdentityContext brokerContext) {
|
||||
IdentityProviderModel idpConfig = brokerContext.getIdpConfig();
|
||||
|
||||
if (requiresUpdateProfilePage(context, userCtx, brokerContext)) {
|
||||
|
||||
logger.debugf("Identity provider '%s' requires update profile action for broker user '%s'.", idpConfig.getAlias(), userCtx.getUsername());
|
||||
|
||||
// No formData for first render. The profile is rendered from userCtx
|
||||
Response challengeResponse = context.form()
|
||||
.setAttribute(LoginFormsProvider.UPDATE_PROFILE_CONTEXT_ATTR, userCtx)
|
||||
.setFormData(null)
|
||||
.createUpdateProfilePage();
|
||||
context.challenge(challengeResponse);
|
||||
} else {
|
||||
// Not required to update profile. Marked success
|
||||
context.success();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean requiresUpdateProfilePage(AuthenticationFlowContext context, SerializedBrokeredIdentityContext userCtx, BrokeredIdentityContext brokerContext) {
|
||||
String enforceUpdateProfile = context.getClientSession().getNote(ENFORCE_UPDATE_PROFILE);
|
||||
if (Boolean.parseBoolean(enforceUpdateProfile)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String updateProfileFirstLogin;
|
||||
AuthenticatorConfigModel authenticatorConfig = context.getAuthenticatorConfig();
|
||||
if (authenticatorConfig == null || !authenticatorConfig.getConfig().containsKey(IdpReviewProfileAuthenticatorFactory.UPDATE_PROFILE_ON_FIRST_LOGIN)) {
|
||||
updateProfileFirstLogin = IdentityProviderRepresentation.UPFLM_MISSING;
|
||||
} else {
|
||||
updateProfileFirstLogin = authenticatorConfig.getConfig().get(IdpReviewProfileAuthenticatorFactory.UPDATE_PROFILE_ON_FIRST_LOGIN);
|
||||
}
|
||||
|
||||
RealmModel realm = context.getRealm();
|
||||
return IdentityProviderRepresentation.UPFLM_ON.equals(updateProfileFirstLogin)
|
||||
|| (IdentityProviderRepresentation.UPFLM_MISSING.equals(updateProfileFirstLogin) && !Validation.validateUserMandatoryFields(realm, userCtx));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext userCtx, BrokeredIdentityContext brokerContext) {
|
||||
EventBuilder event = context.getEvent();
|
||||
event.event(EventType.UPDATE_PROFILE);
|
||||
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
|
||||
|
||||
RealmModel realm = context.getRealm();
|
||||
|
||||
List<FormMessage> errors = Validation.validateUpdateProfileForm(true, formData);
|
||||
if (errors != null && !errors.isEmpty()) {
|
||||
Response challenge = context.form()
|
||||
.setErrors(errors)
|
||||
.setAttribute(LoginFormsProvider.UPDATE_PROFILE_CONTEXT_ATTR, userCtx)
|
||||
.setFormData(formData)
|
||||
.createUpdateProfilePage();
|
||||
context.challenge(challenge);
|
||||
return;
|
||||
}
|
||||
|
||||
userCtx.setUsername(formData.getFirst(UserModel.USERNAME));
|
||||
userCtx.setFirstName(formData.getFirst(UserModel.FIRST_NAME));
|
||||
userCtx.setLastName(formData.getFirst(UserModel.LAST_NAME));
|
||||
|
||||
String email = formData.getFirst(UserModel.EMAIL);
|
||||
if (!ObjectUtil.isEqualOrBothNull(email, userCtx.getEmail())) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.tracef("Email updated on updateProfile page to '%s' ", email);
|
||||
}
|
||||
|
||||
userCtx.setEmail(email);
|
||||
context.getClientSession().setNote(UPDATE_PROFILE_EMAIL_CHANGED, "true");
|
||||
}
|
||||
|
||||
AttributeFormDataProcessor.process(formData, realm, userCtx);
|
||||
|
||||
userCtx.saveToClientSession(context.getClientSession());
|
||||
|
||||
logger.debugf("Profile updated successfully after first authentication with identity provider '%s' for broker user '%s'.", brokerContext.getIdpConfig().getAlias(), userCtx.getUsername());
|
||||
|
||||
event.detail(Details.UPDATED_EMAIL, email);
|
||||
context.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
package org.keycloak.authentication.authenticators.broker;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authentication.Authenticator;
|
||||
import org.keycloak.authentication.AuthenticatorFactory;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class IdpReviewProfileAuthenticatorFactory implements AuthenticatorFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "idp-review-profile";
|
||||
static IdpReviewProfileAuthenticator SINGLETON = new IdpReviewProfileAuthenticator();
|
||||
|
||||
public static final String UPDATE_PROFILE_ON_FIRST_LOGIN = "update.profile.on.first.login";
|
||||
|
||||
@Override
|
||||
public Authenticator create(KeycloakSession session) {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReferenceCategory() {
|
||||
return "reviewProfile";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfigurable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
|
||||
AuthenticationExecutionModel.Requirement.REQUIRED,
|
||||
AuthenticationExecutionModel.Requirement.DISABLED};
|
||||
|
||||
@Override
|
||||
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
|
||||
return REQUIREMENT_CHOICES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Review Profile";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "User reviews and updates profile data retrieved from Identity Provider in the displayed form";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserSetupAllowed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(UPDATE_PROFILE_ON_FIRST_LOGIN);
|
||||
property.setLabel("{{:: 'update-profile-on-first-login' | translate}}");
|
||||
property.setType(ProviderConfigProperty.LIST_TYPE);
|
||||
List<String> updateProfileValues = Arrays.asList(IdentityProviderRepresentation.UPFLM_ON, IdentityProviderRepresentation.UPFLM_MISSING, IdentityProviderRepresentation.UPFLM_OFF);
|
||||
property.setDefaultValue(updateProfileValues);
|
||||
property.setHelpText("Define conditions under which a user has to review and update his profile after first-time login. Value 'On' means that"
|
||||
+ " page for reviewing profile will be displayed and user can review and update his profile. Value 'off' means that page won't be displayed."
|
||||
+ " Value 'missing' means that page is displayed just when some required attribute is missing (wasn't downloaded from identity provider). Value 'missing' is the default one");
|
||||
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package org.keycloak.authentication.authenticators.broker;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||
import org.keycloak.authentication.AuthenticationFlowError;
|
||||
import org.keycloak.authentication.AuthenticationFlowException;
|
||||
import org.keycloak.authentication.authenticators.browser.UsernamePasswordForm;
|
||||
import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
|
||||
/**
|
||||
* Same like classic username+password form, but username is "known" and user can't change it
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class IdpUsernamePasswordForm extends UsernamePasswordForm {
|
||||
|
||||
@Override
|
||||
protected Response challenge(AuthenticationFlowContext context, MultivaluedMap<String, String> formData) {
|
||||
UserModel existingUser = AbstractIdpAuthenticator.getExistingUser(context.getSession(), context.getRealm(), context.getClientSession());
|
||||
|
||||
return setupForm(context, formData, existingUser)
|
||||
.setStatus(Response.Status.OK)
|
||||
.createLogin();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean validateForm(AuthenticationFlowContext context, MultivaluedMap<String, String> formData) {
|
||||
UserModel existingUser = AbstractIdpAuthenticator.getExistingUser(context.getSession(), context.getRealm(), context.getClientSession());
|
||||
context.setUser(existingUser);
|
||||
|
||||
// Restore formData for the case of error
|
||||
setupForm(context, formData, existingUser);
|
||||
|
||||
return validatePassword(context, formData);
|
||||
}
|
||||
|
||||
protected LoginFormsProvider setupForm(AuthenticationFlowContext context, MultivaluedMap<String, String> formData, UserModel existingUser) {
|
||||
SerializedBrokeredIdentityContext serializedCtx = SerializedBrokeredIdentityContext.readFromClientSession(context.getClientSession());
|
||||
if (serializedCtx == null) {
|
||||
throw new AuthenticationFlowException("Not found serialized context in clientSession", AuthenticationFlowError.IDENTITY_PROVIDER_ERROR);
|
||||
}
|
||||
|
||||
formData.add(AuthenticationManager.FORM_USERNAME, existingUser.getUsername());
|
||||
return context.form()
|
||||
.setFormData(formData)
|
||||
.setAttribute(LoginFormsProvider.USERNAME_EDIT_DISABLED, true)
|
||||
.setSuccess(Messages.FEDERATED_IDENTITY_CONFIRM_REAUTHENTICATE_MESSAGE, existingUser.getUsername(), serializedCtx.getIdentityProviderId());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package org.keycloak.authentication.authenticators.broker;
|
||||
|
||||
import org.keycloak.authentication.Authenticator;
|
||||
import org.keycloak.authentication.authenticators.browser.UsernamePasswordForm;
|
||||
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class IdpUsernamePasswordFormFactory extends UsernamePasswordFormFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "idp-username-password-form";
|
||||
public static final UsernamePasswordForm IDP_SINGLETON = new IdpUsernamePasswordForm();
|
||||
|
||||
@Override
|
||||
public Authenticator create(KeycloakSession session) {
|
||||
return IDP_SINGLETON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Validates a password from login form. Username is already known from identity provider authentication";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Username Password Form for identity provider reauthentication";
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue