Merge upstream-master into tkyjovsk-master

This commit is contained in:
Tomas Kyjovsky 2015-06-10 23:27:36 +02:00
commit 30359b51fa
81 changed files with 695 additions and 249 deletions

View file

@ -19,6 +19,7 @@ package org.keycloak.broker.oidc;
import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.JsonNode;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper;
import org.keycloak.broker.oidc.util.JsonSimpleHttp; import org.keycloak.broker.oidc.util.JsonSimpleHttp;
import org.keycloak.broker.provider.util.SimpleHttp; import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.broker.provider.AuthenticationRequest; import org.keycloak.broker.provider.AuthenticationRequest;
@ -50,6 +51,7 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.io.IOException; import java.io.IOException;
import java.security.PublicKey; import java.security.PublicKey;
@ -224,7 +226,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
name = getJsonProperty(userInfo, "name"); name = getJsonProperty(userInfo, "name");
preferredUsername = getJsonProperty(userInfo, "preferred_username"); preferredUsername = getJsonProperty(userInfo, "preferred_username");
email = getJsonProperty(userInfo, "email"); email = getJsonProperty(userInfo, "email");
identity.getContextData().put(USER_INFO, userInfo); AbstractJsonUserAttributeMapper.storeUserProfileForMapper(identity, userInfo, getConfig().getAlias());
} }
identity.getContextData().put(FEDERATED_ACCESS_TOKEN_RESPONSE, tokenResponse); identity.getContextData().put(FEDERATED_ACCESS_TOKEN_RESPONSE, tokenResponse);
identity.getContextData().put(VALIDATED_ID_TOKEN, idToken); identity.getContextData().put(VALIDATED_ID_TOKEN, idToken);

View file

@ -0,0 +1,206 @@
package org.keycloak.broker.oidc.mappers;
import java.util.ArrayList;
import java.util.List;
import org.codehaus.jackson.JsonNode;
import org.jboss.logging.Logger;
import org.keycloak.broker.oidc.OIDCIdentityProvider;
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderConfigProperty;
/**
* Abstract class for Social Provider mappers which allow mapping of JSON user profile field into Keycloak user
* attribute. Concrete mapper classes with own ID and provider mapping must be implemented for each social provider who
* uses {@link JsonNode} user profile.
*
* @author Vlastimil Elias (velias at redhat dot com)
*/
public abstract class AbstractJsonUserAttributeMapper extends AbstractIdentityProviderMapper {
protected static final Logger logger = Logger.getLogger(AbstractJsonUserAttributeMapper.class);
protected static final Logger LOGGER_DUMP_USER_PROFILE = Logger.getLogger("org.keycloak.social.user_profile_dump");
private static final String JSON_PATH_DELIMITER = ".";
/**
* Config param where name of mapping source JSON User Profile field is stored.
*/
public static final String CONF_JSON_FIELD = "jsonField";
/**
* Config param where name of mapping target USer attribute is stored.
*/
public static final String CONF_USER_ATTRIBUTE = "userAttribute";
/**
* Key in {@link BrokeredIdentityContext#getContextData()} where {@link JsonNode} with user profile is stored.
*/
public static final String CONTEXT_JSON_NODE = OIDCIdentityProvider.USER_INFO;
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
ProviderConfigProperty property;
ProviderConfigProperty property1;
property1 = new ProviderConfigProperty();
property1.setName(CONF_JSON_FIELD);
property1.setLabel("Social Profile JSON Field Path");
property1.setHelpText("Path of field in Social provider User Profile JSON data to get value from. You can use dot notation for nesting and square brackets for array index. Eg. 'contact.address[0].country'.");
property1.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property1);
property = new ProviderConfigProperty();
property.setName(CONF_USER_ATTRIBUTE);
property.setLabel("User Attribute Name");
property.setHelpText("User attribute name to store information into.");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
}
/**
* Store used profile JsonNode into user context for later use by this mapper. Profile data are dumped into special logger if enabled also to allow investigation of the structure.
*
* @param user context to store profile data into
* @param profile to store into context
* @param provider identification of social provider to be used in log dump
*
* @see #importNewUser(KeycloakSession, RealmModel, UserModel, IdentityProviderMapperModel, BrokeredIdentityContext)
* @see BrokeredIdentityContext#getContextData()
*/
public static void storeUserProfileForMapper(BrokeredIdentityContext user, JsonNode profile, String provider) {
user.getContextData().put(AbstractJsonUserAttributeMapper.CONTEXT_JSON_NODE, profile);
if (LOGGER_DUMP_USER_PROFILE.isDebugEnabled())
LOGGER_DUMP_USER_PROFILE.debug("User Profile JSON Data for provider "+provider+": " + profile);
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getDisplayCategory() {
return "Attribute Importer";
}
@Override
public String getDisplayType() {
return "Attribute Importer";
}
@Override
public String getHelpText() {
return "Import user profile information if it exists in Social provider JSON data into the specified user attribute.";
}
@Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String attribute = mapperModel.getConfig().get(CONF_USER_ATTRIBUTE);
if (attribute == null || attribute.trim().isEmpty()) {
logger.debug("Attribute is not configured");
return;
}
attribute = attribute.trim();
String value = getJsonValue(mapperModel, context);
if (value != null) {
user.setAttribute(attribute, value);
}
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
// we do not update user profile from social provider
}
protected static String getJsonValue(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String jsonField = mapperModel.getConfig().get(CONF_JSON_FIELD);
if (jsonField == null || jsonField.trim().isEmpty()) {
logger.debug("JSON field path is not configured");
return null;
}
jsonField = jsonField.trim();
if (jsonField.startsWith(JSON_PATH_DELIMITER) || jsonField.endsWith(JSON_PATH_DELIMITER) || jsonField.startsWith("[")) {
logger.debug("JSON field path is invalid " + jsonField);
return null;
}
JsonNode profileJsonNode = (JsonNode) context.getContextData().get(CONTEXT_JSON_NODE);
String value = getJsonValue(profileJsonNode, jsonField);
if (value == null) {
logger.debug("User profile JSON value '" + jsonField + "' is not available.");
}
return value;
}
protected static String getJsonValue(JsonNode baseNode, String fieldPath) {
logger.debug("Going to process JsonNode path " + fieldPath + " on data " + baseNode);
if (baseNode != null) {
int idx = fieldPath.indexOf(JSON_PATH_DELIMITER);
String currentFieldName = fieldPath;
if (idx > 0) {
currentFieldName = fieldPath.substring(0, idx).trim();
if (currentFieldName.isEmpty()) {
logger.debug("JSON path is invalid " + fieldPath);
return null;
}
}
String currentNodeName = currentFieldName;
int arrayIndex = -1;
if (currentFieldName.endsWith("]")) {
int bi = currentFieldName.indexOf("[");
if (bi == -1) {
logger.debug("Invalid array index construct in " + currentFieldName);
return null;
}
try {
String is = currentFieldName.substring(bi+1, currentFieldName.length() - 1).trim();
arrayIndex = Integer.parseInt(is);
} catch (Exception e) {
logger.debug("Invalid array index construct in " + currentFieldName);
return null;
}
currentNodeName = currentFieldName.substring(0,bi).trim();
}
JsonNode currentNode = baseNode.get(currentNodeName);
if (arrayIndex > -1 && currentNode.isArray()) {
logger.debug("Going to take array node at index " + arrayIndex);
currentNode = currentNode.get(arrayIndex);
}
if (currentNode == null) {
logger.debug("JsonNode not found for name " + currentFieldName);
return null;
}
if (idx < 0) {
if (!currentNode.isValueNode()) {
logger.debug("JsonNode is not value node for name " + currentFieldName);
return null;
}
String ret = currentNode.asText();
if (ret != null && !ret.trim().isEmpty())
return ret.trim();
} else {
return getJsonValue(currentNode, fieldPath.substring(idx + 1));
}
}
return null;
}
}

View file

@ -0,0 +1,120 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
*/
package org.keycloak.broker.oidc.mappers;
import java.io.IOException;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.junit.Assert;
import org.junit.Test;
/**
* Unit test for {@link AbstractJsonUserAttributeMapper}
*
* @author Vlastimil Elias (velias at redhat dot com)
*/
public class AbstractJsonUserAttributeMapperTest {
private static ObjectMapper mapper = new ObjectMapper();
private static JsonNode baseNode;
private JsonNode getJsonNode() throws JsonProcessingException, IOException {
if (baseNode == null)
baseNode = mapper.readTree("{ \"value1\" : \"v1 \",\"value_empty\" : \"\", \"value_b\" : true, \"value_i\" : 454, " + " \"value_array\":[\"a1\",\"a2\"], " +" \"nest1\": {\"value1\": \" fgh \",\"value_empty\" : \"\", \"nest2\":{\"value_b\" : false, \"value_i\" : 43}}, "+ " \"nesta\": { \"a\":[{\"av1\": \"vala1\"},{\"av1\": \"vala2\"}]}"+" }");
return baseNode;
}
@Test
public void getJsonValue_invalidPath() throws JsonProcessingException, IOException {
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "."));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), ".."));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "...value1"));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), ".value1"));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value1."));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "[]"));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "[value1"));
}
@Test
public void getJsonValue_simpleValues() throws JsonProcessingException, IOException {
//unknown field returns null
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_unknown"));
// we check value is trimmed also!
Assert.assertEquals("v1", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value1"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_empty"));
Assert.assertEquals("true", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_b"));
Assert.assertEquals("454", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_i"));
}
@Test
public void getJsonValue_nestedSimpleValues() throws JsonProcessingException, IOException {
// null if path points to JSON object
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.nest2"));
//unknown field returns null
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.value_unknown"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.nest2.value_unknown"));
// we check value is trimmed also!
Assert.assertEquals("fgh", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.value1"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.value_empty"));
Assert.assertEquals("false", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.nest2.value_b"));
Assert.assertEquals("43", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.nest2.value_i"));
// null if invalid nested path
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1."));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.nest2."));
}
@Test
public void getJsonValue_simpleArray() throws JsonProcessingException, IOException {
// array field itself returns null if no index is provided
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array"));
// outside index returns null
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[2]"));
//corect index
Assert.assertEquals("a1", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[0]"));
Assert.assertEquals("a2", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[1]"));
//incorrect array constructs
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[]"));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array]"));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array["));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[a]"));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[-2]"));
}
@Test
public void getJsonValue_nestedArrayWithObjects() throws JsonProcessingException, IOException {
Assert.assertEquals("vala1", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[0].av1"));
Assert.assertEquals("vala2", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[1].av1"));
//different path erros or nonexisting indexes or fields return null
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[2].av1"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[0]"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[0].av_unknown"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[].av1"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a.av1"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a].av1"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[.av1"));
}
}

View file

@ -1246,6 +1246,24 @@ keycloak.createLoginUrl({
the tool tips to see what each mapper can do for you. the tool tips to see what each mapper can do for you.
</para> </para>
</section> </section>
<section>
<title>Mapping/Importing User profile data from Social Identity Provider</title>
<para>
You can import user profile data provided by social identity providers like Google, GitHub, LinkedIn, Stackoverflow and Facebook
into new Keycloak user created from given social accounts. After you configure a broker, you'll see a <literal>Mappers</literal>
button appear. Click on that and you'll get to the list of mappers that are assigned to this broker. There is a
<literal>Create</literal> button on this page. Clicking on this create button allows you to create a broker mapper.
"Attribute Importer" mapper allows you to define path in JSON user profile data provided by the provider to get value from.
You can use dot notation for nesting and square brackets to access fields in array by index. For example 'contact.address[0].country'.
Then you can define name of Keycloak's user profile attribute this value is stored into.
</para>
<para>
To investigate structure of user profile JSON data provided by social providers you can enable <literal>DEBUG</literal> level for
logger <literal>org.keycloak.social.user_profile_dump</literal> and login using given provider. Then you can find user profile
JSON structure in Keycloak log file.
</para>
</section>
<section> <section>
<title>Examples</title> <title>Examples</title>

View file

@ -1,5 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1><strong>Settings</strong> {{realm.realm|capitalize}}</h1> <h1>Settings</h1>
<kc-tabs-realm></kc-tabs-realm> <kc-tabs-realm></kc-tabs-realm>

View file

@ -5,7 +5,7 @@
<li>{{client.clientId}}</li> <li>{{client.clientId}}</li>
</ol> </ol>
<h1><strong>Client</strong> {{client.clientId|capitalize}}</h1> <h1>{{client.clientId|capitalize}}</h1>
<kc-tabs-client></kc-tabs-client> <kc-tabs-client></kc-tabs-client>

View file

@ -5,7 +5,7 @@
<li>{{client.clientId}}</li> <li>{{client.clientId}}</li>
</ol> </ol>
<h1><strong>Client</strong> {{client.clientId|capitalize}}</h1> <h1>{{client.clientId|capitalize}}</h1>
<kc-tabs-client></kc-tabs-client> <kc-tabs-client></kc-tabs-client>

View file

@ -6,8 +6,8 @@
<li data-ng-hide="create">{{client.clientId}}</li> <li data-ng-hide="create">{{client.clientId}}</li>
</ol> </ol>
<h1 data-ng-show="create"><strong>Add Client</strong></h1> <h1 data-ng-show="create">Add Client</h1>
<h1 data-ng-hide="create"><strong>Client</strong> {{client.clientId|capitalize}}</h1> <h1 data-ng-hide="create">{{client.clientId|capitalize}}</h1>
<kc-tabs-client></kc-tabs-client> <kc-tabs-client></kc-tabs-client>

View file

@ -5,7 +5,7 @@
<li>Import Client</li> <li>Import Client</li>
</ol> </ol>
<h1><strong>Import Client</strong></h1> <h1>Import Client</h1>
<form class="form-horizontal" name="realmForm" novalidate> <form class="form-horizontal" name="realmForm" novalidate>
<fieldset class="border-top"> <fieldset class="border-top">

View file

@ -5,7 +5,7 @@
<li>{{client.clientId}}</li> <li>{{client.clientId}}</li>
</ol> </ol>
<h1><strong>Client</strong> {{client.clientId|capitalize}}</h1> <h1>{{client.clientId|capitalize}}</h1>
<kc-tabs-client></kc-tabs-client> <kc-tabs-client></kc-tabs-client>

View file

@ -5,7 +5,7 @@
<li>{{client.clientId}}</li> <li>{{client.clientId}}</li>
</ol> </ol>
<h1><strong>Client</strong> {{client.clientId|capitalize}}</h1> <h1>{{client.clientId|capitalize}}</h1>
<kc-tabs-client></kc-tabs-client> <kc-tabs-client></kc-tabs-client>

View file

@ -1,6 +1,6 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1> <h1>
<span><strong>Clients</strong> {{realm.realm|capitalize}}</span> <span>Clients</span>
<kc-tooltip>Clients are trusted browser apps and web services in a realm. These clients can request a login. You can also define client specific roles.</kc-tooltip> <kc-tooltip>Clients are trusted browser apps and web services in a realm. These clients can request a login. You can also define client specific roles.</kc-tooltip>
</h1> </h1>

View file

@ -7,7 +7,7 @@
<li class="active">Add Builtin Protocol Mappers</li> <li class="active">Add Builtin Protocol Mappers</li>
</ol> </ol>
<h1><strong>Add Builtin Protocol Mapper</strong></h1> <h1>Add Builtin Protocol Mapper</h1>
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
<thead> <thead>

View file

@ -5,7 +5,7 @@
<li>{{client.clientId}}</li> <li>{{client.clientId}}</li>
</ol> </ol>
<h1><strong>Client</strong> {{client.clientId|capitalize}}</h1> <h1>{{client.clientId|capitalize}}</h1>
<kc-tabs-client></kc-tabs-client> <kc-tabs-client></kc-tabs-client>

View file

@ -5,7 +5,7 @@
<li>{{client.clientId}}</li> <li>{{client.clientId}}</li>
</ol> </ol>
<h1><strong>Client</strong> {{client.clientId|capitalize}}</h1> <h1>{{client.clientId|capitalize}}</h1>
<kc-tabs-client></kc-tabs-client> <kc-tabs-client></kc-tabs-client>

View file

@ -8,8 +8,8 @@
<li data-ng-hide="create">{{role.name}}</li> <li data-ng-hide="create">{{role.name}}</li>
</ol> </ol>
<h1 data-ng-show="create"><strong>Add Role</strong></h1> <h1 data-ng-show="create">Add Role</h1>
<h1 data-ng-hide="create"><strong>Role</strong> {{role.name}}</h1> <h1 data-ng-hide="create">{{role.name|capitalize}}</h1>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients"> <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients">

View file

@ -5,7 +5,7 @@
<li>{{client.clientId}}</li> <li>{{client.clientId}}</li>
</ol> </ol>
<h1><strong>Client</strong> {{client.clientId|capitalize}}</h1> <h1>{{client.clientId|capitalize}}</h1>
<kc-tabs-client></kc-tabs-client> <kc-tabs-client></kc-tabs-client>

View file

@ -7,7 +7,7 @@
<li class="active">SAML {{keyType}} Key Export</li> <li class="active">SAML {{keyType}} Key Export</li>
</ol> </ol>
<h1><strong>Export SAML Key</strong> {{client.clientId|capitalize}}</h1> <h1>Export SAML Key {{client.clientId|capitalize}}</h1>
<form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm"> <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
<fieldset class="form-group col-sm-10"> <fieldset class="form-group col-sm-10">

View file

@ -7,7 +7,7 @@
<li class="active">SAML {{keyType}} Key Import</li> <li class="active">SAML {{keyType}} Key Import</li>
</ol> </ol>
<h1><strong>Import SAML Key</strong> {{client.clientId|capitalize}}</h1> <h1>Import SAML Key {{client.clientId|capitalize}}</h1>
<form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm"> <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
<fieldset> <fieldset>

View file

@ -5,7 +5,7 @@
<li>{{client.clientId}}</li> <li>{{client.clientId}}</li>
</ol> </ol>
<h1><strong>Client</strong> {{client.clientId|capitalize}}</h1> <h1>{{client.clientId|capitalize}}</h1>
<kc-tabs-client></kc-tabs-client> <kc-tabs-client></kc-tabs-client>

View file

@ -5,7 +5,7 @@
<li>{{client.clientId}}</li> <li>{{client.clientId}}</li>
</ol> </ol>
<h1><strong>Client</strong> {{client.clientId|capitalize}}</h1> <h1>{{client.clientId|capitalize}}</h1>
<kc-tabs-client></kc-tabs-client> <kc-tabs-client></kc-tabs-client>

View file

@ -5,7 +5,7 @@
<li>{{client.clientId}}</li> <li>{{client.clientId}}</li>
</ol> </ol>
<h1><strong>Client</strong> {{client.clientId|capitalize}}</h1> <h1>{{client.clientId|capitalize}}</h1>
<kc-tabs-client></kc-tabs-client> <kc-tabs-client></kc-tabs-client>

View file

@ -1,5 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1><strong>Settings</strong> {{realm.realm|capitalize}}</h1> <h1>Settings</h1>
<kc-tabs-realm></kc-tabs-realm> <kc-tabs-realm></kc-tabs-realm>

View file

@ -5,8 +5,8 @@
<li data-ng-show="create">Add User Federation Provider</li> <li data-ng-show="create">Add User Federation Provider</li>
</ol> </ol>
<h1 data-ng-hide="create"><strong>{{instance.providerName|capitalize}} User Federation Provider</strong> {{instance.displayName|capitalize}}</h1> <h1 data-ng-hide="create">{{instance.providerName|capitalize}}</h1>
<h1 data-ng-show="create"><strong>Add {{instance.providerName|capitalize}} User Federation Provider</strong></h1> <h1 data-ng-show="create">Add {{instance.providerName|capitalize}} User Federation Provide</h1>
<ul class="nav nav-tabs" data-ng-hide="create"> <ul class="nav nav-tabs" data-ng-hide="create">
<li class="active"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">Settings</a></li> <li class="active"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">Settings</a></li>

View file

@ -5,8 +5,8 @@
<li data-ng-show="create">Add User Federation Provider</li> <li data-ng-show="create">Add User Federation Provider</li>
</ol> </ol>
<h1 data-ng-hide="create"><strong>Kerberos User Federation Provider</strong> {{instance.displayName|capitalize}}</h1> <h1 data-ng-hide="create">Kerberos</h1>
<h1 data-ng-show="create"><strong>Add Kerberos User Federation Provider</strong></h1> <h1 data-ng-show="create">Add Kerberos User Federation Provider</h1>
<ul class="nav nav-tabs" data-ng-hide="create"> <ul class="nav nav-tabs" data-ng-hide="create">
<li class="active"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">Settings</a></li> <li class="active"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">Settings</a></li>

View file

@ -5,8 +5,8 @@
<li data-ng-show="create">Add User Federation Provider</li> <li data-ng-show="create">Add User Federation Provider</li>
</ol> </ol>
<h1 data-ng-hide="create"><strong>LDAP User Federation Provider</strong> {{instance.displayName|capitalize}}</h1> <h1 data-ng-hide="create">LDAP</h1>
<h1 data-ng-show="create"><strong>Add LDAP User Federation Provider</strong></h1> <h1 data-ng-show="create">Add LDAP User Federation Provider</h1>
<ul class="nav nav-tabs" data-ng-hide="create"> <ul class="nav nav-tabs" data-ng-hide="create">
<li class="active"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">Settings</a></li> <li class="active"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">Settings</a></li>

View file

@ -7,8 +7,8 @@
<li class="active" data-ng-hide="create">{{mapper.name}}</li> <li class="active" data-ng-hide="create">{{mapper.name}}</li>
</ol> </ol>
<h1 data-ng-hide="create"><strong>User Federation Mapper</strong> {{mapper.name}}</h1> <h1 data-ng-hide="create">{{mapper.name|capitalize}}</h1>
<h1 data-ng-show="create"><strong>Add User Federation Mapper</strong></h1> <h1 data-ng-show="create">Add User Federation Mapper</h1>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm"> <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset> <fieldset>

View file

@ -5,7 +5,7 @@
<li>User Federation Mappers</li> <li>User Federation Mappers</li>
</ol> </ol>
<h1><strong>{{provider.providerName === 'ldap' ? 'LDAP' : (provider.providerName|capitalize)}} User Federation Provider</strong> {{provider.displayName|capitalize}}</h1> <h1>{{provider.providerName === 'ldap' ? 'LDAP' : (provider.providerName|capitalize)}}</h1>
<ul class="nav nav-tabs" data-ng-hide="create"> <ul class="nav nav-tabs" data-ng-hide="create">
<li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{provider.providerName}}/{{provider.id}}">Settings</a></li> <li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{provider.providerName}}/{{provider.id}}">Settings</a></li>

View file

@ -4,11 +4,11 @@
<li><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">{{identityProvider.alias}}</a></li> <li><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">{{identityProvider.alias}}</a></li>
<li><a href="#/realms/{{realm.realm}}/identity-provider-mappers/{{identityProvider.alias}}/mappers">Identity Provider Mappers</a></li> <li><a href="#/realms/{{realm.realm}}/identity-provider-mappers/{{identityProvider.alias}}/mappers">Identity Provider Mappers</a></li>
<li class="active" data-ng-show="create">Create IdentityProvider Mapper</li> <li class="active" data-ng-show="create">Create IdentityProvider Mapper</li>
<li class="active" data-ng-hide="create">{{mapper.name}}</li> <li class="active" data-ng-hide="create">{{mapper.name|capitalize}}</li>
</ol> </ol>
<h1 data-ng-hide="create"><strong>Identity Provider Mapper</strong> {{mapper.name}}</h1> <h1 data-ng-hide="create">{{mapper.name|capitalize}}</h1>
<h1 data-ng-show="create"><strong>Add Identity Provider Mapper</strong></h1> <h1 data-ng-show="create">Add Identity Provider Mapper</h1>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm"> <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset> <fieldset>

View file

@ -4,7 +4,7 @@
<li>{{identityProvider.alias}}</li> <li>{{identityProvider.alias}}</li>
</ol> </ol>
<h1><strong>Identity Provider</strong> {{identityProvider.alias|capitalize}}</h1> <h1>{{identityProvider.alias|capitalize}}</h1>
<ul class="nav nav-tabs" data-ng-hide="newIdentityProvider"> <ul class="nav nav-tabs" data-ng-hide="newIdentityProvider">
<li><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">Settings</a></li> <li><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">Settings</a></li>

View file

@ -8,8 +8,8 @@
<li class="active" data-ng-hide="create">{{mapper.name}}</li> <li class="active" data-ng-hide="create">{{mapper.name}}</li>
</ol> </ol>
<h1 data-ng-show="create"><strong>Create Protocol Mapper</strong></h1> <h1 data-ng-show="create">Create Protocol Mapper</h1>
<h1 data-ng-hide="create"><strong>Protocol Mapper</strong> {{mapper.name}}</h1> <h1 data-ng-hide="create">{{mapper.name|capitalize}}</h1>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm"> <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">

View file

@ -1,5 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1><strong>Settings</strong> {{realm.realm|capitalize}}</h1> <h1>Settings</h1>
<kc-tabs-realm></kc-tabs-realm> <kc-tabs-realm></kc-tabs-realm>

View file

@ -1,5 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1><strong>Settings</strong> {{realm.realm|capitalize}}</h1> <h1>Settings</h1>
<kc-tabs-realm></kc-tabs-realm> <kc-tabs-realm></kc-tabs-realm>

View file

@ -1,5 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1><strong>Roles</strong> {{realm.realm|capitalize}}</h1> <h1>Roles</h1>
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li><a href="#/realms/{{realm.realm}}/roles">Realm Roles</a></li> <li><a href="#/realms/{{realm.realm}}/roles">Realm Roles</a></li>

View file

@ -1,5 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1 data-ng-hide="createRealm"><strong>Settings</strong> {{realm.realm|capitalize}}</h1> <h1 data-ng-hide="createRealm">Settings</h1>
<h1 data-ng-show="createRealm">Add Realm</h1> <h1 data-ng-show="createRealm">Add Realm</h1>
<kc-tabs-realm></kc-tabs-realm> <kc-tabs-realm></kc-tabs-realm>

View file

@ -1,6 +1,6 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1> <h1>
<span><strong>Admin Events</strong> {{realm.realm|capitalize}}</span> <span>Admin Events</span>
<kc-tooltip>Displays saved admin events for the realm. Events are related to admin account, for example a realm creation. To enable persisted events go to config.</kc-tooltip> <kc-tooltip>Displays saved admin events for the realm. Events are related to admin account, for example a realm creation. To enable persisted events go to config.</kc-tooltip>
</h1> </h1>

View file

@ -1,6 +1,6 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1> <h1>
<span><strong>Events Config</strong> {{realm.realm|capitalize}}</span> <span>Events Config</span>
<kc-tooltip>Displays configuration options to enable persistence of user and admin events.</kc-tooltip> <kc-tooltip>Displays configuration options to enable persistence of user and admin events.</kc-tooltip>
</h1> </h1>
@ -10,7 +10,7 @@
<li data-ng-class="(path[2] == 'events-settings') && 'active'"><a href="#/realms/{{realm.realm}}/events-settings">Config</a></li> <li data-ng-class="(path[2] == 'events-settings') && 'active'"><a href="#/realms/{{realm.realm}}/events-settings">Config</a></li>
</ul> </ul>
<div id="content"> <div id="content">
<h2><span>{{realm.realm}}</span> Events Config</h2> <h2>Events Config</h2>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageEvents"> <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageEvents">

View file

@ -1,6 +1,6 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1> <h1>
<span><strong>Events</strong> {{realm.realm|capitalize}}</span> <span>Events</span>
<kc-tooltip>Displays saved events for the realm. Events are related to user accounts, for example a user login. To enable persisted events go to config.</kc-tooltip> <kc-tooltip>Displays saved events for the realm. Events are related to user accounts, for example a user login. To enable persisted events go to config.</kc-tooltip>
</h1> </h1>

View file

@ -4,8 +4,8 @@
<li>{{identityProvider.alias}}</li> <li>{{identityProvider.alias}}</li>
</ol> </ol>
<h1 data-ng-hide="create"><strong>Identity Provider</strong> {{identityProvider.alias|capitalize}}</h1> <h1 data-ng-hide="create">{{identityProvider.alias|capitalize}}</h1>
<h1 data-ng-show="create"><strong>Add OpenID Connect Identity Provider</strong></h1> <h1 data-ng-show="create">Add OpenID Connect Identity Provider</h1>
<ul class="nav nav-tabs" data-ng-hide="newIdentityProvider"> <ul class="nav nav-tabs" data-ng-hide="newIdentityProvider">
<li><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">Settings</a></li> <li><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">Settings</a></li>

View file

@ -4,8 +4,8 @@
<li>{{identityProvider.alias}}</li> <li>{{identityProvider.alias}}</li>
</ol> </ol>
<h1 data-ng-hide="create"><strong>Identity Provider</strong> {{identityProvider.alias|capitalize}}</h1> <h1 data-ng-hide="create">{{identityProvider.alias|capitalize}}</h1>
<h1 data-ng-show="create"><strong>Add OpenID Connect Identity Provider</strong></h1> <h1 data-ng-show="create">Add OpenID Connect Identity Provider</h1>
<ul class="nav nav-tabs" data-ng-hide="newIdentityProvider"> <ul class="nav nav-tabs" data-ng-hide="newIdentityProvider">
<li class="active"><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">Settings</a></li> <li class="active"><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">Settings</a></li>

View file

@ -4,8 +4,8 @@
<li>{{identityProvider.alias}}</li> <li>{{identityProvider.alias}}</li>
</ol> </ol>
<h1 data-ng-hide="create"><strong>Identity Provider</strong> {{identityProvider.alias|capitalize}}</h1> <h1 data-ng-hide="create">{{identityProvider.alias|capitalize}}</h1>
<h1 data-ng-show="create"><strong>Add SAML Identity Provider</strong></h1> <h1 data-ng-show="create">Add SAML Identity Provider</h1>
<ul class="nav nav-tabs" data-ng-hide="newIdentityProvider"> <ul class="nav nav-tabs" data-ng-hide="newIdentityProvider">
<li class="active"><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">Settings</a></li> <li class="active"><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">Settings</a></li>

View file

@ -4,8 +4,8 @@
<li>{{identityProvider.alias}}</li> <li>{{identityProvider.alias}}</li>
</ol> </ol>
<h1 data-ng-hide="create"><strong>Identity Provider</strong> {{identityProvider.alias|capitalize}}</h1> <h1 data-ng-hide="create">{{identityProvider.alias|capitalize}}</h1>
<h1 data-ng-show="create"><strong>Add Social Identity Provider</strong></h1> <h1 data-ng-show="create">Add Social Identity Provider</h1>
<ul class="nav nav-tabs" data-ng-hide="newIdentityProvider"> <ul class="nav nav-tabs" data-ng-hide="newIdentityProvider">
<li class="active"><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">Settings</a></li> <li class="active"><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">Settings</a></li>

View file

@ -1,7 +1,7 @@
<div class="form-group clearfix"> <div class="form-group clearfix">
<label class="col-md-2 control-label" for="clientId">Key <span class="required">*</span></label> <label class="col-md-2 control-label" for="clientId">Key <span class="required">*</span></label>
<div class="col-md-6"> <div class="col-md-6">
<input class="form-control" id="clientId" type="text" ng-model="identityProvider.config.key" required> <input class="form-control" id="clientId" type="text" ng-model="identityProvider.config.key" required>
</div> </div>
<kc-tooltip>The Key obtained from Stack Overflow client registration.</kc-tooltip> <kc-tooltip>The Key obtained from Stack Overflow client registration.</kc-tooltip>
</div> </div>

View file

@ -1,5 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1><strong>Identity Providers</strong> {{realm.realm|capitalize}}</h1> <h1>Identity Providers</h1>
<form name="realmForm" novalidate class="form-horizontal"> <form name="realmForm" novalidate class="form-horizontal">
<fieldset> <fieldset>

View file

@ -1,5 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1><strong>Settings</strong> {{realm.realm|capitalize}}</h1> <h1>Settings</h1>
<kc-tabs-realm></kc-tabs-realm> <kc-tabs-realm></kc-tabs-realm>

View file

@ -1,5 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1><strong>Settings</strong> {{realm.realm|capitalize}}</h1> <h1>Settings</h1>
<kc-tabs-realm></kc-tabs-realm> <kc-tabs-realm></kc-tabs-realm>

View file

@ -1,5 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1><strong>Settings</strong> {{realm.realm|capitalize}}</h1> <h1>Settings</h1>
<kc-tabs-realm></kc-tabs-realm> <kc-tabs-realm></kc-tabs-realm>

View file

@ -1,5 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1><strong>Settings</strong> {{realm.realm|capitalize}}</h1> <h1>Settings</h1>
<kc-tabs-realm></kc-tabs-realm> <kc-tabs-realm></kc-tabs-realm>

View file

@ -1,5 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1><strong>Settings</strong> {{realm.realm|capitalize}}</h1> <h1>Settings</h1>
<kc-tabs-realm></kc-tabs-realm> <kc-tabs-realm></kc-tabs-realm>

View file

@ -6,8 +6,8 @@
<li data-ng-show="create">Add Role</li> <li data-ng-show="create">Add Role</li>
</ol> </ol>
<h1 data-ng-hide="create"><strong>Role</strong> {{role.name}}</h1> <h1 data-ng-hide="create">{{role.name|capitalize}}</h1>
<h1 data-ng-show="create"><strong>Add Role</strong></h1> <h1 data-ng-show="create">Add Role</h1>
<form class="form-horizontal clearfix" name="realmForm" novalidate kc-read-only="!access.manageRealm"> <form class="form-horizontal clearfix" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset> <fieldset>

View file

@ -1,5 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1><strong>Roles</strong> {{realm.realm|capitalize}}</h1> <h1>Roles</h1>
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li class="active"><a href="#/realms/{{realm.realm}}/roles">Realm Roles</a></li> <li class="active"><a href="#/realms/{{realm.realm}}/roles">Realm Roles</a></li>

View file

@ -4,7 +4,7 @@
<li>{{user.username}}</li> <li>{{user.username}}</li>
</ol> </ol>
<h1><strong>User</strong> {{user.username|capitalize}}</h1> <h1>{{user.username|capitalize}}</h1>
<kc-tabs-user></kc-tabs-user> <kc-tabs-user></kc-tabs-user>

View file

@ -1,5 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1><strong>Sessions</strong> {{realm.realm|capitalize}}</h1> <h1>Sessions</h1>
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li class="active"><a href="#/realms/{{realm.realm}}/sessions/realm">Realm Sessions</a></li> <li class="active"><a href="#/realms/{{realm.realm}}/sessions/realm">Realm Sessions</a></li>

View file

@ -1,5 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1><strong>Sessions</strong> {{realm.realm|capitalize}}</h1> <h1>Sessions</h1>
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li><a href="#/realms/{{realm.realm}}/sessions/realm">Realm Sessions</a></li> <li><a href="#/realms/{{realm.realm}}/sessions/realm">Realm Sessions</a></li>

View file

@ -4,7 +4,7 @@
<li>{{user.username}}</li> <li>{{user.username}}</li>
</ol> </ol>
<h1><strong>User</strong> {{user.username|capitalize}}</h1> <h1>{{user.username|capitalize}}</h1>
<kc-tabs-user></kc-tabs-user> <kc-tabs-user></kc-tabs-user>

View file

@ -4,7 +4,7 @@
<li>{{user.username}}</li> <li>{{user.username}}</li>
</ol> </ol>
<h1><strong>User</strong> {{user.username|capitalize}}</h1> <h1>{{user.username|capitalize}}</h1>
<kc-tabs-user></kc-tabs-user> <kc-tabs-user></kc-tabs-user>

View file

@ -5,7 +5,7 @@
<li data-ng-show="create">Add User</li> <li data-ng-show="create">Add User</li>
</ol> </ol>
<h1 data-ng-hide="create"><strong>User</strong> {{user.username|capitalize}}</h1> <h1 data-ng-hide="create">{{user.username|capitalize}}</h1>
<h1 data-ng-show="create">Add User</h1> <h1 data-ng-show="create">Add User</h1>
<kc-tabs-user></kc-tabs-user> <kc-tabs-user></kc-tabs-user>

View file

@ -4,7 +4,7 @@
<li>{{user.username}}</li> <li>{{user.username}}</li>
</ol> </ol>
<h1><strong>User</strong> {{user.username|capitalize}}</h1> <h1>{{user.username|capitalize}}</h1>
<kc-tabs-user></kc-tabs-user> <kc-tabs-user></kc-tabs-user>

View file

@ -1,6 +1,6 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1> <h1>
<span><strong>User Federation</strong> {{realm.realm|capitalize}}</span> <span>User Federation</span>
</h1> </h1>
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">

View file

@ -1,5 +1,5 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1><strong>Users</strong> {{realm.realm|capitalize}}</h1> <h1>Users</h1>
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
<caption data-ng-show="users" class="hidden">Table of realm users</caption> <caption data-ng-show="users" class="hidden">Table of realm users</caption>

View file

@ -4,7 +4,7 @@
<li>{{user.username}}</li> <li>{{user.username}}</li>
</ol> </ol>
<h1><strong>User</strong> {{user.username|capitalize}}</h1> <h1>{{user.username|capitalize}}</h1>
<kc-tabs-user></kc-tabs-user> <kc-tabs-user></kc-tabs-user>

View file

@ -61,53 +61,6 @@
</div> </div>
</div> </div>
<div class="form-group">
<div class="${properties.kcLabelWrapperClass!}">
<label for="user.attributes.street" class="${properties.kcLabelClass!}">${msg("street")}</label>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="text" class="${properties.kcInputClass!}" id="user.attributes.street" name="user.attributes.street"/>
</div>
</div>
<div class="form-group">
<div class="${properties.kcLabelWrapperClass!}">
<label for="user.attributes.locality" class="${properties.kcLabelClass!}">${msg("locality")}</label>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="text" class="${properties.kcInputClass!}" id="user.attributes.locality" name="user.attributes.locality"/>
</div>
</div>
<div class="form-group">
<div class="${properties.kcLabelWrapperClass!}">
<label for="user.attributes.region" class="${properties.kcLabelClass!}">${msg("region")}</label>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="text" class="${properties.kcInputClass!}" id="user.attributes.region" name="user.attributes.region"/>
</div>
</div>
<div class="form-group">
<div class="${properties.kcLabelWrapperClass!}">
<label for="user.attributes.postal_code" class="${properties.kcLabelClass!}">${msg("postal_code")}</label>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="text" class="${properties.kcInputClass!}" id="user.attributes.postal_code" name="user.attributes.postal_code"/>
</div>
</div>
<div class="form-group">
<div class="${properties.kcLabelWrapperClass!}">
<label for="user.attributes.country" class="${properties.kcLabelClass!}">${msg("country")}</label>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="text" class="${properties.kcInputClass!}" id="user.attributes.country" name="user.attributes.country"/>
</div>
</div>
<div class="${properties.kcFormGroupClass!}"> <div class="${properties.kcFormGroupClass!}">
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}"> <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
<div class="${properties.kcFormOptionsWrapperClass!}"> <div class="${properties.kcFormOptionsWrapperClass!}">

View file

@ -121,11 +121,6 @@
display: block; display: block;
} }
#kc-login {
float: right;
margin-left: 10px;
}
#kc-feedback-wrapper { #kc-feedback-wrapper {
display: inline-block; display: inline-block;
width: auto; width: auto;

