conflict
This commit is contained in:
commit
7dc05a45ac
107 changed files with 1616 additions and 375 deletions
|
@ -58,6 +58,11 @@ public abstract class AbstractIdentityProvider<C extends IdentityProviderModel>
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response performLogin(AuthenticationRequest request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
|
||||
return null;
|
||||
|
|
|
@ -8,6 +8,12 @@
|
|||
<delete tableName="CLIENT_SESSION"/>
|
||||
<delete tableName="USER_SESSION_NOTE"/>
|
||||
<delete tableName="USER_SESSION"/>
|
||||
|
||||
<addColumn tableName="CLIENT">
|
||||
<column name="SERVICE_ACCOUNTS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<addColumn tableName="CLIENT_SESSION">
|
||||
<column name="CURRENT_ACTION" type="VARCHAR(36)">
|
||||
<constraints nullable="true"/>
|
||||
|
|
|
@ -29,6 +29,8 @@ public interface OAuth2Constants {
|
|||
|
||||
String PASSWORD = "password";
|
||||
|
||||
String CLIENT_CREDENTIALS = "client_credentials";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package org.keycloak.constants;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface ServiceAccountConstants {
|
||||
|
||||
String CLIENT_AUTH = "client_auth";
|
||||
|
||||
String SERVICE_ACCOUNT_USER_PREFIX = "service-account-";
|
||||
String SERVICE_ACCOUNT_CLIENT_ATTRIBUTE = "serviceAccountClient";
|
||||
|
||||
String CLIENT_ID_PROTOCOL_MAPPER = "Client ID";
|
||||
String CLIENT_HOST_PROTOCOL_MAPPER = "Client Host";
|
||||
String CLIENT_ADDRESS_PROTOCOL_MAPPER = "Client IP Address";
|
||||
String CLIENT_ID = "clientId";
|
||||
String CLIENT_HOST = "clientHost";
|
||||
String CLIENT_ADDRESS = "clientAddress";
|
||||
|
||||
}
|
|
@ -22,6 +22,7 @@ public class ClientRepresentation {
|
|||
protected Integer notBefore;
|
||||
protected Boolean bearerOnly;
|
||||
protected Boolean consentRequired;
|
||||
protected Boolean serviceAccountsEnabled;
|
||||
protected Boolean directGrantsOnly;
|
||||
protected Boolean publicClient;
|
||||
protected Boolean frontchannelLogout;
|
||||
|
@ -144,6 +145,14 @@ public class ClientRepresentation {
|
|||
this.consentRequired = consentRequired;
|
||||
}
|
||||
|
||||
public Boolean isServiceAccountsEnabled() {
|
||||
return serviceAccountsEnabled;
|
||||
}
|
||||
|
||||
public void setServiceAccountsEnabled(Boolean serviceAccountsEnabled) {
|
||||
this.serviceAccountsEnabled = serviceAccountsEnabled;
|
||||
}
|
||||
|
||||
public Boolean isDirectGrantsOnly() {
|
||||
return directGrantsOnly;
|
||||
}
|
||||
|
|
|
@ -35,4 +35,10 @@ public interface Details {
|
|||
String IMPERSONATOR_REALM = "impersonator_realm";
|
||||
String IMPERSONATOR = "impersonator";
|
||||
|
||||
String CLIENT_AUTH_METHOD = "client_auth_method";
|
||||
String CLIENT_AUTH_METHOD_VALUE_CLIENT_CREDENTIALS = "client_credentials";
|
||||
String CLIENT_AUTH_METHOD_VALUE_CERTIFICATE = "client_certificate";
|
||||
String CLIENT_AUTH_METHOD_VALUE_KERBEROS_KEYTAB = "kerberos_keytab";
|
||||
String CLIENT_AUTH_METHOD_VALUE_SIGNED_JWT = "signed_jwt";
|
||||
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@ public enum EventType {
|
|||
CODE_TO_TOKEN(true),
|
||||
CODE_TO_TOKEN_ERROR(true),
|
||||
|
||||
CLIENT_LOGIN(true),
|
||||
CLIENT_LOGIN_ERROR(true),
|
||||
|
||||
REFRESH_TOKEN(false),
|
||||
REFRESH_TOKEN_ERROR(false),
|
||||
VALIDATE_ACCESS_TOKEN(false),
|
||||
|
|
|
@ -210,6 +210,14 @@ An pure HTML5/Javascript example using Keycloak to secure it.
|
|||
If you are already logged in, you will not be asked for a username and password, but you will be redirected to
|
||||
an oauth grant page. This page asks you if you want to grant certain permissions to the third-part app.
|
||||
|
||||
Step 10: Service Account Example
|
||||
================================
|
||||
An example for retrieve service account dedicated to the Client Application itself (not to any user).
|
||||
|
||||
[http://localhost:8080/service-account-portal](http://localhost:8080/service-account-portal)
|
||||
|
||||
Client authentication is done with OAuth2 Client Credentials Grant in out-of-bound request (Not Keycloak login screen displayed)
|
||||
|
||||
Admin Console
|
||||
==========================
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
<module>database-service</module>
|
||||
<module>third-party</module>
|
||||
<module>third-party-cdi</module>
|
||||
<module>service-account</module>
|
||||
</modules>
|
||||
|
||||
<profiles>
|
||||
|
|
60
examples/demo-template/service-account/pom.xml
Normal file
60
examples/demo-template/service-account/pom.xml
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-examples-demo-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.4.0.Final-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.keycloak.example.demo</groupId>
|
||||
<artifactId>service-account-example</artifactId>
|
||||
<packaging>war</packaging>
|
||||
<name>Service Account Example App</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.spec.javax.servlet</groupId>
|
||||
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-adapter-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>service-account-portal</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jboss.as.plugins</groupId>
|
||||
<artifactId>jboss-as-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>false</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.wildfly.plugins</groupId>
|
||||
<artifactId>wildfly-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>false</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,234 @@
|
|||
package org.keycloak.example;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.RSATokenVerifier;
|
||||
import org.keycloak.VerificationException;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.keycloak.adapters.ServerRequest;
|
||||
import org.keycloak.constants.ServiceAccountConstants;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.util.BasicAuthHelper;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ProductServiceAccountServlet extends HttpServlet {
|
||||
|
||||
public static final String ERROR = "error";
|
||||
public static final String TOKEN = "token";
|
||||
public static final String TOKEN_PARSED = "idTokenParsed";
|
||||
public static final String REFRESH_TOKEN = "refreshToken";
|
||||
public static final String PRODUCTS = "products";
|
||||
|
||||
@Override
|
||||
public void init() throws ServletException {
|
||||
InputStream config = getServletContext().getResourceAsStream("WEB-INF/keycloak.json");
|
||||
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config);
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
|
||||
getServletContext().setAttribute(KeycloakDeployment.class.getName(), deployment);
|
||||
getServletContext().setAttribute(HttpClient.class.getName(), client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
getHttpClient().getConnectionManager().shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
String reqUri = req.getRequestURI();
|
||||
if (reqUri.endsWith("/login")) {
|
||||
serviceAccountLogin(req);
|
||||
} else if (reqUri.endsWith("/refresh")) {
|
||||
refreshToken(req);
|
||||
} else if (reqUri.endsWith("/logout")){
|
||||
logout(req);
|
||||
}
|
||||
|
||||
// Don't load products if some error happened during login,refresh or logout
|
||||
if (req.getAttribute(ERROR) == null) {
|
||||
loadProducts(req);
|
||||
}
|
||||
|
||||
req.getRequestDispatcher("/WEB-INF/page.jsp").forward(req, resp);
|
||||
}
|
||||
|
||||
private void serviceAccountLogin(HttpServletRequest req) {
|
||||
KeycloakDeployment deployment = getKeycloakDeployment();
|
||||
HttpClient client = getHttpClient();
|
||||
|
||||
String clientId = deployment.getResourceName();
|
||||
String clientSecret = deployment.getResourceCredentials().get("secret");
|
||||
|
||||
try {
|
||||
HttpPost post = new HttpPost(deployment.getTokenUrl());
|
||||
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
|
||||
formparams.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS));
|
||||
|
||||
String authHeader = BasicAuthHelper.createHeader(clientId, clientSecret);
|
||||
post.addHeader("Authorization", authHeader);
|
||||
|
||||
UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
|
||||
post.setEntity(form);
|
||||
|
||||
HttpResponse response = client.execute(post);
|
||||
int status = response.getStatusLine().getStatusCode();
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (status != 200) {
|
||||
String json = getContent(entity);
|
||||
String error = "Service account login failed. Bad status: " + status + " response: " + json;
|
||||
req.setAttribute(ERROR, error);
|
||||
} else if (entity == null) {
|
||||
req.setAttribute(ERROR, "No entity");
|
||||
} else {
|
||||
String json = getContent(entity);
|
||||
AccessTokenResponse tokenResp = JsonSerialization.readValue(json, AccessTokenResponse.class);
|
||||
setTokens(req, deployment, tokenResp);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
req.setAttribute(ERROR, "Service account login failed. IOException occured. See server.log for details. Message is: " + ioe.getMessage());
|
||||
} catch (VerificationException vfe) {
|
||||
req.setAttribute(ERROR, "Service account login failed. Failed to verify token Message is: " + vfe.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void setTokens(HttpServletRequest req, KeycloakDeployment deployment, AccessTokenResponse tokenResponse) throws IOException, VerificationException {
|
||||
String token = tokenResponse.getToken();
|
||||
String refreshToken = tokenResponse.getRefreshToken();
|
||||
AccessToken tokenParsed = RSATokenVerifier.verifyToken(token, deployment.getRealmKey(), deployment.getRealmInfoUrl());
|
||||
req.getSession().setAttribute(TOKEN, token);
|
||||
req.getSession().setAttribute(REFRESH_TOKEN, refreshToken);
|
||||
req.getSession().setAttribute(TOKEN_PARSED, tokenParsed);
|
||||
}
|
||||
|
||||
private void loadProducts(HttpServletRequest req) {
|
||||
HttpClient client = getHttpClient();
|
||||
String token = (String) req.getSession().getAttribute(TOKEN);
|
||||
|
||||
HttpGet get = new HttpGet("http://localhost:8080/database/products");
|
||||
if (token != null) {
|
||||
get.addHeader("Authorization", "Bearer " + token);
|
||||
}
|
||||
try {
|
||||
HttpResponse response = client.execute(get);
|
||||
HttpEntity entity = response.getEntity();
|
||||
int status = response.getStatusLine().getStatusCode();
|
||||
if (status != 200) {
|
||||
String json = getContent(entity);
|
||||
String error = "Failed retrieve products.";
|
||||
|
||||
if (status == 401) {
|
||||
error = error + " You need to login first with the service account.";
|
||||
} else if (status == 403) {
|
||||
error = error + " Maybe service account user doesn't have needed role? Assign role 'user' in Keycloak admin console to user '" +
|
||||
ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + getKeycloakDeployment().getResourceName() + "' and then logout and login again.";
|
||||
}
|
||||
error = error + " Status: " + status + ", Response: " + json;
|
||||
req.setAttribute(ERROR, error);
|
||||
} else if (entity == null) {
|
||||
req.setAttribute(ERROR, "No entity");
|
||||
} else {
|
||||
String products = getContent(entity);
|
||||
req.setAttribute(PRODUCTS, products);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
req.setAttribute(ERROR, "Failed retrieve products. IOException occured. See server.log for details. Message is: " + ioe.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshToken(HttpServletRequest req) {
|
||||
KeycloakDeployment deployment = getKeycloakDeployment();
|
||||
String refreshToken = (String) req.getSession().getAttribute(REFRESH_TOKEN);
|
||||
if (refreshToken == null) {
|
||||
req.setAttribute(ERROR, "No refresh token available. Please login first");
|
||||
} else {
|
||||
try {
|
||||
AccessTokenResponse tokenResponse = ServerRequest.invokeRefresh(deployment, refreshToken);
|
||||
setTokens(req, deployment, tokenResponse);
|
||||
} catch (ServerRequest.HttpFailure hfe) {
|
||||
hfe.printStackTrace();
|
||||
req.setAttribute(ERROR, "Failed refresh token. See server.log for details. Status was: " + hfe.getStatus() + ", Error is: " + hfe.getError());
|
||||
} catch (Exception ioe) {
|
||||
ioe.printStackTrace();
|
||||
req.setAttribute(ERROR, "Failed refresh token. See server.log for details. Message is: " + ioe.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void logout(HttpServletRequest req) {
|
||||
KeycloakDeployment deployment = getKeycloakDeployment();
|
||||
String refreshToken = (String) req.getSession().getAttribute(REFRESH_TOKEN);
|
||||
if (refreshToken == null) {
|
||||
req.setAttribute(ERROR, "No refresh token available. Please login first");
|
||||
} else {
|
||||
try {
|
||||
ServerRequest.invokeLogout(deployment, refreshToken);
|
||||
req.getSession().removeAttribute(TOKEN);
|
||||
req.getSession().removeAttribute(REFRESH_TOKEN);
|
||||
req.getSession().removeAttribute(TOKEN_PARSED);
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
req.setAttribute(ERROR, "Failed refresh token. See server.log for details. Message is: " + ioe.getMessage());
|
||||
} catch (ServerRequest.HttpFailure hfe) {
|
||||
hfe.printStackTrace();
|
||||
req.setAttribute(ERROR, "Failed refresh token. See server.log for details. Status was: " + hfe.getStatus() + ", Error is: " + hfe.getError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getContent(HttpEntity entity) throws IOException {
|
||||
if (entity == null) return null;
|
||||
InputStream is = entity.getContent();
|
||||
try {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
int c;
|
||||
while ((c = is.read()) != -1) {
|
||||
os.write(c);
|
||||
}
|
||||
byte[] bytes = os.toByteArray();
|
||||
String data = new String(bytes);
|
||||
return data;
|
||||
} finally {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException ignored) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private KeycloakDeployment getKeycloakDeployment() {
|
||||
return (KeycloakDeployment) getServletContext().getAttribute(KeycloakDeployment.class.getName());
|
||||
}
|
||||
|
||||
private HttpClient getHttpClient() {
|
||||
return (HttpClient) getServletContext().getAttribute(HttpClient.class.getName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<jboss-deployment-structure>
|
||||
<deployment>
|
||||
<dependencies>
|
||||
<!-- the Demo code uses classes in these modules. These are optional to import if you are not using
|
||||
Apache Http Client or the HttpClientBuilder that comes with the adapter core -->
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
</dependencies>
|
||||
</deployment>
|
||||
</jboss-deployment-structure>
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"realm" : "demo",
|
||||
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||
"auth-server-url" : "http://localhost:8080/auth",
|
||||
"ssl-required" : "external",
|
||||
"resource" : "product-sa-client",
|
||||
"credentials": {
|
||||
"secret": "password"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
|
||||
pageEncoding="ISO-8859-1" %>
|
||||
<%@ page import="org.keycloak.example.ProductServiceAccountServlet" %>
|
||||
<%@ page import="org.keycloak.representations.AccessToken" %>
|
||||
<%@ page import="org.keycloak.constants.ServiceAccountConstants" %>
|
||||
<%@ page import="org.keycloak.util.Time" %>
|
||||
<html>
|
||||
<head>
|
||||
<title>Service account portal</title>
|
||||
</head>
|
||||
<body bgcolor="#FFFFFF">
|
||||
<%
|
||||
AccessToken token = (AccessToken) request.getSession().getAttribute(ProductServiceAccountServlet.TOKEN_PARSED);
|
||||
String products = (String) request.getAttribute(ProductServiceAccountServlet.PRODUCTS);
|
||||
String appError = (String) request.getAttribute(ProductServiceAccountServlet.ERROR);
|
||||
%>
|
||||
<h1>Service account portal</h1>
|
||||
<p><a href="/service-account-portal/app/login">Login</a> | <a href="/service-account-portal/app/refresh">Refresh token</a> | <a
|
||||
href="/service-account-portal/app/logout">Logout</a></p>
|
||||
<hr />
|
||||
|
||||
<% if (appError != null) { %>
|
||||
<p><font color="red">
|
||||
<b>Error: </b> <%= appError %>
|
||||
</font></p>
|
||||
<hr />
|
||||
<% } %>
|
||||
|
||||
<% if (token != null) { %>
|
||||
<p>
|
||||
<b>Service account available</b><br />
|
||||
Client ID: <%= token.getOtherClaims().get(ServiceAccountConstants.CLIENT_ID) %><br />
|
||||
Client hostname: <%= token.getOtherClaims().get(ServiceAccountConstants.CLIENT_HOST) %><br />
|
||||
Client address: <%= token.getOtherClaims().get(ServiceAccountConstants.CLIENT_ADDRESS) %><br />
|
||||
Token expiration: <%= Time.toDate(token.getExpiration()) %><br />
|
||||
<% if (token.isExpired()) { %>
|
||||
<font color="red">Access token is expired. You may need to refresh</font><br />
|
||||
<% } %>
|
||||
</p>
|
||||
<hr />
|
||||
<% } %>
|
||||
|
||||
<% if (products != null) { %>
|
||||
<p>
|
||||
<b>Products retrieved successfully from REST endpoint</b><br />
|
||||
Product list: <%= products %>
|
||||
</p>
|
||||
<hr />
|
||||
<% } %>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,19 @@
|
|||
<?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>service-account-portal</module-name>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>ServiceAccountExample</servlet-name>
|
||||
<servlet-class>org.keycloak.example.ProductServiceAccountServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>ServiceAccountExample</servlet-name>
|
||||
<url-pattern>/app/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Refresh" content="0; URL=app">
|
||||
</head>
|
||||
</html>
|
|
@ -162,6 +162,12 @@
|
|||
"publicClient": true,
|
||||
"directGrantsOnly": true,
|
||||
"consentRequired": true
|
||||
},
|
||||
{
|
||||
"clientId": "product-sa-client",
|
||||
"enabled": true,
|
||||
"secret": "password",
|
||||
"serviceAccountsEnabled": true
|
||||
}
|
||||
],
|
||||
"clientScopeMappings": {
|
||||
|
|
|
@ -762,6 +762,18 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
controller : 'ClientInstallationCtrl'
|
||||
})
|
||||
.when('/realms/:realm/clients/:client/service-accounts', {
|
||||
templateUrl : resourceUrl + '/partials/client-service-accounts.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
client : function(ClientLoader) {
|
||||
return ClientLoader();
|
||||
}
|
||||
},
|
||||
controller : 'ClientServiceAccountsCtrl'
|
||||
})
|
||||
.when('/create/client/:realm', {
|
||||
templateUrl : resourceUrl + '/partials/client-detail.html',
|
||||
resolve : {
|
||||
|
@ -1594,6 +1606,24 @@ module.directive('kcNavigationUser', function () {
|
|||
}
|
||||
});
|
||||
|
||||
module.directive('kcTabsIdentityProvider', function () {
|
||||
return {
|
||||
scope: true,
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
templateUrl: resourceUrl + '/templates/kc-tabs-identity-provider.html'
|
||||
}
|
||||
});
|
||||
|
||||
module.directive('kcTabsUserFederation', function () {
|
||||
return {
|
||||
scope: true,
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
templateUrl: resourceUrl + '/templates/kc-tabs-user-federation.html'
|
||||
}
|
||||
});
|
||||
|
||||
module.controller('RoleSelectorModalCtrl', function($scope, realm, config, configName, RealmRoles, Client, ClientRole, $modalInstance) {
|
||||
console.log('realm: ' + realm.realm);
|
||||
$scope.selectedRealmRole = {
|
||||
|
@ -1812,4 +1842,20 @@ module.directive('kcTooltip', function($compile) {
|
|||
$compile(label)(scope);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
module.directive( 'kcOpen', function ( $location ) {
|
||||
return function ( scope, element, attrs ) {
|
||||
var path;
|
||||
|
||||
attrs.$observe( 'kcOpen', function (val) {
|
||||
path = val;
|
||||
});
|
||||
|
||||
element.bind( 'click', function () {
|
||||
scope.$apply( function () {
|
||||
$location.path(path);
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
|
@ -4,6 +4,20 @@ Array.prototype.remove = function(from, to) {
|
|||
return this.push.apply(this, rest);
|
||||
};
|
||||
|
||||
module.controller('ClientTabCtrl', function(Dialog, $scope, Current, Notifications, $location) {
|
||||
$scope.removeClient = function() {
|
||||
Dialog.confirmDelete($scope.client.clientId, 'client', function() {
|
||||
$scope.client.$remove({
|
||||
realm : Current.realm.realm,
|
||||
client : $scope.client.id
|
||||
}, function() {
|
||||
$location.url("/realms/" + Current.realm.realm + "/clients");
|
||||
Notifications.success("The client has been deleted.");
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('ClientRoleListCtrl', function($scope, $location, realm, client, roles) {
|
||||
$scope.realm = realm;
|
||||
$scope.roles = roles;
|
||||
|
@ -834,20 +848,6 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, $route, se
|
|||
$scope.cancel = function() {
|
||||
$location.url("/realms/" + realm.realm + "/clients");
|
||||
};
|
||||
|
||||
$scope.remove = function() {
|
||||
Dialog.confirmDelete($scope.client.clientId, 'client', function() {
|
||||
$scope.client.$remove({
|
||||
realm : realm.realm,
|
||||
client : $scope.client.id
|
||||
}, function() {
|
||||
$location.url("/realms/" + realm.realm + "/clients");
|
||||
Notifications.success("The client has been deleted.");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
|
||||
module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, client, clients, Notifications,
|
||||
|
@ -1298,6 +1298,25 @@ module.controller('ClientProtocolMapperCreateCtrl', function($scope, realm, serv
|
|||
|
||||
});
|
||||
|
||||
module.controller('ClientServiceAccountsCtrl', function($scope, $http, realm, client, Notifications, Client) {
|
||||
$scope.realm = realm;
|
||||
$scope.client = angular.copy(client);
|
||||
|
||||
$scope.serviceAccountsEnabledChanged = function() {
|
||||
if (client.serviceAccountsEnabled != $scope.client.serviceAccountsEnabled) {
|
||||
Client.update({
|
||||
realm : realm.realm,
|
||||
client : client.id
|
||||
}, $scope.client, function() {
|
||||
$scope.changed = false;
|
||||
client = angular.copy($scope.client);
|
||||
Notifications.success("Service Account settings updated.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, WhoAmI, Current, $
|
|||
get impersonation() {
|
||||
return getAccess('impersonation');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
|
@ -113,6 +113,18 @@ module.controller('HomeCtrl', function(Realm, Auth, $location) {
|
|||
});
|
||||
});
|
||||
|
||||
module.controller('RealmTabCtrl', function(Dialog, $scope, Current, Realm, Notifications, $location) {
|
||||
$scope.removeRealm = function() {
|
||||
Dialog.confirmDelete(Current.realm.realm, 'realm', function() {
|
||||
Realm.remove({ id : Current.realm.realm }, function() {
|
||||
Current.realms = Realm.query();
|
||||
Notifications.success("The realm has been deleted.");
|
||||
$location.url("/");
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('RealmListCtrl', function($scope, Realm, Current) {
|
||||
$scope.realms = Realm.query();
|
||||
Current.realms = $scope.realms;
|
||||
|
@ -286,16 +298,6 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, ser
|
|||
$scope.cancel = function() {
|
||||
window.history.back();
|
||||
};
|
||||
|
||||
$scope.remove = function() {
|
||||
Dialog.confirmDelete($scope.realm.realm, 'realm', function() {
|
||||
Realm.remove({ id : $scope.realm.realm }, function() {
|
||||
Current.realms = Realm.query();
|
||||
Notifications.success("The realm has been deleted.");
|
||||
$location.url("/");
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
function genericRealmUpdate($scope, Current, Realm, realm, serverInfo, $http, $location, Dialog, Notifications, url) {
|
||||
|
@ -593,6 +595,22 @@ module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, clien
|
|||
|
||||
});
|
||||
|
||||
|
||||
|
||||
module.controller('IdentityProviderTabCtrl', function(Dialog, $scope, Current, Notifications, $location) {
|
||||
$scope.removeIdentityProvider = function() {
|
||||
Dialog.confirmDelete($scope.identityProvider.alias, 'provider', function() {
|
||||
$scope.identityProvider.$remove({
|
||||
realm : Current.realm.realm,
|
||||
alias : $scope.identityProvider.alias
|
||||
}, function() {
|
||||
$location.url("/realms/" + Current.realm.realm + "/identity-provider-settings");
|
||||
Notifications.success("The identity provider has been deleted.");
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload, $http, $route, realm, instance, providerFactory, IdentityProvider, serverInfo, $location, Notifications, Dialog) {
|
||||
console.log('RealmIdentityProviderCtrl');
|
||||
|
||||
|
@ -802,18 +820,6 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
|
|||
$location.url("/create/identity-provider/" + realm.realm + "/" + provider.id);
|
||||
};
|
||||
|
||||
$scope.remove = function() {
|
||||
Dialog.confirmDelete($scope.identityProvider.alias, 'provider', function() {
|
||||
$scope.identityProvider.$remove({
|
||||
realm : realm.realm,
|
||||
alias : $scope.identityProvider.alias
|
||||
}, function() {
|
||||
$location.url("/realms/" + realm.realm + "/identity-provider-settings");
|
||||
Notifications.success("The client has been deleted.");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.save = function() {
|
||||
if ($scope.newIdentityProvider) {
|
||||
if (!$scope.identityProvider.alias) {
|
||||
|
|
|
@ -273,6 +273,21 @@ module.controller('UserListCtrl', function($scope, realm, User, UserImpersonatio
|
|||
});
|
||||
|
||||
|
||||
module.controller('UserTabCtrl', function($scope, $location, Dialog, Notifications, Current) {
|
||||
$scope.removeUser = function() {
|
||||
Dialog.confirmDelete($scope.user.id, 'user', function() {
|
||||
$scope.user.$remove({
|
||||
realm : Current.realm.realm,
|
||||
userId : $scope.user.id
|
||||
}, function() {
|
||||
$location.url("/realms/" + Current.realm.realm + "/users");
|
||||
Notifications.success("The user has been deleted.");
|
||||
}, function() {
|
||||
Notifications.error("User couldn't be deleted");
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser, User, UserFederationInstances, UserImpersonation, RequiredActions, $location, Dialog, Notifications) {
|
||||
$scope.realm = realm;
|
||||
|
@ -420,20 +435,6 @@ module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser
|
|||
$scope.cancel = function() {
|
||||
$location.url("/realms/" + realm.realm + "/users");
|
||||
};
|
||||
|
||||
$scope.remove = function() {
|
||||
Dialog.confirmDelete($scope.user.id, 'user', function() {
|
||||
$scope.user.$remove({
|
||||
realm : realm.realm,
|
||||
userId : $scope.user.id
|
||||
}, function() {
|
||||
$location.url("/realms/" + realm.realm + "/users");
|
||||
Notifications.success("The user has been deleted.");
|
||||
}, function() {
|
||||
Notifications.error("User couldn't be deleted");
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('UserCredentialsCtrl', function($scope, realm, user, User, UserCredentials, Notifications, Dialog) {
|
||||
|
@ -544,11 +545,27 @@ module.controller('UserFederationCtrl', function($scope, $location, realm, UserF
|
|||
|
||||
});
|
||||
|
||||
module.controller('UserFederationTabCtrl', function(Dialog, $scope, Current, Notifications, $location) {
|
||||
$scope.removeUserFederation = function() {
|
||||
Dialog.confirm('Delete', 'Are you sure you want to permanently delete this provider? All imported users will also be deleted.', function() {
|
||||
$scope.instance.$remove({
|
||||
realm : Current.realm.realm,
|
||||
instance : $scope.instance.id
|
||||
}, function() {
|
||||
$location.url("/realms/" + Current.realm.realm + "/user-federation");
|
||||
Notifications.success("The provider has been deleted.");
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
module.controller('GenericUserFederationCtrl', function($scope, $location, Notifications, $route, Dialog, realm, instance, providerFactory, UserFederationInstances, UserFederationSync) {
|
||||
console.log('GenericUserFederationCtrl');
|
||||
|
||||
$scope.create = !instance.providerName;
|
||||
$scope.providerFactory = providerFactory;
|
||||
$scope.provider = instance;
|
||||
|
||||
console.log("providerFactory: " + providerFactory.id);
|
||||
|
||||
|
@ -644,18 +661,6 @@ module.controller('GenericUserFederationCtrl', function($scope, $location, Notif
|
|||
}
|
||||
};
|
||||
|
||||
$scope.remove = function() {
|
||||
Dialog.confirm('Delete', 'Are you sure you want to permanently delete this provider? All imported users will also be deleted.', function() {
|
||||
$scope.instance.$remove({
|
||||
realm : realm.realm,
|
||||
instance : $scope.instance.id
|
||||
}, function() {
|
||||
$location.url("/realms/" + realm.realm + "/user-federation");
|
||||
Notifications.success("The provider has been deleted.");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.triggerFullSync = function() {
|
||||
console.log('GenericCtrl: triggerFullSync');
|
||||
triggerSync('triggerFullSync');
|
||||
|
@ -906,6 +911,7 @@ module.controller('UserFederationMapperListCtrl', function($scope, $location, No
|
|||
|
||||
$scope.realm = realm;
|
||||
$scope.provider = provider;
|
||||
$scope.instance = provider;
|
||||
|
||||
$scope.mapperTypes = mapperTypes;
|
||||
$scope.mappers = mappers;
|
||||
|
|
|
@ -38,8 +38,8 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageRealm">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||
<h1>Settings</h1>
|
||||
|
||||
<kc-tabs-realm></kc-tabs-realm>
|
||||
|
||||
<ul class="nav nav-tabs nav-tabs-pf">
|
||||
|
@ -98,8 +96,8 @@
|
|||
|
||||
<div class="form-group" data-ng-show="access.manageRealm">
|
||||
<div class="col-md-10 col-md-offset-2">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
</fieldset>
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
|
||||
<button data-kc-save data-ng-show="create">Save</button>
|
||||
<button data-kc-save data-ng-show="create">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
<li>{{client.clientId}}</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{client.clientId|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-client></kc-tabs-client>
|
||||
|
||||
<form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!access.manageClients">
|
||||
|
@ -34,8 +32,8 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
|
||||
<button data-kc-save data-ng-show="changed">Save</button>
|
||||
<button data-kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button data-kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button data-kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
<li>{{client.clientId}}</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{client.clientId|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-client></kc-tabs-client>
|
||||
|
||||
<form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!access.manageClients">
|
||||
|
|
|
@ -6,10 +6,6 @@
|
|||
<li data-ng-hide="create">{{client.clientId}}</li>
|
||||
</ol>
|
||||
|
||||
<h1 data-ng-show="create">Add Client</h1>
|
||||
<h1 data-ng-hide="create">{{client.clientId|capitalize}}<i id="removeClient" class="pficon pficon-delete clickable" data-ng-show="!create && access.manageClients"
|
||||
data-ng-hide="changed" data-ng-click="remove()"></i></h1>
|
||||
|
||||
<kc-tabs-client></kc-tabs-client>
|
||||
|
||||
<form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageClients">
|
||||
|
@ -274,12 +270,12 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="create && access.manageClients">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-cancel data-ng-click="cancel()">Cancel</button>
|
||||
</div>
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
<li>{{client.clientId}}</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{client.clientId|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-client></kc-tabs-client>
|
||||
|
||||
<form class="form" name="realmForm" novalidate>
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
<li>{{client.clientId}}</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{client.clientId|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-client></kc-tabs-client>
|
||||
|
||||
<form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="kc-table-actions" colspan="3">
|
||||
<th class="kc-table-actions" colspan="4">
|
||||
<div class="form-inline">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
|
@ -29,6 +29,7 @@
|
|||
<th>Client ID</th>
|
||||
<th>Enabled</th>
|
||||
<th>Base URL</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -38,6 +39,10 @@
|
|||
<td ng-class="{'text-muted': !client.baseUrl}">
|
||||
<a href="{{client.baseUrl}}" target="_blank" data-ng-show="client.baseUrl">{{client.baseUrl}}</a>
|
||||
<span data-ng-hide="client.baseUrl">Not defined</span>
|
||||
</td>
|
||||
<td class="kc-action-cell">
|
||||
<button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}">Edit</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-ng-show="(clients | filter:search).length == 0">
|
||||
<td class="text-muted" colspan="3" data-ng-show="search.clientId">No results</td>
|
||||
|
|
|
@ -22,10 +22,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pull-right" data-ng-show="access.manageRealm">
|
||||
<button class="btn btn-primary" data-ng-click="add()">Add Selected</button>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
|
@ -48,6 +44,10 @@
|
|||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div data-ng-show="access.manageRealm">
|
||||
<button class="btn btn-primary" data-ng-click="add()">Add Selected</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<kc-menu></kc-menu>
|
|
@ -5,8 +5,6 @@
|
|||
<li>{{client.clientId}}</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{client.clientId|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-client></kc-tabs-client>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
<li>{{client.clientId}}</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{client.clientId|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-client></kc-tabs-client>
|
||||
|
||||
<form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!access.manageRealm">
|
||||
|
|
|
@ -43,14 +43,14 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="create && access.manageClients">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-save>Save</button>
|
||||
<button kc-cancel data-ng-click="cancel()">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -5,14 +5,12 @@
|
|||
<li>{{client.clientId}}</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{client.clientId|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-client></kc-tabs-client>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="kc-table-actions" colspan="3" data-ng-show="access.manageClients">
|
||||
<th class="kc-table-actions" colspan="4" data-ng-show="access.manageClients">
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-default" href="#/create/role/{{realm.realm}}/clients/{{client.id}}">Add Role</a>
|
||||
</div>
|
||||
|
@ -22,6 +20,7 @@
|
|||
<th>Role Name</th>
|
||||
<th>Composite</th>
|
||||
<th>Description</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -29,6 +28,9 @@
|
|||
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}">{{role.name}}</a></td>
|
||||
<td>{{role.composite}}</td>
|
||||
<td>{{role.description}}</td>
|
||||
<td class="kc-action-cell">
|
||||
<button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}">Edit</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-ng-show="!roles || roles.length == 0">
|
||||
<td>No client roles available</td>
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
<li>{{client.clientId}}</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{client.clientId|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-client></kc-tabs-client>
|
||||
|
||||
<h2><span>{{client.clientId}}</span> Scope Mappings </h2>
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<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}}/clients">Clients</a></li>
|
||||
<li>{{client.clientId}}</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{client.clientId|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-client></kc-tabs-client>
|
||||
|
||||
<h2><span>{{client.clientId}}</span> Service Accounts </h2>
|
||||
<p class="subtitle"></p>
|
||||
<form class="form-horizontal" name="serviceAccountsEnabledForm" novalidate kc-read-only="!access.manageClients">
|
||||
<fieldset class="border-top">
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="serviceAccountsEnabled">Service Accounts Enabled</label>
|
||||
<kc-tooltip>Allows you to authenticate this client to Keycloak and retrieve access token dedicated to this client.</kc-tooltip>
|
||||
<div class="col-md-6">
|
||||
<input ng-model="client.serviceAccountsEnabled" ng-click="serviceAccountsEnabledChanged()" name="serviceAccountsEnabled" id="serviceAccountsEnabled" onoffswitch />
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<kc-menu></kc-menu>
|
|
@ -5,8 +5,6 @@
|
|||
<li>{{client.clientId}}</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{client.clientId|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-client></kc-tabs-client>
|
||||
|
||||
<form class="form-horizontal" name="sessionStats">
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||
<h1>Settings</h1>
|
||||
|
||||
<kc-tabs-realm></kc-tabs-realm>
|
||||
|
||||
<ul class="nav nav-tabs nav-tabs-pf">
|
||||
|
@ -27,8 +25,8 @@
|
|||
</fieldset>
|
||||
<div class="form-group" data-ng-show="access.manageRealm">
|
||||
<div class="col-md-10 col-md-offset-2">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -5,14 +5,7 @@
|
|||
<li data-ng-show="create">Add User Federation Provider</li>
|
||||
</ol>
|
||||
|
||||
<h1 data-ng-hide="create">{{instance.providerName|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create && access.manageUsers"
|
||||
data-ng-hide="changed" data-ng-click="remove()"></i></h1>
|
||||
<h1 data-ng-show="create">Add {{instance.providerName|capitalize}} User Federation Provide</h1>
|
||||
|
||||
<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><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}/mappers">Mappers</a></li>
|
||||
</ul>
|
||||
<kc-tabs-user-federation></kc-tabs-user-federation>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||
<fieldset>
|
||||
|
@ -87,8 +80,8 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageUsers">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
<button class="btn btn-primary" data-ng-click="triggerChangedUsersSync()" data-ng-hide="changed">Synchronize changed users</button>
|
||||
<button class="btn btn-primary" data-ng-click="triggerFullSync()" data-ng-hide="changed">Synchronize all users</button>
|
||||
</div>
|
||||
|
|
|
@ -5,14 +5,7 @@
|
|||
<li data-ng-show="create">Add User Federation Provider</li>
|
||||
</ol>
|
||||
|
||||
<h1 data-ng-hide="create">Kerberos<i class="pficon pficon-delete clickable" data-ng-show="!create && access.manageUsers"
|
||||
data-ng-hide="changed" data-ng-click="remove()"></i></h1>
|
||||
<h1 data-ng-show="create">Add Kerberos User Federation Provider</h1>
|
||||
|
||||
<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><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}/mappers">Mappers</a></li>
|
||||
</ul>
|
||||
<kc-tabs-user-federation></kc-tabs-user-federation>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||
<fieldset>
|
||||
|
@ -106,8 +99,8 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageUsers">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -5,14 +5,7 @@
|
|||
<li data-ng-show="create">Add User Federation Provider</li>
|
||||
</ol>
|
||||
|
||||
<h1 data-ng-hide="create">LDAP<i class="pficon pficon-delete clickable" data-ng-show="!create && access.manageUsers"
|
||||
data-ng-hide="changed" data-ng-click="remove()"></i></h1>
|
||||
<h1 data-ng-show="create">Add LDAP User Federation Provider</h1>
|
||||
|
||||
<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><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}/mappers">Mappers</a></li>
|
||||
</ul>
|
||||
<kc-tabs-user-federation></kc-tabs-user-federation>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||
|
||||
|
@ -281,8 +274,8 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageUsers">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
<button class="btn btn-primary" data-ng-click="triggerChangedUsersSync()" data-ng-hide="changed">Synchronize changed users</button>
|
||||
<button class="btn btn-primary" data-ng-click="triggerFullSync()" data-ng-hide="changed">Synchronize all users</button>
|
||||
</div>
|
||||
|
|
|
@ -48,14 +48,19 @@
|
|||
|
||||
<kc-provider-config config="mapper.config" properties="mapperType.properties" realm="realm" clients="clients"></kc-provider-config>
|
||||
</fieldset>
|
||||
<div class="pull-right form-actions" data-ng-show="create && access.manageRealm">
|
||||
<button kc-cancel data-ng-click="cancel()">Cancel</button>
|
||||
<button kc-save>Save</button>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageRealm">
|
||||
<button kc-save>Save</button>
|
||||
<button kc-cancel data-ng-click="cancel()">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pull-right form-actions" data-ng-show="!create && access.manageRealm">
|
||||
<button kc-reset data-ng-show="changed">Clear changes</button>
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageRealm">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Clear changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -5,12 +5,7 @@
|
|||
<li>User Federation Mappers</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{provider.providerName === 'ldap' ? 'LDAP' : (provider.providerName|capitalize)}}</h1>
|
||||
|
||||
<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 class="active"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{provider.providerName}}/{{provider.id}}/mappers">Mappers</a></li>
|
||||
</ul>
|
||||
<kc-tabs-user-federation></kc-tabs-user-federation>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
|
|
|
@ -47,14 +47,19 @@
|
|||
</div>
|
||||
<kc-provider-config config="mapper.config" properties="mapperType.properties" realm="realm"></kc-provider-config>
|
||||
</fieldset>
|
||||
<div class="pull-right form-actions" data-ng-show="create && access.manageRealm">
|
||||
<button kc-cancel data-ng-click="cancel()">Cancel</button>
|
||||
<button kc-save>Save</button>
|
||||
|
||||
<div class="form-group" data-ng-show="create && access.manageRealm">
|
||||
<div class="col-md-10 col-md-offset-2">
|
||||
<button kc-save>Save</button>
|
||||
<button kc-cancel data-ng-click="cancel()">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pull-right form-actions" data-ng-show="!create && access.manageRealm">
|
||||
<button kc-reset data-ng-show="changed">Clear changes</button>
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<div class="form-group" data-ng-show="!create && access.manageRealm">
|
||||
<div class="col-md-10 col-md-offset-2">
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -4,13 +4,7 @@
|
|||
<li>{{identityProvider.alias}}</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{identityProvider.alias|capitalize}}</h1>
|
||||
|
||||
<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 class="active"><a href="#/realms/{{realm.realm}}/identity-provider-mappers/{{identityProvider.alias}}/mappers">Mappers</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}/export" data-ng-show="!importFile && !newIdentityProvider && identityProvider.providerId == 'saml'">Export</a></li>
|
||||
</ul>
|
||||
<kc-tabs-identity-provider></kc-tabs-identity-provider>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
|
|
|
@ -4,49 +4,46 @@
|
|||
<kc-tabs-authentication></kc-tabs-authentication>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||
<fieldset class="border-top">
|
||||
<legend><span class="text">Realm Password Policy</span> <kc-tooltip>Specify required password format. You can also set how many times a password is hashed before it is stored in database. Multiple Regex patterns, separated by comma, can be added.</kc-tooltip></legend>
|
||||
<table class="table table-striped table-bordered">
|
||||
<caption class="hidden">Table of Password Policies</caption>
|
||||
<thead>
|
||||
<tr ng-show="(allPolicies|remove:policy:'name').length > 0">
|
||||
<th colspan="5" class="kc-table-actions">
|
||||
<div class="pull-right">
|
||||
<div>
|
||||
<select class="form-control" ng-model="selectedPolicy"
|
||||
ng-options="(p.name|capitalize) for p in (allPolicies|remove:policy:'name')"
|
||||
data-ng-change="addPolicy(selectedPolicy); selectedPolicy = null">
|
||||
<option value="" disabled selected>Add policy...</option>
|
||||
</select>
|
||||
</div>
|
||||
<table class="table table-striped table-bordered">
|
||||
<caption class="hidden">Table of Password Policies</caption>
|
||||
<thead>
|
||||
<tr ng-show="(allPolicies|remove:policy:'name').length > 0">
|
||||
<th colspan="5" class="kc-table-actions">
|
||||
<div class="pull-right">
|
||||
<div>
|
||||
<select class="form-control" ng-model="selectedPolicy"
|
||||
ng-options="(p.name|capitalize) for p in (allPolicies|remove:policy:'name')"
|
||||
data-ng-change="addPolicy(selectedPolicy); selectedPolicy = null">
|
||||
<option value="" disabled selected>Add policy...</option>
|
||||
</select>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Policy Type</th>
|
||||
<th>Policy Value</th>
|
||||
<th class="actions">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="p in policy">
|
||||
<td>{{p.name|capitalize}}</td>
|
||||
<td>
|
||||
<input class="form-control" ng-model="p.value" ng-show="p.name != 'notUsername' "
|
||||
placeholder="No value assigned" min="1" required>
|
||||
</td>
|
||||
<td class="actions">
|
||||
<div class="action-div"><i class="pficon pficon-delete" ng-click="removePolicy($index)" tooltip-placement="right" tooltip="Remove Policy"></i></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Policy Type</th>
|
||||
<th>Policy Value</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="p in policy">
|
||||
<td>{{p.name|capitalize}}</td>
|
||||
<td>
|
||||
<input class="form-control" ng-model="p.value" ng-show="p.name != 'notUsername' "
|
||||
placeholder="No value assigned" min="1" required>
|
||||
</td>
|
||||
<td class="kc-action-cell">
|
||||
<button class="btn btn-default btn-block btn-sm" ng-click="removePolicy($index)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="form-group" data-ng-show="access.manageRealm">
|
||||
<div class="col-md-10 col-md-offset-2">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-show="changed">Cancel</button>
|
||||
<div class="col-md-12">
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -81,8 +81,8 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageRealm">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||
<h1>Settings</h1>
|
||||
|
||||
<kc-tabs-realm></kc-tabs-realm>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||
|
@ -21,8 +19,8 @@
|
|||
|
||||
<div class="form-group" data-ng-show="access.manageRealm">
|
||||
<div class="col-md-10 col-md-offset-2">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||
|
||||
<h1>Add Realm</h1>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate>
|
||||
<fieldset>
|
||||
<legend><span class="text">Import Realm</span></legend>
|
||||
|
@ -15,10 +12,10 @@
|
|||
<span class="kc-uploaded-file" data-ng-show="files.length > 0">{{files[0].name}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" data-ng-show="files.length > 0">
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2">
|
||||
<button type="submit" data-ng-click="uploadFile()" class="btn btn-primary">Upload</button>
|
||||
<button type="submit" data-ng-click="clearFileSelect()" class="btn btn-default">Cancel</button>
|
||||
<button type="submit" data-ng-disabled="files.length == 0" data-ng-click="uploadFile()" class="btn btn-primary">Upload</button>
|
||||
<button type="submit" data-ng-disabled="files.length == 0" data-ng-click="clearFileSelect()" class="btn btn-default">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
@ -44,7 +41,7 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-save data-ng-disabled="!changed">Create</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||
<h1 data-ng-hide="createRealm">Settings</h1>
|
||||
<h1 data-ng-show="createRealm">Add Realm</h1>
|
||||
|
||||
<kc-tabs-realm></kc-tabs-realm>
|
||||
|
||||
<form class="form-horizontal " name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||
|
@ -27,9 +24,8 @@
|
|||
</div>
|
||||
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!createRealm && access.manageRealm">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete Realm</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -4,14 +4,7 @@
|
|||
<li>{{identityProvider.alias}}</li>
|
||||
</ol>
|
||||
|
||||
<h1 data-ng-hide="create">{{identityProvider.alias|capitalize}}</h1>
|
||||
<h1 data-ng-show="create">Add OpenID Connect Identity Provider</h1>
|
||||
|
||||
<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-mappers/{{identityProvider.alias}}/mappers">Mappers</a></li>
|
||||
<li class="active"><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}/export" data-ng-show="!importFile && !newIdentityProvider">Export</a></li>
|
||||
</ul>
|
||||
<kc-tabs-identity-provider></kc-tabs-identity-provider>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate>
|
||||
<fieldset class="border-top">
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
<li>{{identityProvider.alias}}</li>
|
||||
</ol>
|
||||
|
||||
<h1 data-ng-hide="create">{{identityProvider.alias|capitalize}}<i class="pficon pficon-delete clickable"
|
||||
data-ng-hide="newIdentityProvider || changed" data-ng-click="remove()"></i></h1>
|
||||
<h1 data-ng-show="create">Add OpenID Connect Identity Provider</h1>
|
||||
<kc-tabs-identity-provider></kc-tabs-identity-provider>
|
||||
|
||||
<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>
|
||||
|
@ -217,8 +215,8 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-cancel data-ng-click="cancel()" data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-cancel data-ng-click="cancel()" data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -4,15 +4,7 @@
|
|||
<li>{{identityProvider.alias}}</li>
|
||||
</ol>
|
||||
|
||||
<h1 data-ng-hide="create">{{identityProvider.alias|capitalize}}<i class="pficon pficon-delete clickable"
|
||||
data-ng-hide="newIdentityProvider || changed" data-ng-click="remove()"></i></h1>
|
||||
<h1 data-ng-show="create">Add SAML Identity Provider</h1>
|
||||
|
||||
<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><a href="#/realms/{{realm.realm}}/identity-provider-mappers/{{identityProvider.alias}}/mappers">Mappers</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}/export" data-ng-show="!importFile && !newIdentityProvider && identityProvider.providerId == 'saml'">Export</a></li>
|
||||
</ul>
|
||||
<kc-tabs-identity-provider></kc-tabs-identity-provider>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate>
|
||||
<fieldset>
|
||||
|
@ -196,8 +188,8 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-cancel data-ng-click="cancel()" data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-cancel data-ng-click="cancel()" data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -4,14 +4,7 @@
|
|||
<li>{{identityProvider.alias}}</li>
|
||||
</ol>
|
||||
|
||||
<h1 data-ng-hide="create">{{identityProvider.alias|capitalize}}<i class="pficon pficon-delete clickable"
|
||||
data-ng-hide="newIdentityProvider || changed" data-ng-click="remove()"></i></h1>
|
||||
<h1 data-ng-show="create">Add Social Identity Provider</h1>
|
||||
|
||||
<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><a href="#/realms/{{realm.realm}}/identity-provider-mappers/{{identityProvider.alias}}/mappers">Mappers</a></li>
|
||||
</ul>
|
||||
<kc-tabs-identity-provider></kc-tabs-identity-provider>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate>
|
||||
<fieldset>
|
||||
|
@ -108,8 +101,8 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-cancel data-ng-click="cancel()" data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-cancel data-ng-click="cancel()" data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<caption class="hidden">Table of identity providers</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="4" class="kc-table-actions">
|
||||
<th colspan="5" class="kc-table-actions">
|
||||
<div class="dropdown pull-right">
|
||||
<select class="form-control" ng-model="provider"
|
||||
ng-options="p.name group by p.groupName for p in allProviders track by p.id"
|
||||
|
@ -23,6 +23,7 @@
|
|||
<th>Provider</th>
|
||||
<th>Enabled</th>
|
||||
<th width="15%">GUI order</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ng-show="configuredProviders.length > 0">
|
||||
|
@ -33,6 +34,9 @@
|
|||
<td>{{identityProvider.providerId}}</td>
|
||||
<td>{{identityProvider.enabled}}</td>
|
||||
<td>{{identityProvider.config.guiOrder}}</td>
|
||||
<td class="kc-action-cell">
|
||||
<button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">Edit</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||
<h1>Settings</h1>
|
||||
|
||||
<kc-tabs-realm></kc-tabs-realm>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||
<h1>Settings</h1>
|
||||
|
||||
<kc-tabs-realm></kc-tabs-realm>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||
|
@ -64,8 +62,8 @@
|
|||
|
||||
<div class="form-group" data-ng-show="access.manageRealm">
|
||||
<div class="col-md-10 col-md-offset-2">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||
<h1>Settings</h1>
|
||||
|
||||
<kc-tabs-realm></kc-tabs-realm>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||
|
@ -56,8 +54,8 @@
|
|||
|
||||
<div class="form-group" data-ng-show="access.manageRealm">
|
||||
<div class="col-md-10 col-md-offset-2">
|
||||
<button data-kc-save data-ng-show="changed">Save</button>
|
||||
<button data-kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button data-kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button data-kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||
<h1>Settings</h1>
|
||||
|
||||
<kc-tabs-realm></kc-tabs-realm>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||
|
@ -88,8 +86,8 @@
|
|||
|
||||
<div class="form-group" data-ng-show="access.manageRealm">
|
||||
<div class="col-md-10 col-md-offset-2">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||
<h1>Settings</h1>
|
||||
|
||||
<kc-tabs-realm></kc-tabs-realm>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||
|
@ -116,8 +114,8 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -46,8 +46,8 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageRealm">
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-reset data-ng-show="changed">Cancel</button>
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="kc-table-actions" colspan="3">
|
||||
<th class="kc-table-actions" colspan="4">
|
||||
<div class="form-inline">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
|
@ -30,6 +30,7 @@
|
|||
<th>Role Name</th>
|
||||
<th>Composite</th>
|
||||
<th>Description</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -37,6 +38,9 @@
|
|||
<td><a href="#/realms/{{realm.realm}}/roles/{{role.id}}">{{role.name}}</a></td>
|
||||
<td>{{role.composite}}</td>
|
||||
<td>{{role.description}}</td>
|
||||
<td class="kc-action-cell">
|
||||
<button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/roles/{{role.id}}">Edit</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-ng-show="(roles | filter:{name: searchQuery}).length == 0">
|
||||
<td class="text-muted" colspan="3" data-ng-show="searchQuery">No results</td>
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
<li>{{user.username}}</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{user.username|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-user></kc-tabs-user>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate>
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
<li>{{user.username}}</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{user.username|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-user></kc-tabs-user>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
|
@ -37,9 +35,9 @@
|
|||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" ng-click="revokeConsent(consent.clientId)">
|
||||
<i class="pficon pficon-delete"></i>
|
||||
<td class="kc-action-cell">
|
||||
<button class="btn btn-default btn-block btn-sm" ng-click="revokeConsent(consent.clientId)">
|
||||
<i class="pficon pficon-delete"></i> Revoke
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
<li>{{user.username}}</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{user.username|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-user></kc-tabs-user>
|
||||
|
||||
<form class="form-horizontal" name="userForm" novalidate>
|
||||
|
|
|
@ -5,10 +5,6 @@
|
|||
<li data-ng-show="create">Add User</li>
|
||||
</ol>
|
||||
|
||||
<h1 data-ng-hide="create">{{user.username|capitalize}}<i id="removeUser" class="pficon pficon-delete clickable" data-ng-show="!create && access.manageUsers && !changed"
|
||||
data-ng-click="remove()"></i></h1>
|
||||
<h1 data-ng-show="create">Add User</h1>
|
||||
|
||||
<kc-tabs-user></kc-tabs-user>
|
||||
|
||||
<form class="form-horizontal" name="userForm" novalidate kc-read-only="!access.manageUsers">
|
||||
|
@ -126,9 +122,9 @@
|
|||
<button kc-cancel data-ng-click="cancel()">Cancel</button>
|
||||
</div>
|
||||
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create">
|
||||
<button kc-save data-ng-show="access.manageUsers && changed">Save</button>
|
||||
<button kc-reset data-ng-show="access.manageUsers && changed">Cancel</button>
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageUsers">
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
<button data-ng-show="access.impersonation" class="btn btn-default" data-ng-click="impersonate()" tooltip="Login as this user. If user is in same realm as you, your current login session will be logged out before you are logged in as this user.">Impersonate</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -39,9 +39,12 @@
|
|||
|
||||
</fieldset>
|
||||
|
||||
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
|
||||
<button kc-cancel data-ng-click="cancel()">Cancel</button>
|
||||
<button kc-save>Save</button>
|
||||
|
||||
<div class="form-group" data-ng-show="access.manageRealm">
|
||||
<div class="col-md-10 col-md-offset-2">
|
||||
<button kc-save>Save</button>
|
||||
<button kc-cancel data-ng-click="cancel()">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
|
|
@ -4,16 +4,14 @@
|
|||
<li>{{user.username}}</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{user.username|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-user></kc-tabs-user>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<tr data-ng-show="hasAnyProvidersToCreate()">
|
||||
<th class="kc-table-actions" colspan="4">
|
||||
<div class="form-inline">
|
||||
<div class="pull-right" data-ng-show="hasAnyProvidersToCreate()">
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-primary" href="#/create/federated-identity/{{realm.realm}}/{{user.id}}">Create</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -31,8 +29,8 @@
|
|||
<td>{{identity.identityProvider}}</td>
|
||||
<td>{{identity.userId}}</td>
|
||||
<td>{{identity.userName}}</td>
|
||||
<td class="actions">
|
||||
<div class="action-div"><i class="pficon pficon-delete" ng-click="removeProviderLink(identity)" tooltip-placement="right" tooltip="Remove Provider Link"></i></div>
|
||||
<td class="kc-action-cell">
|
||||
<button class="btn btn-default btn-block btn-sm" ng-click="removeProviderLink(identity)">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-ng-show="federatedIdentities.length == 0">
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr ng-show="providers.length > 0 && access.manageUsers">
|
||||
<th colspan="3" class="kc-table-actions">
|
||||
<th colspan="4" class="kc-table-actions">
|
||||
<div class="pull-right">
|
||||
<div>
|
||||
<select class="form-control" ng-model="selectedProvider"
|
||||
|
@ -22,6 +22,7 @@
|
|||
<th>ID</th>
|
||||
<th>Provider Name</th>
|
||||
<th>Priority</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -29,6 +30,9 @@
|
|||
<td><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">{{instance.displayName}}</a></td>
|
||||
<td>{{instance.providerName|capitalize}}</td>
|
||||
<td>{{instance.priority}}</td>
|
||||
<td class="kc-action-cell">
|
||||
<button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">Edit</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-ng-show="!instances || instances.length == 0">
|
||||
<td class="text-muted">No user federation providers configured</td>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<caption data-ng-show="users" class="hidden">Table of realm users</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="{{access.impersonation == true ? '5' : '4'}}">
|
||||
<th colspan="{{access.impersonation == true ? '6' : '5'}}">
|
||||
<div class="form-inline">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
|
@ -30,7 +30,7 @@
|
|||
<th>Last Name</th>
|
||||
<th>First Name</th>
|
||||
<th>Email</th>
|
||||
<th data-ng-show="access.impersonation"></th>
|
||||
<th colspan="{{access.impersonation == true ? '2' : '1'}}">Actions</th>
|
||||
</tr>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -51,7 +51,12 @@
|
|||
<td>{{user.lastName}}</td>
|
||||
<td>{{user.firstName}}</td>
|
||||
<td>{{user.email}}</td>
|
||||
<td data-ng-show="access.impersonation"><button class="btn btn-default" data-ng-click="impersonate(user.id)" tooltip="Login as this user. If user is in same realm as you, your current login session will be logged out before you are logged in as this user.">Impersonate</button></td>
|
||||
<td class="kc-action-cell">
|
||||
<button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/users/{{user.id}}">Edit</button>
|
||||
</td>
|
||||
<td data-ng-show="access.impersonation" class="kc-action-cell">
|
||||
<button class="btn btn-default btn-block btn-sm" data-ng-click="impersonate(user.id)" tooltip="Login as this user. If user is in same realm as you, your current login session will be logged out before you are logged in as this user.">Impersonate</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-ng-show="!users || users.length == 0">
|
||||
<td class="text-muted" data-ng-show="!users">Please enter a search, or click on view all users</td>
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
<li>{{user.username}}</li>
|
||||
</ol>
|
||||
|
||||
<h1>{{user.username|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-user></kc-tabs-user>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
|
@ -36,7 +34,9 @@
|
|||
</div>
|
||||
</ul>
|
||||
</td>
|
||||
<td data-ng-show="access.manageUsers"><a href="" ng-click="logoutSession(session.id)">logout</a> </td>
|
||||
<td class="kc-action-cell" data-ng-show="access.manageUsers">
|
||||
<button class="btn btn-default btn-block btn-sm" ng-click="logoutSession(session.id)">Logout</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|| path[2] == 'cache-settings'
|
||||
|| path[2] == 'defense'
|
||||
|| path[2] == 'keys-settings' || path[2] == 'smtp-settings' || path[2] == 'ldap-settings' || path[2] == 'auth-settings') && path[3] != 'clients') && 'active'">
|
||||
<a href="#/realms/{{realm.realm}}"><span class="pficon pficon-settings"></span> Settings</a>
|
||||
<a href="#/realms/{{realm.realm}}"><span class="pficon pficon-settings"></span> Realm Settings</a>
|
||||
</li>
|
||||
<li data-ng-show="access.viewClients" data-ng-class="(path[2] == 'clients' || path[1] == 'client' || path[3] == 'clients') && 'active'"><a href="#/realms/{{realm.realm}}/clients"><i class="fa fa-cubes"></i> Clients</a></li>
|
||||
<li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'roles' || path[2] == 'default-roles' || (path[1] == 'role' && path[3] != 'clients')) && 'active'"><a href="#/realms/{{realm.realm}}/roles"><i class="fa fa-tasks"></i> Roles</a></li>
|
||||
|
|
|
@ -1,28 +1,41 @@
|
|||
<ul class="nav nav-tabs nav-tabs-pf" data-ng-hide="create && !path[4]">
|
||||
<li ng-class="{active: !path[4]}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">Settings</a></li>
|
||||
<li ng-class="{active: path[4] == 'credentials'}" data-ng-show="!client.publicClient && client.protocol != 'saml'"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/credentials">Credentials</a></li>
|
||||
<li ng-class="{active: path[4] == 'saml'}" data-ng-show="client.protocol == 'saml' && (client.attributes['saml.client.signature'] == 'true' || client.attributes['saml.encrypt'] == 'true')"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/saml/keys">SAML Keys</a></li>
|
||||
<li ng-class="{active: path[4] == 'roles'}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles">Roles</a></li>
|
||||
<li ng-class="{active: path[4] == 'mappers'}" data-ng-show="!client.bearerOnly">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/mappers">Mappers</a>
|
||||
<kc-tooltip>Protocol mappers perform transformation on tokens and documents. They an do things like map user data into protocol claims, or just transform any requests going between the client and auth server.</kc-tooltip>
|
||||
</li>
|
||||
<li ng-class="{active: path[4] == 'scope-mappings'}" data-ng-show="!client.bearerOnly">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/scope-mappings">Scope</a>
|
||||
<kc-tooltip>Scope mappings allow you to restrict which user role mappings are included within the access token requested by the client.</kc-tooltip>
|
||||
</li>
|
||||
<li ng-class="{active: path[4] == 'revocation'}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/revocation">Revocation</a></li>
|
||||
<!-- <li ng-class="{active: path[4] == 'identity-provider'}" data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/identity-provider">Identity Provider</a></li> -->
|
||||
<li ng-class="{active: path[4] == 'sessions'}" data-ng-show="!client.bearerOnly">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/sessions">Sessions</a>
|
||||
<kc-tooltip>View active sessions for this client. Allows you to see which users are active and when they logged in.</kc-tooltip>
|
||||
</li>
|
||||
<div data-ng-controller="ClientTabCtrl">
|
||||
|
||||
<li ng-class="{active: path[4] == 'clustering'}" data-ng-show="!client.publicClient"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/clustering">Clustering</a></li>
|
||||
<h1 data-ng-show="create">Add Client</h1>
|
||||
<h1 data-ng-hide="create">
|
||||
{{client.clientId|capitalize}}
|
||||
<i id="removeClient" class="pficon pficon-delete clickable" data-ng-show="access.manageClients" data-ng-click="removeClient()"></i>
|
||||
</h1>
|
||||
|
||||
<li ng-class="{active: path[4] == 'installation'}" data-ng-show="client.protocol != 'saml'">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/installation">Installation</a>
|
||||
<kc-tooltip>Helper utility for generating various client adapter configuration formats which you can download or cut and paste to configure your clients.</kc-tooltip>
|
||||
</li>
|
||||
<ul class="nav nav-tabs nav-tabs-pf" data-ng-hide="create && !path[4]">
|
||||
<li ng-class="{active: !path[4]}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">Settings</a></li>
|
||||
<li ng-class="{active: path[4] == 'credentials'}" data-ng-show="!client.publicClient && client.protocol != 'saml'"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/credentials">Credentials</a></li>
|
||||
<li ng-class="{active: path[4] == 'saml'}" data-ng-show="client.protocol == 'saml' && (client.attributes['saml.client.signature'] == 'true' || client.attributes['saml.encrypt'] == 'true')"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/saml/keys">SAML Keys</a></li>
|
||||
<li ng-class="{active: path[4] == 'roles'}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles">Roles</a></li>
|
||||
<li ng-class="{active: path[4] == 'mappers'}" data-ng-show="!client.bearerOnly">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/mappers">Mappers</a>
|
||||
<kc-tooltip>Protocol mappers perform transformation on tokens and documents. They an do things like map user data into protocol claims, or just transform any requests going between the client and auth server.</kc-tooltip>
|
||||
</li>
|
||||
<li ng-class="{active: path[4] == 'scope-mappings'}" data-ng-show="!client.bearerOnly">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/scope-mappings">Scope</a>
|
||||
<kc-tooltip>Scope mappings allow you to restrict which user role mappings are included within the access token requested by the client.</kc-tooltip>
|
||||
</li>
|
||||
<li ng-class="{active: path[4] == 'revocation'}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/revocation">Revocation</a></li>
|
||||
<!-- <li ng-class="{active: path[4] == 'identity-provider'}" data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/identity-provider">Identity Provider</a></li> -->
|
||||
<li ng-class="{active: path[4] == 'sessions'}" data-ng-show="!client.bearerOnly">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/sessions">Sessions</a>
|
||||
<kc-tooltip>View active sessions for this client. Allows you to see which users are active and when they logged in.</kc-tooltip>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<li ng-class="{active: path[4] == 'clustering'}" data-ng-show="!client.publicClient"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/clustering">Clustering</a></li>
|
||||
|
||||
<li ng-class="{active: path[4] == 'installation'}" data-ng-show="client.protocol != 'saml'">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/installation">Installation</a>
|
||||
<kc-tooltip>Helper utility for generating various client adapter configuration formats which you can download or cut and paste to configure your clients.</kc-tooltip>
|
||||
</li>
|
||||
|
||||
<li ng-class="{active: path[4] == 'service-accounts'}" data-ng-show="!client.publicClient && !client.bearerOnly">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/service-accounts">Service Accounts</a>
|
||||
<kc-tooltip>Allows you to authenticate this client to Keycloak and retrieve access tokens dedicated to this client.</kc-tooltip>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
|
@ -0,0 +1,13 @@
|
|||
<div data-ng-controller="IdentityProviderTabCtrl">
|
||||
<h1 data-ng-hide="path[0] == 'create'">
|
||||
{{identityProvider.alias|capitalize}}
|
||||
<i class="pficon pficon-delete clickable" data-ng-hide="newIdentityProvider || changed" data-ng-click="removeIdentityProvider()"></i>
|
||||
</h1>
|
||||
<h1 data-ng-show="path[0] == 'create'">Add Identity Provider</h1>
|
||||
|
||||
<ul class="nav nav-tabs" data-ng-hide="newIdentityProvider">
|
||||
<li ng-class="{active: !path[6] && path.length > 5}"><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">Settings</a></li>
|
||||
<li ng-class="{active: path[4] == 'mappers'}"><a href="#/realms/{{realm.realm}}/identity-provider-mappers/{{identityProvider.alias}}/mappers">Mappers</a></li>
|
||||
<li ng-class="{active: path[6] == 'export'}"><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}/export" data-ng-show="!importFile && !newIdentityProvider && identityProvider.providerId == 'saml'">Export</a></li>
|
||||
</ul>
|
||||
</div>
|
|
@ -1,10 +1,18 @@
|
|||
<ul class="nav nav-tabs">
|
||||
<li ng-class="{active: !path[2]}"><a href="#/realms/{{realm.realm}}">General</a></li>
|
||||
<li ng-class="{active: path[2] == 'login-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/login-settings">Login</a></li>
|
||||
<li ng-class="{active: path[2] == 'keys-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/keys-settings">Keys</a></li>
|
||||
<li ng-class="{active: path[2] == 'smtp-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/smtp-settings">Email</a></li>
|
||||
<li ng-class="{active: path[2] == 'theme-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/theme-settings">Themes</a></li>
|
||||
<li ng-class="{active: path[2] == 'cache-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/cache-settings">Cache</a></li>
|
||||
<li ng-class="{active: path[2] == 'token-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/token-settings">Tokens</a></li>
|
||||
<li ng-class="{active: path[2] == 'defense'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/defense/headers">Security Defenses</a></li>
|
||||
</ul>
|
||||
<div data-ng-controller="RealmTabCtrl">
|
||||
<h1 data-ng-hide="createRealm">
|
||||
{{realm.realm|capitalize}}
|
||||
<i id="removeRealm" class="pficon pficon-delete clickable" data-ng-show="access.manageRealm" data-ng-click="removeRealm()"></i>
|
||||
</h1>
|
||||
<h1 data-ng-show="createRealm">Add Realm</h1>
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li ng-class="{active: !path[2]}"><a href="#/realms/{{realm.realm}}">General</a></li>
|
||||
<li ng-class="{active: path[2] == 'login-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/login-settings">Login</a></li>
|
||||
<li ng-class="{active: path[2] == 'keys-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/keys-settings">Keys</a></li>
|
||||
<li ng-class="{active: path[2] == 'smtp-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/smtp-settings">Email</a></li>
|
||||
<li ng-class="{active: path[2] == 'theme-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/theme-settings">Themes</a></li>
|
||||
<li ng-class="{active: path[2] == 'cache-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/cache-settings">Cache</a></li>
|
||||
<li ng-class="{active: path[2] == 'token-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/token-settings">Tokens</a></li>
|
||||
<li ng-class="{active: path[2] == 'defense'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/defense/headers">Security Defenses</a></li>
|
||||
</ul>
|
||||
</div>
|
|
@ -0,0 +1,12 @@
|
|||
<div data-ng-controller="UserFederationTabCtrl">
|
||||
<h1 data-ng-hide="create">
|
||||
{{instance.displayName|capitalize}}
|
||||
<i class="pficon pficon-delete clickable" data-ng-show="!create && access.manageUsers" data-ng-click="removeUserFederation()"></i>
|
||||
</h1>
|
||||
<h1 data-ng-show="create">Add User Federation Provider</h1>
|
||||
|
||||
<ul class="nav nav-tabs" data-ng-hide="create">
|
||||
<li ng-class="{active: !path[6]}"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">Settings</a></li>
|
||||
<li ng-class="{active: path[6] == 'mappers'}"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}/mappers">Mappers</a></li>
|
||||
</ul>
|
||||
</div>
|
|
@ -1,8 +1,16 @@
|
|||
<ul class="nav nav-tabs" data-ng-show="!create">
|
||||
<li ng-class="{active: !path[4] && path[0] != 'create'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}">Attributes</a></li>
|
||||
<li ng-class="{active: path[4] == 'user-credentials'}" data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/user-credentials">Credentials</a></li>
|
||||
<li ng-class="{active: path[4] == 'role-mappings'}" ><a href="#/realms/{{realm.realm}}/users/{{user.id}}/role-mappings">Role Mappings</a></li>
|
||||
<li ng-class="{active: path[4] == 'consents'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/consents">Consents</a></li>
|
||||
<li ng-class="{active: path[4] == 'sessions'}" ><a href="#/realms/{{realm.realm}}/users/{{user.id}}/sessions">Sessions</a></li>
|
||||
<li ng-class="{active: path[4] == 'federated-identity' || path[1] == 'federated-identity'}" data-ng-show="user.federatedIdentities != null"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/federated-identity">Identity Provider Links</a></li>
|
||||
</ul>
|
||||
<div data-ng-controller="UserTabCtrl">
|
||||
<h1 data-ng-hide="create">
|
||||
{{user.username|capitalize}}
|
||||
<i id="removeUser" class="pficon pficon-delete clickable" data-ng-show="!create && access.manageUsers" data-ng-click="removeUser()"></i>
|
||||
</h1>
|
||||
<h1 data-ng-show="create">Add User</h1>
|
||||
|
||||
<ul class="nav nav-tabs" data-ng-show="!create">
|
||||
<li ng-class="{active: !path[4] && path[0] != 'create'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}">Attributes</a></li>
|
||||
<li ng-class="{active: path[4] == 'user-credentials'}" data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/user-credentials">Credentials</a></li>
|
||||
<li ng-class="{active: path[4] == 'role-mappings'}" ><a href="#/realms/{{realm.realm}}/users/{{user.id}}/role-mappings">Role Mappings</a></li>
|
||||
<li ng-class="{active: path[4] == 'consents'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/consents">Consents</a></li>
|
||||
<li ng-class="{active: path[4] == 'sessions'}" ><a href="#/realms/{{realm.realm}}/users/{{user.id}}/sessions">Sessions</a></li>
|
||||
<li ng-class="{active: path[4] == 'federated-identity' || path[1] == 'federated-identity'}" data-ng-show="user.federatedIdentities != null"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/federated-identity">Identity Provider Links</a></li>
|
||||
</ul>
|
||||
</div>
|
|
@ -299,3 +299,17 @@ h1 i {
|
|||
margin-left: 10px;
|
||||
}
|
||||
|
||||
/* Action cell */
|
||||
.kc-action-cell {
|
||||
position: relative;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.kc-action-cell .btn {
|
||||
border: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
|
@ -103,6 +103,9 @@ public interface ClientModel extends RoleContainerModel {
|
|||
boolean isConsentRequired();
|
||||
void setConsentRequired(boolean consentRequired);
|
||||
|
||||
boolean isServiceAccountsEnabled();
|
||||
void setServiceAccountsEnabled(boolean serviceAccountsEnabled);
|
||||
|
||||
Set<RoleModel> getScopeMappings();
|
||||
void addScopeMapping(RoleModel role);
|
||||
void deleteScopeMapping(RoleModel role);
|
||||
|
|
|
@ -304,6 +304,11 @@ public class UserFederationManager implements UserProvider {
|
|||
}, realm, firstResult, maxResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUserByUserAttributes(Map<String, String> attributes, RealmModel realm) {
|
||||
return session.userStorage().searchForUserByUserAttributes(attributes, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<FederatedIdentityModel> getFederatedIdentities(UserModel user, RealmModel realm) {
|
||||
validateUser(realm, user);
|
||||
|
|
|
@ -32,6 +32,10 @@ public interface UserProvider extends Provider {
|
|||
List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults);
|
||||
List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm);
|
||||
List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults);
|
||||
|
||||
// Searching by UserModel.attribute (not property)
|
||||
List<UserModel> searchForUserByUserAttributes(Map<String, String> attributes, RealmModel realm);
|
||||
|
||||
Set<FederatedIdentityModel> getFederatedIdentities(UserModel user, RealmModel realm);
|
||||
FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm);
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ public class ClientEntity extends AbstractIdentifiableEntity {
|
|||
private String baseUrl;
|
||||
private boolean bearerOnly;
|
||||
private boolean consentRequired;
|
||||
private boolean serviceAccountsEnabled;
|
||||
private boolean directGrantsOnly;
|
||||
private int nodeReRegistrationTimeout;
|
||||
|
||||
|
@ -210,6 +211,14 @@ public class ClientEntity extends AbstractIdentifiableEntity {
|
|||
this.consentRequired = consentRequired;
|
||||
}
|
||||
|
||||
public boolean isServiceAccountsEnabled() {
|
||||
return serviceAccountsEnabled;
|
||||
}
|
||||
|
||||
public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
|
||||
this.serviceAccountsEnabled = serviceAccountsEnabled;
|
||||
}
|
||||
|
||||
public boolean isDirectGrantsOnly() {
|
||||
return directGrantsOnly;
|
||||
}
|
||||
|
|
|
@ -351,6 +351,6 @@ public final class KeycloakModelUtils {
|
|||
}
|
||||
|
||||
public static String toLowerCaseSafe(String str) {
|
||||
return str==null ? str : str.toLowerCase();
|
||||
return str==null ? null : str.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -289,6 +289,7 @@ public class ModelToRepresentation {
|
|||
rep.setFullScopeAllowed(clientModel.isFullScopeAllowed());
|
||||
rep.setBearerOnly(clientModel.isBearerOnly());
|
||||
rep.setConsentRequired(clientModel.isConsentRequired());
|
||||
rep.setServiceAccountsEnabled(clientModel.isServiceAccountsEnabled());
|
||||
rep.setDirectGrantsOnly(clientModel.isDirectGrantsOnly());
|
||||
rep.setSurrogateAuthRequired(clientModel.isSurrogateAuthRequired());
|
||||
rep.setBaseUrl(clientModel.getBaseUrl());
|
||||
|
|
|
@ -625,6 +625,7 @@ public class RepresentationToModel {
|
|||
if (resourceRep.getBaseUrl() != null) client.setBaseUrl(resourceRep.getBaseUrl());
|
||||
if (resourceRep.isBearerOnly() != null) client.setBearerOnly(resourceRep.isBearerOnly());
|
||||
if (resourceRep.isConsentRequired() != null) client.setConsentRequired(resourceRep.isConsentRequired());
|
||||
if (resourceRep.isServiceAccountsEnabled() != null) client.setServiceAccountsEnabled(resourceRep.isServiceAccountsEnabled());
|
||||
if (resourceRep.isDirectGrantsOnly() != null) client.setDirectGrantsOnly(resourceRep.isDirectGrantsOnly());
|
||||
if (resourceRep.isPublicClient() != null) client.setPublicClient(resourceRep.isPublicClient());
|
||||
if (resourceRep.isFrontchannelLogout() != null) client.setFrontchannelLogout(resourceRep.isFrontchannelLogout());
|
||||
|
@ -714,6 +715,7 @@ public class RepresentationToModel {
|
|||
if (rep.isEnabled() != null) resource.setEnabled(rep.isEnabled());
|
||||
if (rep.isBearerOnly() != null) resource.setBearerOnly(rep.isBearerOnly());
|
||||
if (rep.isConsentRequired() != null) resource.setConsentRequired(rep.isConsentRequired());
|
||||
if (rep.isServiceAccountsEnabled() != null) resource.setServiceAccountsEnabled(rep.isServiceAccountsEnabled());
|
||||
if (rep.isDirectGrantsOnly() != null) resource.setDirectGrantsOnly(rep.isDirectGrantsOnly());
|
||||
if (rep.isPublicClient() != null) resource.setPublicClient(rep.isPublicClient());
|
||||
if (rep.isFullScopeAllowed() != null) resource.setFullScopeAllowed(rep.isFullScopeAllowed());
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.keycloak.models.utils.CredentialValidation;
|
|||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -225,6 +226,25 @@ public class FileUserProvider implements UserProvider {
|
|||
return sortedSubList(found, firstResult, maxResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUserByUserAttributes(Map<String, String> attributes, RealmModel realm) {
|
||||
Collection<UserModel> users = inMemoryModel.getUsers(realm.getId());
|
||||
|
||||
for (Map.Entry<String, String> entry : attributes.entrySet()) {
|
||||
|
||||
List<UserModel> matchedUsers = new ArrayList<>();
|
||||
for (UserModel user : users) {
|
||||
List<String> vals = user.getAttribute(entry.getKey());
|
||||
if (vals.contains(entry.getValue())) {
|
||||
matchedUsers.add(user);
|
||||
}
|
||||
}
|
||||
users = matchedUsers;
|
||||
}
|
||||
|
||||
return (List<UserModel>) users;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<FederatedIdentityModel> getFederatedIdentities(UserModel userModel, RealmModel realm) {
|
||||
UserEntity userEntity = ((UserAdapter)getUserById(userModel.getId(), realm)).getUserEntity();
|
||||
|
|
|
@ -441,6 +441,16 @@ public class ClientAdapter implements ClientModel {
|
|||
entity.setConsentRequired(consentRequired);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isServiceAccountsEnabled() {
|
||||
return entity.isServiceAccountsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
|
||||
entity.setServiceAccountsEnabled(serviceAccountsEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectGrantsOnly() {
|
||||
return entity.isDirectGrantsOnly();
|
||||
|
|
|
@ -412,6 +412,18 @@ public class ClientAdapter implements ClientModel {
|
|||
updated.setConsentRequired(consentRequired);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isServiceAccountsEnabled() {
|
||||
if (updated != null) return updated.isServiceAccountsEnabled();
|
||||
return cached.isServiceAccountsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
|
||||
getDelegateForUpdate();
|
||||
updated.setServiceAccountsEnabled(serviceAccountsEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getRole(String name) {
|
||||
if (updated != null) return updated.getRole(name);
|
||||
|
|
|
@ -241,6 +241,11 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
|
|||
return getDelegate().searchForUserByAttributes(attributes, realm, firstResult, maxResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUserByUserAttributes(Map<String, String> attributes, RealmModel realm) {
|
||||
return getDelegate().searchForUserByUserAttributes(attributes, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<FederatedIdentityModel> getFederatedIdentities(UserModel user, RealmModel realm) {
|
||||
return getDelegate().getFederatedIdentities(user, realm);
|
||||
|
|
|
@ -108,6 +108,11 @@ public class NoCacheUserProvider implements CacheUserProvider {
|
|||
return getDelegate().searchForUserByAttributes(attributes, realm, firstResult, maxResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUserByUserAttributes(Map<String, String> attributes, RealmModel realm) {
|
||||
return getDelegate().searchForUserByUserAttributes(attributes, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<FederatedIdentityModel> getFederatedIdentities(UserModel user, RealmModel realm) {
|
||||
return getDelegate().getFederatedIdentities(user, realm);
|
||||
|
|
|
@ -46,6 +46,7 @@ public class CachedClient implements Serializable {
|
|||
private List<String> defaultRoles = new LinkedList<String>();
|
||||
private boolean bearerOnly;
|
||||
private boolean consentRequired;
|
||||
private boolean serviceAccountsEnabled;
|
||||
private Map<String, String> roles = new HashMap<String, String>();
|
||||
private int nodeReRegistrationTimeout;
|
||||
private Map<String, Integer> registeredNodes;
|
||||
|
@ -78,6 +79,7 @@ public class CachedClient implements Serializable {
|
|||
defaultRoles.addAll(model.getDefaultRoles());
|
||||
bearerOnly = model.isBearerOnly();
|
||||
consentRequired = model.isConsentRequired();
|
||||
serviceAccountsEnabled = model.isServiceAccountsEnabled();
|
||||
for (RoleModel role : model.getRoles()) {
|
||||
roles.put(role.getName(), role.getId());
|
||||
cache.addCachedRole(new CachedClientRole(id, role, realm));
|
||||
|
@ -178,6 +180,10 @@ public class CachedClient implements Serializable {
|
|||
return consentRequired;
|
||||
}
|
||||
|
||||
public boolean isServiceAccountsEnabled() {
|
||||
return serviceAccountsEnabled;
|
||||
}
|
||||
|
||||
public Map<String, String> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
|
|
@ -460,6 +460,16 @@ public class ClientAdapter implements ClientModel {
|
|||
entity.setConsentRequired(consentRequired);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isServiceAccountsEnabled() {
|
||||
return entity.isServiceAccountsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
|
||||
entity.setServiceAccountsEnabled(serviceAccountsEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectGrantsOnly() {
|
||||
return entity.isDirectGrantsOnly();
|
||||
|
|
|
@ -18,8 +18,10 @@ import org.keycloak.models.utils.CredentialValidation;
|
|||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.TypedQuery;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -379,6 +381,38 @@ public class JpaUserProvider implements UserProvider {
|
|||
return users;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUserByUserAttributes(Map<String, String> attributes, RealmModel realm) {
|
||||
StringBuilder builder = new StringBuilder("select attr.user,count(attr.user) from UserAttributeEntity attr where attr.user.realmId = :realmId");
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, String> entry : attributes.entrySet()) {
|
||||
String attrName = entry.getKey();
|
||||
if (first) {
|
||||
builder.append(" and ");
|
||||
first = false;
|
||||
} else {
|
||||
builder.append(" or ");
|
||||
}
|
||||
builder.append(" ( attr.name like :").append(attrName);
|
||||
builder.append(" and attr.value like :").append(attrName).append("val )");
|
||||
}
|
||||
builder.append(" group by attr.user having count(attr.user) = " + attributes.size());
|
||||
Query query = em.createQuery(builder.toString());
|
||||
query.setParameter("realmId", realm.getId());
|
||||
for (Map.Entry<String, String> entry : attributes.entrySet()) {
|
||||
query.setParameter(entry.getKey(), entry.getKey());
|
||||
query.setParameter(entry.getKey() + "val", entry.getValue());
|
||||
}
|
||||
List results = query.getResultList();
|
||||
|
||||
List<UserModel> users = new ArrayList<UserModel>();
|
||||
for (Object o : results) {
|
||||
UserEntity user = (UserEntity) ((Object[])o)[0];
|
||||
users.add(new UserAdapter(realm, em, user));
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
private FederatedIdentityEntity findFederatedIdentity(UserModel user, String identityProvider) {
|
||||
TypedQuery<FederatedIdentityEntity> query = em.createNamedQuery("findFederatedIdentityByUserAndProvider", FederatedIdentityEntity.class);
|
||||
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
|
||||
|
|
|
@ -95,6 +95,9 @@ public class ClientEntity {
|
|||
@Column(name="CONSENT_REQUIRED")
|
||||
private boolean consentRequired;
|
||||
|
||||
@Column(name="SERVICE_ACCOUNTS_ENABLED")
|
||||
private boolean serviceAccountsEnabled;
|
||||
|
||||
@Column(name="NODE_REREG_TIMEOUT")
|
||||
private int nodeReRegistrationTimeout;
|
||||
|
||||
|
@ -295,6 +298,14 @@ public class ClientEntity {
|
|||
this.consentRequired = consentRequired;
|
||||
}
|
||||
|
||||
public boolean isServiceAccountsEnabled() {
|
||||
return serviceAccountsEnabled;
|
||||
}
|
||||
|
||||
public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
|
||||
this.serviceAccountsEnabled = serviceAccountsEnabled;
|
||||
}
|
||||
|
||||
public boolean isDirectGrantsOnly() {
|
||||
return directGrantsOnly;
|
||||
}
|
||||
|
|
|
@ -461,6 +461,17 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
|
|||
updateMongoEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isServiceAccountsEnabled() {
|
||||
return getMongoEntity().isServiceAccountsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
|
||||
getMongoEntity().setServiceAccountsEnabled(serviceAccountsEnabled);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectGrantsOnly() {
|
||||
return getMongoEntity().isDirectGrantsOnly();
|
||||
|
|
|
@ -214,6 +214,19 @@ public class MongoUserProvider implements UserProvider {
|
|||
return convertUserEntities(realm, users);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUserByUserAttributes(Map<String, String> attributes, RealmModel realm) {
|
||||
QueryBuilder queryBuilder = new QueryBuilder()
|
||||
.and("realmId").is(realm.getId());
|
||||
|
||||
for (Map.Entry<String, String> entry : attributes.entrySet()) {
|
||||
queryBuilder.and("attributes." + entry.getKey()).is(entry.getValue());
|
||||
}
|
||||
|
||||
List<MongoUserEntity> users = getMongoStore().loadEntities(MongoUserEntity.class, queryBuilder.get(), invocationContext);
|
||||
return convertUserEntities(realm, users);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<FederatedIdentityModel> getFederatedIdentities(UserModel userModel, RealmModel realm) {
|
||||
UserModel user = getUserById(userModel.getId(), realm);
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
package org.keycloak.protocol.oidc;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.ClientConnection;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.constants.ServiceAccountConstants;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.UserSessionProvider;
|
||||
import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.services.ErrorResponseException;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.ClientManager;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.resources.Cors;
|
||||
|
||||
/**
|
||||
* Endpoint for authenticate clients and retrieve service accounts
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ServiceAccountManager {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(ServiceAccountManager.class);
|
||||
|
||||
private TokenManager tokenManager;
|
||||
private AuthenticationManager authManager;
|
||||
private EventBuilder event;
|
||||
private HttpRequest request;
|
||||
private MultivaluedMap<String, String> formParams;
|
||||
|
||||
private KeycloakSession session;
|
||||
|
||||
private RealmModel realm;
|
||||
private HttpHeaders headers;
|
||||
private UriInfo uriInfo;
|
||||
private ClientConnection clientConnection;
|
||||
|
||||
private ClientModel client;
|
||||
private UserModel clientUser;
|
||||
|
||||
public ServiceAccountManager(TokenManager tokenManager, AuthenticationManager authManager, EventBuilder event, HttpRequest request, MultivaluedMap<String, String> formParams, KeycloakSession session) {
|
||||
this.tokenManager = tokenManager;
|
||||
this.authManager = authManager;
|
||||
this.event = event;
|
||||
this.request = request;
|
||||
this.formParams = formParams;
|
||||
this.session = session;
|
||||
|
||||
this.realm = session.getContext().getRealm();
|
||||
this.headers = session.getContext().getRequestHeaders();
|
||||
this.uriInfo = session.getContext().getUri();
|
||||
this.clientConnection = session.getContext().getConnection();
|
||||
}
|
||||
|
||||
public Response buildClientCredentialsGrant() {
|
||||
authenticateClient();
|
||||
checkClient();
|
||||
return finishClientAuthorization();
|
||||
}
|
||||
|
||||
protected void authenticateClient() {
|
||||
// TODO: This should be externalized into pluggable SPI for client authentication (hopefully Authentication SPI can be reused).
|
||||
// Right now, just Client Credentials Grants (as per OAuth2 specs) is supported
|
||||
String authorizationHeader = headers.getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||
client = AuthorizeClientUtil.authorizeClient(authorizationHeader, formParams, event, realm);
|
||||
event.detail(Details.CLIENT_AUTH_METHOD, Details.CLIENT_AUTH_METHOD_VALUE_CLIENT_CREDENTIALS);
|
||||
}
|
||||
|
||||
protected void checkClient() {
|
||||
if (client.isBearerOnly()) {
|
||||
event.error(Errors.INVALID_CLIENT);
|
||||
throw new ErrorResponseException("unauthorized_client", "Bearer-only client not allowed to retrieve service account", Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
if (client.isPublicClient()) {
|
||||
event.error(Errors.INVALID_CLIENT);
|
||||
throw new ErrorResponseException("unauthorized_client", "Public client not allowed to retrieve service account", Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
if (!client.isServiceAccountsEnabled()) {
|
||||
event.error(Errors.INVALID_CLIENT);
|
||||
throw new ErrorResponseException("unauthorized_client", "Client not enabled to retrieve service account", Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
protected Response finishClientAuthorization() {
|
||||
event.detail(Details.RESPONSE_TYPE, ServiceAccountConstants.CLIENT_AUTH);
|
||||
|
||||
Map<String, String> search = new HashMap<>();
|
||||
search.put(ServiceAccountConstants.SERVICE_ACCOUNT_CLIENT_ATTRIBUTE, client.getId());
|
||||
List<UserModel> users = session.users().searchForUserByUserAttributes(search, realm);
|
||||
|
||||
if (users.size() == 0) {
|
||||
// May need to handle bootstrap here as well
|
||||
logger.warnf("Service account user for client '%s' not found. Creating now", client.getClientId());
|
||||
new ClientManager(new RealmManager(session)).enableServiceAccount(client);
|
||||
users = session.users().searchForUserByUserAttributes(search, realm);
|
||||
clientUser = users.get(0);
|
||||
} else if (users.size() == 1) {
|
||||
clientUser = users.get(0);
|
||||
} else {
|
||||
throw new ModelDuplicateException("Multiple service account users found for client '" + client.getClientId() + "' . Check your DB");
|
||||
}
|
||||
|
||||
String clientUsername = clientUser.getUsername();
|
||||
event.detail(Details.USERNAME, clientUsername);
|
||||
event.user(clientUser);
|
||||
|
||||
if (!clientUser.isEnabled()) {
|
||||
event.error(Errors.USER_DISABLED);
|
||||
throw new ErrorResponseException("invalid_request", "User '" + clientUsername + "' disabled", Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
String scope = formParams.getFirst(OAuth2Constants.SCOPE);
|
||||
|
||||
UserSessionProvider sessions = session.sessions();
|
||||
|
||||
// TODO: Once more requirements are added, clientSession will be likely created earlier by authentication mechanism
|
||||
ClientSessionModel clientSession = sessions.createClientSession(realm, client);
|
||||
clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
clientSession.setNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
|
||||
|
||||
// TODO: Should rather obtain authMethod from client session?
|
||||
UserSessionModel userSession = sessions.createUserSession(realm, clientUser, clientUsername, clientConnection.getRemoteAddr(), ServiceAccountConstants.CLIENT_AUTH, false, null, null);
|
||||
event.session(userSession);
|
||||
|
||||
TokenManager.attachClientSession(userSession, clientSession);
|
||||
|
||||
// Notes about client details
|
||||
userSession.setNote(ServiceAccountConstants.CLIENT_ID, client.getClientId());
|
||||
userSession.setNote(ServiceAccountConstants.CLIENT_HOST, clientConnection.getRemoteHost());
|
||||
userSession.setNote(ServiceAccountConstants.CLIENT_ADDRESS, clientConnection.getRemoteAddr());
|
||||
|
||||
AccessTokenResponse res = tokenManager.responseBuilder(realm, client, event, session, userSession, clientSession)
|
||||
.generateAccessToken(session, scope, client, clientUser, userSession, clientSession)
|
||||
.generateRefreshToken()
|
||||
.generateIDToken()
|
||||
.build();
|
||||
|
||||
event.success();
|
||||
|
||||
return Cors.add(request, Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).auth().allowedOrigins(client).allowedMethods("POST").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
|
||||
}
|
||||
|
||||
}
|
|
@ -22,6 +22,7 @@ import org.keycloak.models.UserSessionProvider;
|
|||
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.ServiceAccountManager;
|
||||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
|
@ -53,7 +54,7 @@ public class TokenEndpoint {
|
|||
private ClientModel client;
|
||||
|
||||
private enum Action {
|
||||
AUTHORIZATION_CODE, REFRESH_TOKEN, PASSWORD
|
||||
AUTHORIZATION_CODE, REFRESH_TOKEN, PASSWORD, CLIENT_CREDENTIALS
|
||||
}
|
||||
|
||||
@Context
|
||||
|
@ -97,7 +98,11 @@ public class TokenEndpoint {
|
|||
checkSsl();
|
||||
checkRealm();
|
||||
checkGrantType();
|
||||
checkClient();
|
||||
|
||||
// client grant type will do it's own verification of client
|
||||
if (!grantType.equals(OAuth2Constants.CLIENT_CREDENTIALS)) {
|
||||
checkClient();
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case AUTHORIZATION_CODE:
|
||||
|
@ -106,6 +111,8 @@ public class TokenEndpoint {
|
|||
return buildRefreshToken();
|
||||
case PASSWORD:
|
||||
return buildResourceOwnerPasswordCredentialsGrant();
|
||||
case CLIENT_CREDENTIALS:
|
||||
return buildClientCredentialsGrant();
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unknown action " + action);
|
||||
|
@ -144,7 +151,7 @@ public class TokenEndpoint {
|
|||
String authorizationHeader = headers.getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||
client = AuthorizeClientUtil.authorizeClient(authorizationHeader, formParams, event, realm);
|
||||
|
||||
if ((client instanceof ClientModel) && ((ClientModel) client).isBearerOnly()) {
|
||||
if (client.isBearerOnly()) {
|
||||
throw new ErrorResponseException("invalid_client", "Bearer-only not allowed", Response.Status.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
@ -167,6 +174,9 @@ public class TokenEndpoint {
|
|||
} else if (grantType.equals(OAuth2Constants.PASSWORD)) {
|
||||
event.event(EventType.LOGIN);
|
||||
action = Action.PASSWORD;
|
||||
} else if (grantType.equals(OAuth2Constants.CLIENT_CREDENTIALS)) {
|
||||
event.event(EventType.CLIENT_LOGIN);
|
||||
action = Action.CLIENT_CREDENTIALS;
|
||||
} else {
|
||||
throw new ErrorResponseException(Errors.INVALID_REQUEST, "Invalid " + OIDCLoginProtocol.GRANT_TYPE_PARAM, Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
@ -355,4 +365,9 @@ public class TokenEndpoint {
|
|||
return Cors.add(request, Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).auth().allowedOrigins(client).allowedMethods("POST").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
|
||||
}
|
||||
|
||||
public Response buildClientCredentialsGrant() {
|
||||
ServiceAccountManager serviceAccountManager = new ServiceAccountManager(tokenManager, authManager, event, request, formParams, session);
|
||||
return serviceAccountManager.buildClientCredentialsGrant();
|
||||
}
|
||||
|
||||
}
|
|
@ -3,10 +3,15 @@ package org.keycloak.services.managers;
|
|||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
import org.codehaus.jackson.annotate.JsonPropertyOrder;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.constants.ServiceAccountConstants;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionProvider;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
|
||||
import org.keycloak.representations.adapters.config.BaseRealmConfig;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.util.Time;
|
||||
|
@ -84,6 +89,57 @@ public class ClientManager {
|
|||
return validatedNodes;
|
||||
}
|
||||
|
||||
public void enableServiceAccount(ClientModel client) {
|
||||
client.setServiceAccountsEnabled(true);
|
||||
|
||||
// Add dedicated user for this service account
|
||||
RealmModel realm = client.getRealm();
|
||||
Map<String, String> search = new HashMap<>();
|
||||
search.put(ServiceAccountConstants.SERVICE_ACCOUNT_CLIENT_ATTRIBUTE, client.getId());
|
||||
List<UserModel> serviceAccountUsers = realmManager.getSession().users().searchForUserByUserAttributes(search, realm);
|
||||
if (serviceAccountUsers.size() == 0) {
|
||||
String username = ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + client.getClientId();
|
||||
logger.infof("Creating service account user '%s'", username);
|
||||
|
||||
UserModel user = realmManager.getSession().users().addUser(realm, username);
|
||||
user.setEnabled(true);
|
||||
user.setEmail(username + "@placeholder.org");
|
||||
user.setSingleAttribute(ServiceAccountConstants.SERVICE_ACCOUNT_CLIENT_ATTRIBUTE, client.getId());
|
||||
}
|
||||
|
||||
// Add protocol mappers to retrieve clientId in access token
|
||||
if (client.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, ServiceAccountConstants.CLIENT_ID_PROTOCOL_MAPPER) == null) {
|
||||
logger.debugf("Creating service account protocol mapper '%s' for client '%s'", ServiceAccountConstants.CLIENT_ID_PROTOCOL_MAPPER, client.getClientId());
|
||||
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_ID_PROTOCOL_MAPPER,
|
||||
ServiceAccountConstants.CLIENT_ID,
|
||||
ServiceAccountConstants.CLIENT_ID, "String",
|
||||
false, "",
|
||||
true, true);
|
||||
client.addProtocolMapper(protocolMapper);
|
||||
}
|
||||
|
||||
// Add protocol mappers to retrieve hostname and IP address of client in access token
|
||||
if (client.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, ServiceAccountConstants.CLIENT_HOST_PROTOCOL_MAPPER) == null) {
|
||||
logger.debugf("Creating service account protocol mapper '%s' for client '%s'", ServiceAccountConstants.CLIENT_HOST_PROTOCOL_MAPPER, client.getClientId());
|
||||
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_HOST_PROTOCOL_MAPPER,
|
||||
ServiceAccountConstants.CLIENT_HOST,
|
||||
ServiceAccountConstants.CLIENT_HOST, "String",
|
||||
false, "",
|
||||
true, true);
|
||||
client.addProtocolMapper(protocolMapper);
|
||||
}
|
||||
|
||||
if (client.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, ServiceAccountConstants.CLIENT_ADDRESS_PROTOCOL_MAPPER) == null) {
|
||||
logger.debugf("Creating service account protocol mapper '%s' for client '%s'", ServiceAccountConstants.CLIENT_ADDRESS_PROTOCOL_MAPPER, client.getClientId());
|
||||
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_ADDRESS_PROTOCOL_MAPPER,
|
||||
ServiceAccountConstants.CLIENT_ADDRESS,
|
||||
ServiceAccountConstants.CLIENT_ADDRESS, "String",
|
||||
false, "",
|
||||
true, true);
|
||||
client.addProtocolMapper(protocolMapper);
|
||||
}
|
||||
}
|
||||
|
||||
@JsonPropertyOrder({"realm", "realm-public-key", "bearer-only", "auth-server-url", "ssl-required",
|
||||
"resource", "public-client", "credentials",
|
||||
"use-resource-role-mappings"})
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue