KEYCLOAK-828 SPNEGO/Kerberos broker - step 1
This commit is contained in:
parent
d1c4d9795b
commit
0c2795cf7c
12 changed files with 530 additions and 0 deletions
34
broker/kerberos/pom.xml
Normal file
34
broker/kerberos/pom.xml
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0"?>
|
||||
<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-broker-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.Beta1-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-broker-kerberos</artifactId>
|
||||
<name>Keycloak Broker Kerberos</name>
|
||||
<description/>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-broker-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.iharder</groupId>
|
||||
<artifactId>base64</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,25 @@
|
|||
package org.keycloak.broker.kerberos;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class KerberosConstants {
|
||||
|
||||
/**
|
||||
* Value of HTTP Headers "WWW-Authenticate" or "Authorization" used for SPNEGO/Kerberos
|
||||
**/
|
||||
public static final String NEGOTIATE = "Negotiate";
|
||||
|
||||
|
||||
/**
|
||||
* Helper parameter for relay state
|
||||
*/
|
||||
public static final String RELAY_STATE_PARAM = "RelayState";
|
||||
|
||||
|
||||
/**
|
||||
* OID of SPNEGO mechanism. See http://www.oid-info.com/get/1.3.6.1.5.5.2
|
||||
*/
|
||||
public static final String SPNEGO_OID = "1.3.6.1.5.5.2";
|
||||
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
package org.keycloak.broker.kerberos;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.broker.kerberos.impl.KerberosServerSubjectAuthenticator;
|
||||
import org.keycloak.broker.kerberos.impl.SPNEGOAuthenticator;
|
||||
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||
import org.keycloak.broker.provider.AuthenticationResponse;
|
||||
import org.keycloak.broker.provider.FederatedIdentity;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class KerberosIdentityProvider extends AbstractIdentityProvider<KerberosIdentityProviderConfig> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(KerberosIdentityProvider.class);
|
||||
|
||||
public KerberosIdentityProvider(KerberosIdentityProviderConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AuthenticationResponse handleRequest(AuthenticationRequest request) {
|
||||
// TODO: trace
|
||||
logger.info("handleRequest");
|
||||
|
||||
// Just redirect to handleResponse for now
|
||||
URI redirectUri = UriBuilder.fromUri(request.getRedirectUri()).queryParam(KerberosConstants.RELAY_STATE_PARAM, request.getState()).build();
|
||||
Response response = Response.status(302)
|
||||
.location(redirectUri)
|
||||
.build();
|
||||
|
||||
return AuthenticationResponse.fromResponse(response);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getRelayState(AuthenticationRequest request) {
|
||||
UriInfo uriInfo = request.getUriInfo();
|
||||
return uriInfo.getQueryParameters().getFirst(KerberosConstants.RELAY_STATE_PARAM);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AuthenticationResponse handleResponse(AuthenticationRequest request) {
|
||||
String authHeader = request.getHttpRequest().getHttpHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||
|
||||
// Case when we don't yet have any Negotiate header
|
||||
if (authHeader == null) {
|
||||
return sendNegotiateResponse(null);
|
||||
}
|
||||
|
||||
String[] tokens = authHeader.split(" ");
|
||||
if (tokens.length != 2) {
|
||||
logger.warn("Invalid length of tokens: " + tokens.length);
|
||||
return sendNegotiateResponse(null);
|
||||
} else if (!KerberosConstants.NEGOTIATE.equalsIgnoreCase(tokens[0])) {
|
||||
logger.warn("Unknown scheme " + tokens[0]);
|
||||
return sendNegotiateResponse(null);
|
||||
} else {
|
||||
String spnegoToken = tokens[1];
|
||||
SPNEGOAuthenticator spnegoAuthenticator = createSPNEGOAuthenticator(spnegoToken);
|
||||
spnegoAuthenticator.authenticate();
|
||||
|
||||
if (spnegoAuthenticator.isAuthenticated()) {
|
||||
FederatedIdentity federatedIdentity = getFederatedIdentity(spnegoAuthenticator);
|
||||
return AuthenticationResponse.end(federatedIdentity);
|
||||
} else {
|
||||
return sendNegotiateResponse(spnegoAuthenticator.getResponseToken());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected SPNEGOAuthenticator createSPNEGOAuthenticator(String spnegoToken) {
|
||||
KerberosServerSubjectAuthenticator kerberosAuth = createKerberosSubjectAuthenticator();
|
||||
return new SPNEGOAuthenticator(kerberosAuth, spnegoToken);
|
||||
}
|
||||
|
||||
protected KerberosServerSubjectAuthenticator createKerberosSubjectAuthenticator() {
|
||||
return new KerberosServerSubjectAuthenticator(getConfig());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send response with header "WWW-Authenticate: Negotiate {negotiateToken}"
|
||||
*
|
||||
* @param negotiateToken token to be send back in response or null if just "WWW-Authenticate: Negotiate" should be sent
|
||||
* @return AuthenticationResponse
|
||||
*/
|
||||
protected AuthenticationResponse sendNegotiateResponse(String negotiateToken) {
|
||||
String negotiateHeader = negotiateToken == null ? KerberosConstants.NEGOTIATE : KerberosConstants.NEGOTIATE + " " + negotiateToken;
|
||||
|
||||
Response response = Response.status(Response.Status.UNAUTHORIZED)
|
||||
.header(HttpHeaders.WWW_AUTHENTICATE, negotiateHeader)
|
||||
.build();
|
||||
return AuthenticationResponse.fromResponse(response);
|
||||
}
|
||||
|
||||
|
||||
protected FederatedIdentity getFederatedIdentity(SPNEGOAuthenticator spnegoAuthenticator) {
|
||||
String kerberosUsername = spnegoAuthenticator.getPrincipal();
|
||||
FederatedIdentity user = new FederatedIdentity(kerberosUsername);
|
||||
user.setUsername(kerberosUsername);
|
||||
|
||||
// Just guessing email, but likely can't do anything better...
|
||||
String[] tokens = kerberosUsername.split("@");
|
||||
String email = tokens[0] + "@" + tokens[1].toLowerCase();
|
||||
user.setEmail(email);
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Response retrieveToken(FederatedIdentityModel identity) {
|
||||
logger.warn("retrieveToken unsupported for Kerberos right now");
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package org.keycloak.broker.kerberos;
|
||||
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class KerberosIdentityProviderConfig extends IdentityProviderModel {
|
||||
|
||||
public KerberosIdentityProviderConfig(IdentityProviderModel identityProviderModel) {
|
||||
super(identityProviderModel);
|
||||
}
|
||||
|
||||
public String getServerPrincipal() {
|
||||
return getConfig().get("serverPrincipal");
|
||||
}
|
||||
|
||||
public String getKeyTab() {
|
||||
return getConfig().get("keyTab");
|
||||
}
|
||||
|
||||
public boolean getDebug() {
|
||||
return Boolean.valueOf(getConfig().get("debug"));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package org.keycloak.broker.kerberos;
|
||||
|
||||
import org.keycloak.broker.kerberos.KerberosIdentityProvider;
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class KerberosIdentityProviderFactory extends AbstractIdentityProviderFactory<KerberosIdentityProvider> {
|
||||
|
||||
public static final String PROVIDER_ID = "kerberos";
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Kerberos";
|
||||
}
|
||||
|
||||
@Override
|
||||
public KerberosIdentityProvider create(IdentityProviderModel model) {
|
||||
return new KerberosIdentityProvider(new KerberosIdentityProviderConfig(model));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package org.keycloak.broker.kerberos.impl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.login.AppConfigurationEntry;
|
||||
import javax.security.auth.login.Configuration;
|
||||
import javax.security.auth.login.LoginContext;
|
||||
import javax.security.auth.login.LoginException;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.broker.kerberos.KerberosIdentityProviderConfig;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class KerberosServerSubjectAuthenticator {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(KerberosServerSubjectAuthenticator.class);
|
||||
|
||||
private final KerberosIdentityProviderConfig config;
|
||||
private LoginContext loginContext;
|
||||
|
||||
public KerberosServerSubjectAuthenticator(KerberosIdentityProviderConfig config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public Subject authenticateServerSubject() throws LoginException {
|
||||
Configuration config = createJaasConfiguration();
|
||||
loginContext = new LoginContext("does-not-matter", null, null, config);
|
||||
loginContext.login();
|
||||
return loginContext.getSubject();
|
||||
}
|
||||
|
||||
public void logoutServerSubject() {
|
||||
if (loginContext != null) {
|
||||
try {
|
||||
loginContext.logout();
|
||||
} catch (LoginException le) {
|
||||
logger.error("Failed to logout kerberos server subject: " + config.getServerPrincipal(), le);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Configuration createJaasConfiguration() {
|
||||
return new Configuration() {
|
||||
|
||||
@Override
|
||||
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
|
||||
Map<String, Object> options = new HashMap<String, Object>();
|
||||
options.put("storeKey", "true");
|
||||
options.put("doNotPrompt", "true");
|
||||
options.put("isInitiator", "false");
|
||||
options.put("useKeyTab", "true");
|
||||
|
||||
options.put("keyTab", config.getKeyTab());
|
||||
options.put("principal", config.getServerPrincipal());
|
||||
options.put("debug", String.valueOf(config.getDebug()));
|
||||
AppConfigurationEntry kerberosLMConfiguration = new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
|
||||
return new AppConfigurationEntry[] { kerberosLMConfiguration };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
package org.keycloak.broker.kerberos.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
import net.iharder.Base64;
|
||||
import org.ietf.jgss.GSSContext;
|
||||
import org.ietf.jgss.GSSCredential;
|
||||
import org.ietf.jgss.GSSException;
|
||||
import org.ietf.jgss.GSSManager;
|
||||
import org.ietf.jgss.Oid;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.broker.kerberos.KerberosConstants;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class SPNEGOAuthenticator {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SPNEGOAuthenticator.class);
|
||||
|
||||
private static final GSSManager GSS_MANAGER = GSSManager.getInstance();
|
||||
|
||||
private final KerberosServerSubjectAuthenticator kerberosSubjectAuthenticator;
|
||||
private final String spnegoToken;
|
||||
|
||||
private boolean authenticated = false;
|
||||
private String principal = null;
|
||||
private GSSCredential delegationCredential;
|
||||
private String responseToken = null;
|
||||
|
||||
public SPNEGOAuthenticator(KerberosServerSubjectAuthenticator kerberosSubjectAuthenticator, String spnegoToken) {
|
||||
this.kerberosSubjectAuthenticator = kerberosSubjectAuthenticator;
|
||||
this.spnegoToken = spnegoToken;
|
||||
}
|
||||
|
||||
public void authenticate() {
|
||||
// TODO: debug
|
||||
logger.info("SPNEGO Login with token: " + spnegoToken);
|
||||
|
||||
try {
|
||||
Subject serverSubject = kerberosSubjectAuthenticator.authenticateServerSubject();
|
||||
authenticated = Subject.doAs(serverSubject, new AcceptSecContext());
|
||||
} catch (Exception e) {
|
||||
logger.warn("SPNEGO login failed: " + e.getMessage());
|
||||
|
||||
// TODO: debug and check if it is shown in the log
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("SPNEGO login failed: " + e.getMessage(), e);
|
||||
}
|
||||
} finally {
|
||||
kerberosSubjectAuthenticator.logoutServerSubject();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAuthenticated() {
|
||||
return authenticated;
|
||||
}
|
||||
|
||||
public String getPrincipal() {
|
||||
return principal;
|
||||
}
|
||||
|
||||
public String getResponseToken() {
|
||||
return responseToken;
|
||||
}
|
||||
|
||||
public GSSCredential getDelegationCredential() {
|
||||
return delegationCredential;
|
||||
}
|
||||
|
||||
private class AcceptSecContext implements PrivilegedExceptionAction<Boolean> {
|
||||
|
||||
@Override
|
||||
public Boolean run() throws Exception {
|
||||
GSSContext gssContext = null;
|
||||
try {
|
||||
// TODO: debug
|
||||
logger.info("Going to establish security context");
|
||||
gssContext = establishContext();
|
||||
logAuthDetails(gssContext);
|
||||
|
||||
// What should be done with delegation credential? Figure out if there are use-cases for storing it as claims in FederatedIdentity
|
||||
if (gssContext.getCredDelegState()) {
|
||||
delegationCredential = gssContext.getDelegCred();
|
||||
}
|
||||
|
||||
if (gssContext.isEstablished()) {
|
||||
principal = gssContext.getSrcName().toString();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
if (gssContext != null) {
|
||||
gssContext.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected GSSContext establishContext() throws GSSException, IOException {
|
||||
Oid spnegoOid = new Oid(KerberosConstants.SPNEGO_OID);
|
||||
GSSCredential credential = GSS_MANAGER.createCredential(null,
|
||||
GSSCredential.DEFAULT_LIFETIME,
|
||||
spnegoOid,
|
||||
GSSCredential.ACCEPT_ONLY);
|
||||
GSSContext gssContext = GSS_MANAGER.createContext(credential);
|
||||
|
||||
byte[] inputToken = Base64.decode(spnegoToken);
|
||||
byte[] respToken = gssContext.acceptSecContext(inputToken, 0, inputToken.length);
|
||||
responseToken = Base64.encodeBytes(respToken);
|
||||
|
||||
return gssContext;
|
||||
}
|
||||
|
||||
protected void logAuthDetails(GSSContext gssContext) throws GSSException {
|
||||
|
||||
// TODO: debug
|
||||
if (logger.isInfoEnabled()) {
|
||||
String message = new StringBuilder("SPNEGO Security context accepted with token: " + responseToken)
|
||||
.append(", established: " + gssContext.isEstablished())
|
||||
.append(", credDelegState: " + gssContext.getCredDelegState())
|
||||
.append(", mutualAuthState: " + gssContext.getMutualAuthState())
|
||||
.append(", lifetime: " + gssContext.getLifetime())
|
||||
.append(", confState: " + gssContext.getConfState())
|
||||
.append(", integState: " + gssContext.getIntegState())
|
||||
.append(", srcName: " + gssContext.getSrcName())
|
||||
.append(", targName: " + gssContext.getTargName())
|
||||
.toString();
|
||||
logger.info(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.broker.kerberos.KerberosIdentityProviderFactory
|
|
@ -18,6 +18,7 @@
|
|||
<module>core</module>
|
||||
<module>oidc</module>
|
||||
<module>saml</module>
|
||||
<module>kerberos</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
|
|
5
dependencies/server-all/pom.xml
vendored
5
dependencies/server-all/pom.xml
vendored
|
@ -93,6 +93,11 @@
|
|||
<artifactId>keycloak-broker-saml</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-broker-kerberos</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-social-github</artifactId>
|
||||
|
|
|
@ -785,6 +785,14 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
|
|||
$scope.identityProvider.config.postBindingResponse = true;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.initKerberosProvider = function() {
|
||||
if (instance && instance.id) {
|
||||
$scope.identityProvider.config.debug = $scope.getBoolean($scope.identityProvider.config.debug);
|
||||
} else {
|
||||
$scope.identityProvider.config.debug = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http, $location, $route, Dialog, Notifications, TimeUnit) {
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
<div class="bs-sidebar col-sm-3 " data-ng-include data-src="'partials/realm-menu.html'"></div>
|
||||
<div id="content-area" class="col-sm-9" role="main" data-ng-init="initKerberosProvider()">
|
||||
<data-kc-navigation data-kc-current="social" data-kc-realm="realm.realm" data-kc-social="realm.social"></data-kc-navigation>
|
||||
<h2></h2>
|
||||
<div id="content">
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Providers</a></li>
|
||||
<li class="active">{{identityProvider.name}} Provider Settings</li>
|
||||
</ol>
|
||||
<h2 class="pull-left">{{identityProvider.name}} Provider Settings</h2>
|
||||
<p class="subtitle"><span class="required">*</span> Required fields</p>
|
||||
<form class="form-horizontal" name="realmForm" novalidate>
|
||||
<fieldset>
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-sm-2 control-label" for="identifier">Alias <span class="required">*</span></label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" id="identifier" type="text" ng-model="identityProvider.id" required>
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="The alias unique identifies an identity provider and it is also used to build the redirect uri." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-sm-2 control-label" for="name">Name <span class="required">*</span></label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" id="name" type="text" ng-model="identityProvider.name" required>
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="The friendly name for this identity provider." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-sm-2 control-label" for="serverPrincipal">Server principal <span class="required">*</span></label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" id="serverPrincipal" type="text" ng-model="identityProvider.config.serverPrincipal" required>
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Full name of server principal for HTTP service including server and domain name. For example HTTP/host.foo.org@FOO.ORG" class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-sm-2 control-label" for="keyTab">KeyTab <span class="required">*</span></label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" id="keyTab" type="text" ng-model="identityProvider.config.keyTab" required>
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Location of Kerberos KeyTab file containing the credentials of server principal. For example /etc/krb5.keytab" class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="debug">Debug </label>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="identityProvider.config.debug" id="debug" onoffswitch />
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Enable/disable debug logging to standard output for Krb5LoginModule." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="enabled">Enabled</label>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="identityProvider.enabled" id="enabled" onoffswitch />
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Enable/disable this identity provider." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="updateProfileFirstLogin">Update Profile on First Login</label>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="identityProvider.updateProfileFirstLogin" name="identityProvider.updateProfileFirstLogin" id="updateProfileFirstLogin" onoffswitch />
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Indicates if user must update his profile right after the first login." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="pull-right form-actions">
|
||||
<button kc-save>Save</button>
|
||||
<button kc-delete data-ng-click="remove()" data-ng-show="!newIdentityProvider">Delete</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in a new issue