View file

@ -374,11 +374,21 @@ public class SamlService {
for (String sessionIndex : logoutRequest.getSessionIndex()) { for (String sessionIndex : logoutRequest.getSessionIndex()) {
ClientSessionModel clientSession = session.sessions().getClientSession(realm, sessionIndex); ClientSessionModel clientSession = session.sessions().getClientSession(realm, sessionIndex);
if (clientSession == null) continue; if (clientSession == null) continue;
UserSessionModel userSession = clientSession.getUserSession();
if (clientSession.getClient().getClientId().equals(client.getClientId())) { if (clientSession.getClient().getClientId().equals(client.getClientId())) {
// remove requesting client from logout // remove requesting client from logout
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT); clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT);
// Remove also other clientSessions of this client as there could be more in this UserSession
if (userSession != null) {
for (ClientSessionModel clientSession2 : userSession.getClientSessions()) {
if (clientSession2.getClient().getId().equals(client.getId())) {
clientSession2.setAction(ClientSessionModel.Action.LOGGED_OUT);
}
}
}
} }
UserSessionModel userSession = clientSession.getUserSession();
try { try {
authManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, true); authManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, true);
} catch (Exception e) { } catch (Exception e) {

View file

@ -3,10 +3,11 @@ package org.keycloak.social.facebook;
import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.JsonNode;
import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider; import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig; import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper;
import org.keycloak.broker.oidc.util.JsonSimpleHttp; import org.keycloak.broker.oidc.util.JsonSimpleHttp;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.broker.provider.BrokeredIdentityContext; import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException; import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.social.SocialIdentityProvider; import org.keycloak.social.SocialIdentityProvider;
/** /**
@ -14,63 +15,65 @@ import org.keycloak.social.SocialIdentityProvider;
*/ */
public class FacebookIdentityProvider extends AbstractOAuth2IdentityProvider implements SocialIdentityProvider { public class FacebookIdentityProvider extends AbstractOAuth2IdentityProvider implements SocialIdentityProvider {
public static final String AUTH_URL = "https://graph.facebook.com/oauth/authorize"; public static final String AUTH_URL = "https://graph.facebook.com/oauth/authorize";
public static final String TOKEN_URL = "https://graph.facebook.com/oauth/access_token"; public static final String TOKEN_URL = "https://graph.facebook.com/oauth/access_token";
public static final String PROFILE_URL = "https://graph.facebook.com/me"; public static final String PROFILE_URL = "https://graph.facebook.com/me";
public static final String DEFAULT_SCOPE = "email"; public static final String DEFAULT_SCOPE = "email";
public FacebookIdentityProvider(OAuth2IdentityProviderConfig config) { public FacebookIdentityProvider(OAuth2IdentityProviderConfig config) {
super(config); super(config);
config.setAuthorizationUrl(AUTH_URL); config.setAuthorizationUrl(AUTH_URL);
config.setTokenUrl(TOKEN_URL); config.setTokenUrl(TOKEN_URL);
config.setUserInfoUrl(PROFILE_URL); config.setUserInfoUrl(PROFILE_URL);
} }
protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) { protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
try { try {
JsonNode profile = JsonSimpleHttp.asJson(SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken)); JsonNode profile = JsonSimpleHttp.asJson(SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken));
String id = getJsonProperty(profile, "id"); String id = getJsonProperty(profile, "id");
BrokeredIdentityContext user = new BrokeredIdentityContext(id); BrokeredIdentityContext user = new BrokeredIdentityContext(id);
String email = getJsonProperty(profile, "email"); String email = getJsonProperty(profile, "email");
user.setEmail(email); user.setEmail(email);
String username = getJsonProperty(profile, "username"); String username = getJsonProperty(profile, "username");
if (username == null) { if (username == null) {
if (email != null) { if (email != null) {
username = email; username = email;
} else { } else {
username = id; username = id;
} }
} }
user.setUsername(username); user.setUsername(username);
String firstName = getJsonProperty(profile, "first_name"); String firstName = getJsonProperty(profile, "first_name");
String lastName = getJsonProperty(profile, "last_name"); String lastName = getJsonProperty(profile, "last_name");
if (lastName == null) { if (lastName == null) {
lastName = ""; lastName = "";
} else { } else {
lastName = " " + lastName; lastName = " " + lastName;
} }
user.setName(firstName + lastName); user.setName(firstName + lastName);
user.setIdpConfig(getConfig()); user.setIdpConfig(getConfig());
user.setIdp(this); user.setIdp(this);
return user; AbstractJsonUserAttributeMapper.storeUserProfileForMapper(user, profile, getConfig().getAlias());
} catch (Exception e) {
throw new IdentityBrokerException("Could not obtain user profile from facebook.", e);
}
}
@Override return user;
protected String getDefaultScopes() { } catch (Exception e) {
return DEFAULT_SCOPE; throw new IdentityBrokerException("Could not obtain user profile from facebook.", e);
} }
}
@Override
protected String getDefaultScopes() {
return DEFAULT_SCOPE;
}
} }

View file

@ -0,0 +1,29 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
*/
package org.keycloak.social.facebook;
import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper;
/**
* User attribute mapper.
*
* @author Vlastimil Elias (velias at redhat dot com)
*/
public class FacebookUserAttributeMapper extends AbstractJsonUserAttributeMapper {
private static final String[] cp = new String[] { FacebookIdentityProviderFactory.PROVIDER_ID };
@Override
public String[] getCompatibleProviders() {
return cp;
}
@Override
public String getId() {
return "facebook-user-attribute-mapper";
}
}

View file

@ -0,0 +1 @@
org.keycloak.social.facebook.FacebookUserAttributeMapper

View file

@ -3,10 +3,11 @@ package org.keycloak.social.github;
import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.JsonNode;
import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider; import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig; import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper;
import org.keycloak.broker.oidc.util.JsonSimpleHttp; import org.keycloak.broker.oidc.util.JsonSimpleHttp;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.broker.provider.BrokeredIdentityContext; import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException; import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.social.SocialIdentityProvider; import org.keycloak.social.SocialIdentityProvider;
/** /**
@ -14,40 +15,42 @@ import org.keycloak.social.SocialIdentityProvider;
*/ */
public class GitHubIdentityProvider extends AbstractOAuth2IdentityProvider implements SocialIdentityProvider { public class GitHubIdentityProvider extends AbstractOAuth2IdentityProvider implements SocialIdentityProvider {
public static final String AUTH_URL = "https://github.com/login/oauth/authorize"; public static final String AUTH_URL = "https://github.com/login/oauth/authorize";
public static final String TOKEN_URL = "https://github.com/login/oauth/access_token"; public static final String TOKEN_URL = "https://github.com/login/oauth/access_token";
public static final String PROFILE_URL = "https://api.github.com/user"; public static final String PROFILE_URL = "https://api.github.com/user";
public static final String DEFAULT_SCOPE = "user:email"; public static final String DEFAULT_SCOPE = "user:email";
public GitHubIdentityProvider(OAuth2IdentityProviderConfig config) { public GitHubIdentityProvider(OAuth2IdentityProviderConfig config) {
super(config); super(config);
config.setAuthorizationUrl(AUTH_URL); config.setAuthorizationUrl(AUTH_URL);
config.setTokenUrl(TOKEN_URL); config.setTokenUrl(TOKEN_URL);
config.setUserInfoUrl(PROFILE_URL); config.setUserInfoUrl(PROFILE_URL);
} }
@Override @Override
protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) { protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
try { try {
JsonNode profile = JsonSimpleHttp.asJson(SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken)); JsonNode profile = JsonSimpleHttp.asJson(SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken));
BrokeredIdentityContext user = new BrokeredIdentityContext(getJsonProperty(profile, "id")); BrokeredIdentityContext user = new BrokeredIdentityContext(getJsonProperty(profile, "id"));
String username = getJsonProperty(profile, "login"); String username = getJsonProperty(profile, "login");
user.setUsername(username); user.setUsername(username);
user.setName(getJsonProperty(profile, "name")); user.setName(getJsonProperty(profile, "name"));
user.setEmail(getJsonProperty(profile, "email")); user.setEmail(getJsonProperty(profile, "email"));
user.setIdpConfig(getConfig()); user.setIdpConfig(getConfig());
user.setIdp(this); user.setIdp(this);
return user; AbstractJsonUserAttributeMapper.storeUserProfileForMapper(user, profile, getConfig().getAlias());
} catch (Exception e) {
throw new IdentityBrokerException("Could not obtain user profile from github.", e);
}
}
@Override return user;
protected String getDefaultScopes() { } catch (Exception e) {
return DEFAULT_SCOPE; throw new IdentityBrokerException("Could not obtain user profile from github.", e);
} }
}
@Override
protected String getDefaultScopes() {
return DEFAULT_SCOPE;
}
} }

View file

@ -0,0 +1,29 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
*/
package org.keycloak.social.github;
import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper;
/**
* User attribute mapper.
*
* @author Vlastimil Elias (velias at redhat dot com)
*/
public class GitHubUserAttributeMapper extends AbstractJsonUserAttributeMapper {
private static final String[] cp = new String[] { GitHubIdentityProviderFactory.PROVIDER_ID };
@Override
public String[] getCompatibleProviders() {
return cp;
}
@Override
public String getId() {
return "github-user-attribute-mapper";
}
}

View file

@ -0,0 +1 @@
org.keycloak.social.github.GitHubUserAttributeMapper

View file

@ -0,0 +1,29 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
*/
package org.keycloak.social.google;
import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper;
/**
* User attribute mapper.
*
* @author Vlastimil Elias (velias at redhat dot com)
*/
public class GoogleUserAttributeMapper extends AbstractJsonUserAttributeMapper {
private static final String[] cp = new String[] { GoogleIdentityProviderFactory.PROVIDER_ID };
@Override
public String[] getCompatibleProviders() {
return cp;
}
@Override
public String getId() {
return "google-user-attribute-mapper";
}
}

View file

@ -0,0 +1 @@
org.keycloak.social.google.GoogleUserAttributeMapper

View file

@ -25,10 +25,11 @@ import org.codehaus.jackson.JsonNode;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider; import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig; import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper;
import org.keycloak.broker.oidc.util.JsonSimpleHttp; import org.keycloak.broker.oidc.util.JsonSimpleHttp;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.broker.provider.BrokeredIdentityContext; import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException; import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.social.SocialIdentityProvider; import org.keycloak.social.SocialIdentityProvider;
/** /**
@ -58,16 +59,18 @@ public class LinkedInIdentityProvider extends AbstractOAuth2IdentityProvider imp
try { try {
JsonNode profile = JsonSimpleHttp.asJson(SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken)); JsonNode profile = JsonSimpleHttp.asJson(SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken));
BrokeredIdentityContext user = new BrokeredIdentityContext(getJsonProperty(profile, "id")); BrokeredIdentityContext user = new BrokeredIdentityContext(getJsonProperty(profile, "id"));
String username = extractUsernameFromProfileURL(getJsonProperty(profile, "publicProfileUrl")); String username = extractUsernameFromProfileURL(getJsonProperty(profile, "publicProfileUrl"));
user.setUsername(username); user.setUsername(username);
user.setName(getJsonProperty(profile, "formattedName")); user.setName(getJsonProperty(profile, "formattedName"));
user.setEmail(getJsonProperty(profile, "emailAddress")); user.setEmail(getJsonProperty(profile, "emailAddress"));
user.setIdpConfig(getConfig()); user.setIdpConfig(getConfig());
user.setIdp(this); user.setIdp(this);
return user; AbstractJsonUserAttributeMapper.storeUserProfileForMapper(user, profile, getConfig().getAlias());
return user;
} catch (Exception e) { } catch (Exception e) {
throw new IdentityBrokerException("Could not obtain user profile from linkedIn.", e); throw new IdentityBrokerException("Could not obtain user profile from linkedIn.", e);
} }

View file

@ -0,0 +1,29 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
*/
package org.keycloak.social.linkedin;
import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper;
/**
* User attribute mapper.
*
* @author Vlastimil Elias (velias at redhat dot com)
*/
public class LinkedInUserAttributeMapper extends AbstractJsonUserAttributeMapper {
private static final String[] cp = new String[] { LinkedInIdentityProviderFactory.PROVIDER_ID };
@Override
public String[] getCompatibleProviders() {
return cp;
}
@Override
public String getId() {
return "linkedin-user-attribute-mapper";
}
}

View file

@ -0,0 +1 @@
org.keycloak.social.linkedin.LinkedInUserAttributeMapper

View file

@ -26,10 +26,11 @@ import java.util.HashMap;
import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.JsonNode;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider; import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper;
import org.keycloak.broker.oidc.util.JsonSimpleHttp; import org.keycloak.broker.oidc.util.JsonSimpleHttp;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.broker.provider.BrokeredIdentityContext; import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException; import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.social.SocialIdentityProvider; import org.keycloak.social.SocialIdentityProvider;
/** /**
@ -37,8 +38,7 @@ import org.keycloak.social.SocialIdentityProvider;
* *
* @author Vlastimil Elias (velias at redhat dot com) * @author Vlastimil Elias (velias at redhat dot com)
*/ */
public class StackoverflowIdentityProvider extends AbstractOAuth2IdentityProvider<StackOverflowIdentityProviderConfig> public class StackoverflowIdentityProvider extends AbstractOAuth2IdentityProvider<StackOverflowIdentityProviderConfig> implements SocialIdentityProvider<StackOverflowIdentityProviderConfig> {
implements SocialIdentityProvider<StackOverflowIdentityProviderConfig> {
private static final Logger log = Logger.getLogger(StackoverflowIdentityProvider.class); private static final Logger log = Logger.getLogger(StackoverflowIdentityProvider.class);
@ -54,8 +54,6 @@ public class StackoverflowIdentityProvider extends AbstractOAuth2IdentityProvide
config.setUserInfoUrl(PROFILE_URL); config.setUserInfoUrl(PROFILE_URL);
} }
@Override @Override
protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) { protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
log.debug("doGetFederatedIdentity()"); log.debug("doGetFederatedIdentity()");
@ -67,18 +65,19 @@ public class StackoverflowIdentityProvider extends AbstractOAuth2IdentityProvide
} }
JsonNode profile = JsonSimpleHttp.asJson(SimpleHttp.doGet(URL)).get("items").get(0); JsonNode profile = JsonSimpleHttp.asJson(SimpleHttp.doGet(URL)).get("items").get(0);
BrokeredIdentityContext user = new BrokeredIdentityContext(getJsonProperty(profile, "user_id")); BrokeredIdentityContext user = new BrokeredIdentityContext(getJsonProperty(profile, "user_id"));
String username = extractUsernameFromProfileURL(getJsonProperty(profile, "link")); String username = extractUsernameFromProfileURL(getJsonProperty(profile, "link"));
user.setUsername(username); user.setUsername(username);
user.setName(unescapeHtml3(getJsonProperty(profile, "display_name"))); user.setName(unescapeHtml3(getJsonProperty(profile, "display_name")));
// email is not provided // email is not provided
// user.setEmail(getJsonProperty(profile, "email")); // user.setEmail(getJsonProperty(profile, "email"));
user.setIdpConfig(getConfig()); user.setIdpConfig(getConfig());
user.setIdp(this); user.setIdp(this);
AbstractJsonUserAttributeMapper.storeUserProfileForMapper(user, profile, getConfig().getAlias());
return user; return user;
} catch (Exception e) { } catch (Exception e) {
throw new IdentityBrokerException("Could not obtain user profile from Stackoverflow: " + e.getMessage(), e); throw new IdentityBrokerException("Could not obtain user profile from Stackoverflow: " + e.getMessage(), e);
} }

View file

@ -0,0 +1,29 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
*/
package org.keycloak.social.stackoverflow;
import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper;
/**
* User attribute mapper.
*
* @author Vlastimil Elias (velias at redhat dot com)
*/
public class StackoverflowUserAttributeMapper extends AbstractJsonUserAttributeMapper {
private static final String[] cp = new String[] { StackoverflowIdentityProviderFactory.PROVIDER_ID };
@Override
public String[] getCompatibleProviders() {
return cp;
}
@Override
public String getId() {
return "stackoverflow-user-attribute-mapper";
}
}

View file

@ -0,0 +1 @@
org.keycloak.social.stackoverflow.StackoverflowUserAttributeMapper

View file

@ -45,6 +45,11 @@ public class SAMLKeyCloakServerBrokerWithSignatureTest extends AbstractIdentityP
} }
}; };
// @Test
public void testSleep() throws Exception {
Thread.sleep(100000000);
}
@Override @Override
protected String getProviderId() { protected String getProviderId() {
return "kc-saml-signed-idp"; return "kc-saml-signed-idp";

View file

@ -247,7 +247,7 @@ public class FederationProvidersIntegrationTest {
loginPage.clickRegister(); loginPage.clickRegister();
registerPage.assertCurrent(); registerPage.assertCurrent();
registerPage.register("firstName", "lastName", "email2@check.cz", "registerUserSuccess2", "Password1", "Password1", "non-LDAP-Mapped street", null, null, "78910", null); registerPage.register("firstName", "lastName", "email2@check.cz", "registerUserSuccess2", "Password1", "Password1");
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType()); Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
KeycloakSession session = keycloakRule.startSession(); KeycloakSession session = keycloakRule.startSession();
@ -257,8 +257,6 @@ public class FederationProvidersIntegrationTest {
Assert.assertNotNull(user); Assert.assertNotNull(user);
Assert.assertNotNull(user.getFederationLink()); Assert.assertNotNull(user.getFederationLink());
Assert.assertEquals(user.getFederationLink(), ldapModel.getId()); Assert.assertEquals(user.getFederationLink(), ldapModel.getId());
Assert.assertEquals("78910", user.getAttribute("postal_code"));
Assert.assertEquals("non-LDAP-Mapped street", user.getAttribute("street"));
} finally { } finally {
keycloakRule.stopSession(session, false); keycloakRule.stopSession(session, false);
} }

View file

@ -57,25 +57,6 @@ public class RegisterPage extends AbstractPage {
@FindBy(className = "feedback-error") @FindBy(className = "feedback-error")
private WebElement loginErrorMessage; private WebElement loginErrorMessage;
public void register(String firstName, String lastName, String email, String username, String password, String passwordConfirm,
String street, String cityOrLocality, String stateOrRegion, String zipOrPostalCode, String country) {
fillExtendedField("street", street);
fillExtendedField("locality", cityOrLocality);
fillExtendedField("region", stateOrRegion);
fillExtendedField("postal_code", zipOrPostalCode);
fillExtendedField("country", country);
register(firstName, lastName, email, username, password, passwordConfirm);
}
private void fillExtendedField(String fieldName, String value) {
WebElement field = driver.findElement(By.id("user.attributes." + fieldName));
field.clear();
if (value != null) {
field.sendKeys(value);
}
}
public void register(String firstName, String lastName, String email, String username, String password, String passwordConfirm) { public void register(String firstName, String lastName, String email, String username, String password, String passwordConfirm) {
firstNameInput.clear(); firstNameInput.clear();
if (firstName != null) { if (firstName != null) {