Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
06fca9c897
40 changed files with 591 additions and 222 deletions
|
@ -160,6 +160,12 @@ public class OAuthRequestAuthenticator {
|
||||||
String scope = getQueryParamValue(OAuth2Constants.SCOPE);
|
String scope = getQueryParamValue(OAuth2Constants.SCOPE);
|
||||||
url = UriUtils.stripQueryParam(url, OAuth2Constants.SCOPE);
|
url = UriUtils.stripQueryParam(url, OAuth2Constants.SCOPE);
|
||||||
|
|
||||||
|
String prompt = getQueryParamValue(OAuth2Constants.PROMPT);
|
||||||
|
url = UriUtils.stripQueryParam(url, OAuth2Constants.PROMPT);
|
||||||
|
|
||||||
|
String maxAge = getQueryParamValue(OAuth2Constants.MAX_AGE);
|
||||||
|
url = UriUtils.stripQueryParam(url, OAuth2Constants.MAX_AGE);
|
||||||
|
|
||||||
KeycloakUriBuilder redirectUriBuilder = deployment.getAuthUrl().clone()
|
KeycloakUriBuilder redirectUriBuilder = deployment.getAuthUrl().clone()
|
||||||
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
|
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
|
||||||
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
|
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
|
||||||
|
@ -172,6 +178,12 @@ public class OAuthRequestAuthenticator {
|
||||||
if (idpHint != null && idpHint.length() > 0) {
|
if (idpHint != null && idpHint.length() > 0) {
|
||||||
redirectUriBuilder.queryParam(AdapterConstants.KC_IDP_HINT,idpHint);
|
redirectUriBuilder.queryParam(AdapterConstants.KC_IDP_HINT,idpHint);
|
||||||
}
|
}
|
||||||
|
if (prompt != null && prompt.length() > 0) {
|
||||||
|
redirectUriBuilder.queryParam(OAuth2Constants.PROMPT, prompt);
|
||||||
|
}
|
||||||
|
if (maxAge != null && maxAge.length() > 0) {
|
||||||
|
redirectUriBuilder.queryParam(OAuth2Constants.MAX_AGE, maxAge);
|
||||||
|
}
|
||||||
|
|
||||||
scope = TokenUtil.attachOIDCScope(scope);
|
scope = TokenUtil.attachOIDCScope(scope);
|
||||||
redirectUriBuilder.queryParam(OAuth2Constants.SCOPE, scope);
|
redirectUriBuilder.queryParam(OAuth2Constants.SCOPE, scope);
|
||||||
|
|
|
@ -224,6 +224,10 @@
|
||||||
url += '&prompt=' + encodeURIComponent(options.prompt);
|
url += '&prompt=' + encodeURIComponent(options.prompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options && options.maxAge) {
|
||||||
|
url += '&max_age=' + encodeURIComponent(options.maxAge);
|
||||||
|
}
|
||||||
|
|
||||||
if (options && options.loginHint) {
|
if (options && options.loginHint) {
|
||||||
url += '&login_hint=' + encodeURIComponent(options.loginHint);
|
url += '&login_hint=' + encodeURIComponent(options.loginHint);
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,10 @@ public interface OAuth2Constants {
|
||||||
|
|
||||||
String UI_LOCALES_PARAM = "ui_locales";
|
String UI_LOCALES_PARAM = "ui_locales";
|
||||||
|
|
||||||
|
String PROMPT = "prompt";
|
||||||
|
|
||||||
|
String MAX_AGE = "max_age";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,11 +29,13 @@ public class OAuthErrorException extends Exception {
|
||||||
public static final String ACCESS_DENIED = "access_denied";
|
public static final String ACCESS_DENIED = "access_denied";
|
||||||
public static final String UNSUPPORTED_RESPONSE_TYPE = "unsupported_response_type";
|
public static final String UNSUPPORTED_RESPONSE_TYPE = "unsupported_response_type";
|
||||||
public static final String SERVER_ERROR = "server_error";
|
public static final String SERVER_ERROR = "server_error";
|
||||||
public static final String TEMPORARILY_UNAVAILABKE = "temporarily_unavailable";
|
public static final String TEMPORARILY_UNAVAILABLE = "temporarily_unavailable";
|
||||||
|
|
||||||
// OpenID Connect 1
|
// OpenID Connect 1
|
||||||
public static final String INTERACTION_REQUIRED = "interaction_required";
|
public static final String INTERACTION_REQUIRED = "interaction_required";
|
||||||
public static final String LOGIN_REQUIRED = "login_required";
|
public static final String LOGIN_REQUIRED = "login_required";
|
||||||
|
public static final String REQUEST_NOT_SUPPORTED = "request_not_supported";
|
||||||
|
public static final String REQUEST_URI_NOT_SUPPORTED = "request_uri_not_supported";
|
||||||
|
|
||||||
// Others
|
// Others
|
||||||
public static final String INVALID_CLIENT = "invalid_client";
|
public static final String INVALID_CLIENT = "invalid_client";
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package org.keycloak.representations.adapters.config;
|
package org.keycloak.representations.adapters.config;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -30,24 +31,30 @@ import java.util.List;
|
||||||
public class PolicyEnforcerConfig {
|
public class PolicyEnforcerConfig {
|
||||||
|
|
||||||
@JsonProperty("create-resources")
|
@JsonProperty("create-resources")
|
||||||
private Boolean createResources;
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
private Boolean createResources = Boolean.FALSE;
|
||||||
|
|
||||||
@JsonProperty("enforcement-mode")
|
@JsonProperty("enforcement-mode")
|
||||||
private EnforcementMode enforcementMode = EnforcementMode.ENFORCING;
|
private EnforcementMode enforcementMode = EnforcementMode.ENFORCING;
|
||||||
|
|
||||||
@JsonProperty("user-managed-access")
|
@JsonProperty("user-managed-access")
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
private UmaProtocolConfig umaProtocolConfig;
|
private UmaProtocolConfig umaProtocolConfig;
|
||||||
|
|
||||||
@JsonProperty("entitlement")
|
@JsonProperty("entitlement")
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
private EntitlementProtocolConfig entitlementProtocolConfig;
|
private EntitlementProtocolConfig entitlementProtocolConfig;
|
||||||
|
|
||||||
@JsonProperty("paths")
|
@JsonProperty("paths")
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
private List<PathConfig> paths = new ArrayList<>();
|
private List<PathConfig> paths = new ArrayList<>();
|
||||||
|
|
||||||
@JsonProperty("online-introspection")
|
@JsonProperty("online-introspection")
|
||||||
private Boolean onlineIntrospection;
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
private Boolean onlineIntrospection = Boolean.FALSE;
|
||||||
|
|
||||||
@JsonProperty("on-deny-redirect-to")
|
@JsonProperty("on-deny-redirect-to")
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
private String accessDeniedPath;
|
private String accessDeniedPath;
|
||||||
|
|
||||||
public Boolean isCreateResources() {
|
public Boolean isCreateResources() {
|
||||||
|
@ -55,10 +62,6 @@ public class PolicyEnforcerConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PathConfig> getPaths() {
|
public List<PathConfig> getPaths() {
|
||||||
if (this.paths == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Collections.unmodifiableList(this.paths);
|
return Collections.unmodifiableList(this.paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,6 +85,14 @@ public class PolicyEnforcerConfig {
|
||||||
return onlineIntrospection;
|
return onlineIntrospection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCreateResources(Boolean createResources) {
|
||||||
|
this.createResources = createResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnlineIntrospection(Boolean onlineIntrospection) {
|
||||||
|
this.onlineIntrospection = onlineIntrospection;
|
||||||
|
}
|
||||||
|
|
||||||
public void setPaths(List<PathConfig> paths) {
|
public void setPaths(List<PathConfig> paths) {
|
||||||
this.paths = paths;
|
this.paths = paths;
|
||||||
}
|
}
|
||||||
|
|
|
@ -369,7 +369,7 @@ public class CachedPolicyStore implements PolicyStore {
|
||||||
|
|
||||||
if (getId() == null) return false;
|
if (getId() == null) return false;
|
||||||
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (!Policy.class.isInstance(o)) return false;
|
||||||
|
|
||||||
Policy that = (Policy) o;
|
Policy that = (Policy) o;
|
||||||
|
|
||||||
|
|
|
@ -200,7 +200,7 @@ public class CachedPolicy implements Policy {
|
||||||
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
AbstractIdentifiableEntity that = (AbstractIdentifiableEntity) o;
|
Policy that = (Policy) o;
|
||||||
|
|
||||||
if (!getId().equals(that.getId())) return false;
|
if (!getId().equals(that.getId())) return false;
|
||||||
|
|
||||||
|
|
|
@ -236,9 +236,9 @@ public class PolicyEntity implements Policy {
|
||||||
|
|
||||||
if (this.id == null) return false;
|
if (this.id == null) return false;
|
||||||
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (!Policy.class.isInstance(o)) return false;
|
||||||
|
|
||||||
AbstractIdentifiableEntity that = (AbstractIdentifiableEntity) o;
|
Policy that = (Policy) o;
|
||||||
|
|
||||||
if (!getId().equals(that.getId())) return false;
|
if (!getId().equals(that.getId())) return false;
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,9 @@ import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
|
||||||
import org.keycloak.connections.jpa.util.JpaUtils;
|
import org.keycloak.connections.jpa.util.JpaUtils;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
import org.keycloak.models.KeycloakSessionTask;
|
||||||
import org.keycloak.models.dblock.DBLockProvider;
|
import org.keycloak.models.dblock.DBLockProvider;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.provider.ServerInfoAwareProviderFactory;
|
import org.keycloak.provider.ServerInfoAwareProviderFactory;
|
||||||
import org.keycloak.models.dblock.DBLockManager;
|
import org.keycloak.models.dblock.DBLockManager;
|
||||||
import org.keycloak.timer.TimerProvider;
|
import org.keycloak.timer.TimerProvider;
|
||||||
|
@ -93,8 +95,6 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
|
||||||
if (emf == null) {
|
if (emf == null) {
|
||||||
logger.debug("Initializing JPA connections");
|
logger.debug("Initializing JPA connections");
|
||||||
|
|
||||||
Connection connection = null;
|
|
||||||
|
|
||||||
Map<String, Object> properties = new HashMap<String, Object>();
|
Map<String, Object> properties = new HashMap<String, Object>();
|
||||||
|
|
||||||
String unitName = "keycloak-default";
|
String unitName = "keycloak-default";
|
||||||
|
@ -126,23 +126,26 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String databaseSchema = config.get("databaseSchema");
|
String databaseSchema;
|
||||||
if (databaseSchema == null) {
|
String databaseSchemaConf = config.get("databaseSchema");
|
||||||
|
if (databaseSchemaConf == null) {
|
||||||
throw new RuntimeException("Property 'databaseSchema' needs to be specified in the configuration");
|
throw new RuntimeException("Property 'databaseSchema' needs to be specified in the configuration");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (databaseSchema.equals("development-update")) {
|
if (databaseSchemaConf.equals("development-update")) {
|
||||||
properties.put("hibernate.hbm2ddl.auto", "update");
|
properties.put("hibernate.hbm2ddl.auto", "update");
|
||||||
databaseSchema = null;
|
databaseSchema = null;
|
||||||
} else if (databaseSchema.equals("development-validate")) {
|
} else if (databaseSchemaConf.equals("development-validate")) {
|
||||||
properties.put("hibernate.hbm2ddl.auto", "validate");
|
properties.put("hibernate.hbm2ddl.auto", "validate");
|
||||||
databaseSchema = null;
|
databaseSchema = null;
|
||||||
|
} else {
|
||||||
|
databaseSchema = databaseSchemaConf;
|
||||||
}
|
}
|
||||||
|
|
||||||
properties.put("hibernate.show_sql", config.getBoolean("showSql", false));
|
properties.put("hibernate.show_sql", config.getBoolean("showSql", false));
|
||||||
properties.put("hibernate.format_sql", config.getBoolean("formatSql", true));
|
properties.put("hibernate.format_sql", config.getBoolean("formatSql", true));
|
||||||
|
|
||||||
connection = getConnection();
|
Connection connection = getConnection();
|
||||||
try{
|
try{
|
||||||
prepareOperationalInfo(connection);
|
prepareOperationalInfo(connection);
|
||||||
|
|
||||||
|
@ -161,19 +164,27 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
|
||||||
|
|
||||||
// Check if having DBLock before trying to initialize hibernate
|
// Check if having DBLock before trying to initialize hibernate
|
||||||
DBLockProvider dbLock = new DBLockManager(session).getDBLock();
|
DBLockProvider dbLock = new DBLockManager(session).getDBLock();
|
||||||
if (!dbLock.hasLock()) {
|
if (dbLock.hasLock()) {
|
||||||
throw new IllegalStateException("Trying to update database, but don't have a DB lock acquired");
|
updateOrValidateDB(databaseSchema, connection, updater, schema);
|
||||||
|
} else {
|
||||||
|
logger.trace("Don't have DBLock retrieved before upgrade. Needs to acquire lock first in separate transaction");
|
||||||
|
|
||||||
|
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), new KeycloakSessionTask() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(KeycloakSession lockSession) {
|
||||||
|
DBLockManager dbLockManager = new DBLockManager(lockSession);
|
||||||
|
DBLockProvider dbLock2 = dbLockManager.getDBLock();
|
||||||
|
dbLock2.waitForLock();
|
||||||
|
try {
|
||||||
|
updateOrValidateDB(databaseSchema, connection, updater, schema);
|
||||||
|
} finally {
|
||||||
|
dbLock2.releaseLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (databaseSchema.equals("update")) {
|
|
||||||
updater.update(connection, schema);
|
|
||||||
} else if (databaseSchema.equals("validate")) {
|
|
||||||
updater.validate(connection, schema);
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.trace("Database update completed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int globalStatsInterval = config.getInt("globalStatsInterval", -1);
|
int globalStatsInterval = config.getInt("globalStatsInterval", -1);
|
||||||
|
@ -272,6 +283,20 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Needs to be called with acquired DBLock
|
||||||
|
protected void updateOrValidateDB(String databaseSchema, Connection connection, JpaUpdaterProvider updater, String schema) {
|
||||||
|
if (databaseSchema.equals("update")) {
|
||||||
|
updater.update(connection, schema);
|
||||||
|
logger.trace("Database update completed");
|
||||||
|
} else if (databaseSchema.equals("validate")) {
|
||||||
|
updater.validate(connection, schema);
|
||||||
|
logger.trace("Database validation completed");
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Connection getConnection() {
|
public Connection getConnection() {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -34,8 +34,10 @@ import org.keycloak.connections.mongo.impl.context.TransactionMongoStoreInvocati
|
||||||
import org.keycloak.connections.mongo.updater.MongoUpdaterProvider;
|
import org.keycloak.connections.mongo.updater.MongoUpdaterProvider;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
import org.keycloak.models.KeycloakSessionTask;
|
||||||
import org.keycloak.models.dblock.DBLockManager;
|
import org.keycloak.models.dblock.DBLockManager;
|
||||||
import org.keycloak.models.dblock.DBLockProvider;
|
import org.keycloak.models.dblock.DBLockProvider;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.provider.ServerInfoAwareProviderFactory;
|
import org.keycloak.provider.ServerInfoAwareProviderFactory;
|
||||||
|
|
||||||
import com.mongodb.DB;
|
import com.mongodb.DB;
|
||||||
|
@ -174,8 +176,26 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
||||||
}
|
}
|
||||||
|
|
||||||
DBLockProvider dbLock = new DBLockManager(session).getDBLock();
|
DBLockProvider dbLock = new DBLockManager(session).getDBLock();
|
||||||
if (!dbLock.hasLock()) {
|
if (dbLock.hasLock()) {
|
||||||
throw new IllegalStateException("Trying to update database, but don't have a DB lock acquired");
|
updateOrValidateDB(databaseSchema, session, mongoUpdater);
|
||||||
|
} else {
|
||||||
|
logger.trace("Don't have DBLock retrieved before upgrade. Needs to acquire lock first in separate transaction");
|
||||||
|
|
||||||
|
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), new KeycloakSessionTask() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(KeycloakSession lockSession) {
|
||||||
|
DBLockManager dbLockManager = new DBLockManager(lockSession);
|
||||||
|
DBLockProvider dbLock2 = dbLockManager.getDBLock();
|
||||||
|
dbLock2.waitForLock();
|
||||||
|
try {
|
||||||
|
updateOrValidateDB(databaseSchema, session, mongoUpdater);
|
||||||
|
} finally {
|
||||||
|
dbLock2.releaseLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (databaseSchema.equals("update")) {
|
if (databaseSchema.equals("update")) {
|
||||||
|
@ -197,6 +217,16 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
||||||
return entityClasses;
|
return entityClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void updateOrValidateDB(String databaseSchema, KeycloakSession session, MongoUpdaterProvider mongoUpdater) {
|
||||||
|
if (databaseSchema.equals("update")) {
|
||||||
|
mongoUpdater.update(session, db);
|
||||||
|
} else if (databaseSchema.equals("validate")) {
|
||||||
|
mongoUpdater.validate(session, db);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
if (client != null) {
|
if (client != null) {
|
||||||
|
|
|
@ -74,4 +74,11 @@ public interface LoginProtocol extends Provider {
|
||||||
Response frontchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession);
|
Response frontchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession);
|
||||||
Response finishLogout(UserSessionModel userSession);
|
Response finishLogout(UserSessionModel userSession);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param userSession
|
||||||
|
* @param clientSession
|
||||||
|
* @return true if SSO cookie authentication can't be used. User will need to "actively" reauthenticate
|
||||||
|
*/
|
||||||
|
boolean requireReauthentication(UserSessionModel userSession, ClientSessionModel clientSession);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
|
import org.keycloak.protocol.LoginProtocol;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.services.ServicesLogger;
|
import org.keycloak.services.ServicesLogger;
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
|
@ -36,8 +37,6 @@ import org.keycloak.util.TokenUtil;
|
||||||
*/
|
*/
|
||||||
public class CookieAuthenticator implements Authenticator {
|
public class CookieAuthenticator implements Authenticator {
|
||||||
|
|
||||||
private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean requiresUser() {
|
public boolean requiresUser() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -50,11 +49,13 @@ public class CookieAuthenticator implements Authenticator {
|
||||||
if (authResult == null) {
|
if (authResult == null) {
|
||||||
context.attempted();
|
context.attempted();
|
||||||
} else {
|
} else {
|
||||||
|
ClientSessionModel clientSession = context.getClientSession();
|
||||||
|
LoginProtocol protocol = context.getSession().getProvider(LoginProtocol.class, clientSession.getAuthMethod());
|
||||||
|
|
||||||
// Cookie re-authentication is skipped if re-authentication is required
|
// Cookie re-authentication is skipped if re-authentication is required
|
||||||
if (requireReauthentication(authResult.getSession(), context.getClientSession())) {
|
if (protocol.requireReauthentication(authResult.getSession(), clientSession)) {
|
||||||
context.attempted();
|
context.attempted();
|
||||||
} else {
|
} else {
|
||||||
ClientSessionModel clientSession = context.getClientSession();
|
|
||||||
clientSession.setNote(AuthenticationManager.SSO_AUTH, "true");
|
clientSession.setNote(AuthenticationManager.SSO_AUTH, "true");
|
||||||
|
|
||||||
context.setUser(authResult.getUser());
|
context.setUser(authResult.getUser());
|
||||||
|
@ -83,32 +84,4 @@ public class CookieAuthenticator implements Authenticator {
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean requireReauthentication(UserSessionModel userSession, ClientSessionModel clientSession) {
|
|
||||||
return isPromptLogin(clientSession) || isAuthTimeExpired(userSession, clientSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isPromptLogin(ClientSessionModel clientSession) {
|
|
||||||
String prompt = clientSession.getNote(OIDCLoginProtocol.PROMPT_PARAM);
|
|
||||||
return TokenUtil.hasPrompt(prompt, OIDCLoginProtocol.PROMPT_VALUE_LOGIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isAuthTimeExpired(UserSessionModel userSession, ClientSessionModel clientSession) {
|
|
||||||
String authTime = userSession.getNote(AuthenticationManager.AUTH_TIME);
|
|
||||||
String maxAge = clientSession.getNote(OIDCLoginProtocol.MAX_AGE_PARAM);
|
|
||||||
if (maxAge == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int authTimeInt = authTime==null ? 0 : Integer.parseInt(authTime);
|
|
||||||
int maxAgeInt = Integer.parseInt(maxAge);
|
|
||||||
|
|
||||||
if (authTimeInt + maxAgeInt < Time.currentTime()) {
|
|
||||||
logger.debugf("Authentication time is expired in CookieAuthenticator. userSession=%s, clientId=%s, maxAge=%d, authTime=%d", userSession.getId(),
|
|
||||||
clientSession.getClient().getId(), maxAgeInt, authTimeInt);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ import javax.ws.rs.PUT;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -177,6 +178,27 @@ public class PolicyService {
|
||||||
return Response.ok(toRepresentation(model, authorization)).build();
|
return Response.ok(toRepresentation(model, authorization)).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Path("/search")
|
||||||
|
@GET
|
||||||
|
@Produces("application/json")
|
||||||
|
@NoCache
|
||||||
|
public Response find(@QueryParam("name") String name) {
|
||||||
|
this.auth.requireView();
|
||||||
|
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||||
|
|
||||||
|
if (name == null) {
|
||||||
|
return Response.status(Status.BAD_REQUEST).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
Policy model = storeFactory.getPolicyStore().findByName(name, this.resourceServer.getId());
|
||||||
|
|
||||||
|
if (model == null) {
|
||||||
|
return Response.status(Status.OK).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.ok(toRepresentation(model, authorization)).build();
|
||||||
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces("application/json")
|
@Produces("application/json")
|
||||||
@NoCache
|
@NoCache
|
||||||
|
|
|
@ -448,7 +448,7 @@ public class ResourceServerService {
|
||||||
private PolicyRepresentation createDefaultPolicy() {
|
private PolicyRepresentation createDefaultPolicy() {
|
||||||
PolicyRepresentation defaultPolicy = new PolicyRepresentation();
|
PolicyRepresentation defaultPolicy = new PolicyRepresentation();
|
||||||
|
|
||||||
defaultPolicy.setName("Only From Realm Policy");
|
defaultPolicy.setName("Default Policy");
|
||||||
defaultPolicy.setDescription("A policy that grants access only for users within this realm");
|
defaultPolicy.setDescription("A policy that grants access only for users within this realm");
|
||||||
defaultPolicy.setType("js");
|
defaultPolicy.setType("js");
|
||||||
defaultPolicy.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
|
defaultPolicy.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
|
||||||
|
@ -456,21 +456,7 @@ public class ResourceServerService {
|
||||||
|
|
||||||
HashMap<String, String> defaultPolicyConfig = new HashMap<>();
|
HashMap<String, String> defaultPolicyConfig = new HashMap<>();
|
||||||
|
|
||||||
defaultPolicyConfig.put("code", "var context = $evaluation.getContext();\n" +
|
defaultPolicyConfig.put("code", "// by default, grants any permission associated with this policy\n$evaluation.grant();\n");
|
||||||
"\n" +
|
|
||||||
"// using attributes from the evaluation context to obtain the realm\n" +
|
|
||||||
"var contextAttributes = context.getAttributes();\n" +
|
|
||||||
"var realmName = contextAttributes.getValue('kc.realm.name').asString(0);\n" +
|
|
||||||
"\n" +
|
|
||||||
"// using attributes from the identity to obtain the issuer\n" +
|
|
||||||
"var identity = context.getIdentity();\n" +
|
|
||||||
"var identityAttributes = identity.getAttributes();\n" +
|
|
||||||
"var issuer = identityAttributes.getValue('iss').asString(0);\n" +
|
|
||||||
"\n" +
|
|
||||||
"// only users from the realm have access granted \n" +
|
|
||||||
"if (issuer.endsWith(realmName)) {\n" +
|
|
||||||
" $evaluation.grant();\n" +
|
|
||||||
"}");
|
|
||||||
|
|
||||||
defaultPolicy.setConfig(defaultPolicyConfig);
|
defaultPolicy.setConfig(defaultPolicyConfig);
|
||||||
|
|
||||||
|
|
|
@ -39,11 +39,14 @@ import javax.ws.rs.PUT;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.keycloak.authorization.admin.util.Models.toRepresentation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
*/
|
*/
|
||||||
|
@ -148,7 +151,28 @@ public class ResourceSetService {
|
||||||
return Response.status(Status.NOT_FOUND).build();
|
return Response.status(Status.NOT_FOUND).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response.ok(Models.toRepresentation(model, this.resourceServer, authorization)).build();
|
return Response.ok(toRepresentation(model, this.resourceServer, authorization)).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("/search")
|
||||||
|
@GET
|
||||||
|
@Produces("application/json")
|
||||||
|
@NoCache
|
||||||
|
public Response find(@QueryParam("name") String name) {
|
||||||
|
this.auth.requireView();
|
||||||
|
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||||
|
|
||||||
|
if (name == null) {
|
||||||
|
return Response.status(Status.BAD_REQUEST).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
Resource model = storeFactory.getResourceStore().findByName(name, this.resourceServer.getId());
|
||||||
|
|
||||||
|
if (model == null) {
|
||||||
|
return Response.status(Status.OK).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.ok(toRepresentation(model, this.resourceServer, authorization)).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
@ -160,7 +184,7 @@ public class ResourceSetService {
|
||||||
|
|
||||||
return Response.ok(
|
return Response.ok(
|
||||||
storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream()
|
storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream()
|
||||||
.map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization))
|
.map(resource -> toRepresentation(resource, this.resourceServer, authorization))
|
||||||
.collect(Collectors.toList()))
|
.collect(Collectors.toList()))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.authorization.admin;
|
package org.keycloak.authorization.admin;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.model.Policy;
|
import org.keycloak.authorization.model.Policy;
|
||||||
import org.keycloak.authorization.model.Resource;
|
import org.keycloak.authorization.model.Resource;
|
||||||
|
@ -36,6 +37,7 @@ import javax.ws.rs.PUT;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -134,6 +136,27 @@ public class ScopeService {
|
||||||
return Response.ok(toRepresentation(model, this.authorization)).build();
|
return Response.ok(toRepresentation(model, this.authorization)).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Path("/search")
|
||||||
|
@GET
|
||||||
|
@Produces("application/json")
|
||||||
|
@NoCache
|
||||||
|
public Response find(@QueryParam("name") String name) {
|
||||||
|
this.auth.requireView();
|
||||||
|
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||||
|
|
||||||
|
if (name == null) {
|
||||||
|
return Response.status(Status.BAD_REQUEST).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
Scope model = storeFactory.getScopeStore().findByName(name, this.resourceServer.getId());
|
||||||
|
|
||||||
|
if (model == null) {
|
||||||
|
return Response.status(Status.OK).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.ok(toRepresentation(model, authorization)).build();
|
||||||
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces("application/json")
|
@Produces("application/json")
|
||||||
public Response findAll() {
|
public Response findAll() {
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.keycloak.protocol.oidc;
|
||||||
|
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.OAuthErrorException;
|
import org.keycloak.OAuthErrorException;
|
||||||
|
import org.keycloak.common.util.Time;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
|
@ -33,8 +34,10 @@ import org.keycloak.protocol.oidc.utils.OIDCResponseMode;
|
||||||
import org.keycloak.protocol.oidc.utils.OIDCResponseType;
|
import org.keycloak.protocol.oidc.utils.OIDCResponseType;
|
||||||
import org.keycloak.representations.AccessTokenResponse;
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
import org.keycloak.services.ServicesLogger;
|
import org.keycloak.services.ServicesLogger;
|
||||||
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
import org.keycloak.services.managers.ClientSessionCode;
|
import org.keycloak.services.managers.ClientSessionCode;
|
||||||
import org.keycloak.services.managers.ResourceAdminManager;
|
import org.keycloak.services.managers.ResourceAdminManager;
|
||||||
|
import org.keycloak.util.TokenUtil;
|
||||||
|
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
@ -57,9 +60,12 @@ public class OIDCLoginProtocol implements LoginProtocol {
|
||||||
public static final String REDIRECT_URI_PARAM = "redirect_uri";
|
public static final String REDIRECT_URI_PARAM = "redirect_uri";
|
||||||
public static final String CLIENT_ID_PARAM = "client_id";
|
public static final String CLIENT_ID_PARAM = "client_id";
|
||||||
public static final String NONCE_PARAM = "nonce";
|
public static final String NONCE_PARAM = "nonce";
|
||||||
public static final String MAX_AGE_PARAM = "max_age";
|
public static final String MAX_AGE_PARAM = OAuth2Constants.MAX_AGE;
|
||||||
public static final String PROMPT_PARAM = "prompt";
|
public static final String PROMPT_PARAM = OAuth2Constants.PROMPT;
|
||||||
public static final String LOGIN_HINT_PARAM = "login_hint";
|
public static final String LOGIN_HINT_PARAM = "login_hint";
|
||||||
|
public static final String REQUEST_PARAM = "request";
|
||||||
|
public static final String REQUEST_URI_PARAM = "request_uri";
|
||||||
|
|
||||||
public static final String LOGOUT_REDIRECT_URI = "OIDC_LOGOUT_REDIRECT_URI";
|
public static final String LOGOUT_REDIRECT_URI = "OIDC_LOGOUT_REDIRECT_URI";
|
||||||
public static final String ISSUER = "iss";
|
public static final String ISSUER = "iss";
|
||||||
|
|
||||||
|
@ -242,6 +248,36 @@ public class OIDCLoginProtocol implements LoginProtocol {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requireReauthentication(UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||||
|
return isPromptLogin(clientSession) || isAuthTimeExpired(userSession, clientSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isPromptLogin(ClientSessionModel clientSession) {
|
||||||
|
String prompt = clientSession.getNote(OIDCLoginProtocol.PROMPT_PARAM);
|
||||||
|
return TokenUtil.hasPrompt(prompt, OIDCLoginProtocol.PROMPT_VALUE_LOGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isAuthTimeExpired(UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||||
|
String authTime = userSession.getNote(AuthenticationManager.AUTH_TIME);
|
||||||
|
String maxAge = clientSession.getNote(OIDCLoginProtocol.MAX_AGE_PARAM);
|
||||||
|
if (maxAge == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int authTimeInt = authTime==null ? 0 : Integer.parseInt(authTime);
|
||||||
|
int maxAgeInt = Integer.parseInt(maxAge);
|
||||||
|
|
||||||
|
if (authTimeInt + maxAgeInt < Time.currentTime()) {
|
||||||
|
logger.debugf("Authentication time is expired, needs to reauthenticate. userSession=%s, clientId=%s, maxAge=%d, authTime=%d", userSession.getId(),
|
||||||
|
clientSession.getClient().getId(), maxAgeInt, authTimeInt);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
||||||
|
|
|
@ -102,6 +102,9 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
|
||||||
|
|
||||||
config.setScopesSupported(SCOPES_SUPPORTED);
|
config.setScopesSupported(SCOPES_SUPPORTED);
|
||||||
|
|
||||||
|
config.setRequestParameterSupported(false);
|
||||||
|
config.setRequestUriParameterSupported(false);
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
|
||||||
|
|
||||||
private Action action;
|
private Action action;
|
||||||
private OIDCResponseType parsedResponseType;
|
private OIDCResponseType parsedResponseType;
|
||||||
|
private OIDCResponseMode parsedResponseMode;
|
||||||
|
|
||||||
private String clientId;
|
private String clientId;
|
||||||
private String redirectUri;
|
private String redirectUri;
|
||||||
|
@ -162,6 +163,11 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
|
||||||
logger.oidcScopeMissing();
|
logger.oidcScopeMissing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errorResponse = checkOIDCParams(params);
|
||||||
|
if (errorResponse != null) {
|
||||||
|
return errorResponse;
|
||||||
|
}
|
||||||
|
|
||||||
createClientSession();
|
createClientSession();
|
||||||
// So back button doesn't work
|
// So back button doesn't work
|
||||||
CacheControlUtil.noBackButtonCacheControlHeader();
|
CacheControlUtil.noBackButtonCacheControlHeader();
|
||||||
|
@ -313,6 +319,24 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
|
||||||
return redirectErrorToClient(parsedResponseMode, OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE, "Client is not allowed to initiate browser login with given response_type. Implicit flow is disabled for the client.");
|
return redirectErrorToClient(parsedResponseMode, OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE, "Client is not allowed to initiate browser login with given response_type. Implicit flow is disabled for the client.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.parsedResponseMode = parsedResponseMode;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Response checkOIDCParams(MultivaluedMap<String, String> params) {
|
||||||
|
if (params.getFirst(OIDCLoginProtocol.REQUEST_PARAM) != null) {
|
||||||
|
logger.unsupportedParameter(OIDCLoginProtocol.REQUEST_PARAM);
|
||||||
|
event.error(Errors.INVALID_REQUEST);
|
||||||
|
return redirectErrorToClient(parsedResponseMode, OAuthErrorException.REQUEST_NOT_SUPPORTED, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.getFirst(OIDCLoginProtocol.REQUEST_URI_PARAM) != null) {
|
||||||
|
logger.unsupportedParameter(OIDCLoginProtocol.REQUEST_URI_PARAM);
|
||||||
|
event.error(Errors.INVALID_REQUEST);
|
||||||
|
return redirectErrorToClient(parsedResponseMode, OAuthErrorException.REQUEST_URI_NOT_SUPPORTED, null);
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,8 @@ public class KeycloakOIDCClientInstallation implements ClientInstallationProvide
|
||||||
PolicyEnforcerConfig enforcerConfig = new PolicyEnforcerConfig();
|
PolicyEnforcerConfig enforcerConfig = new PolicyEnforcerConfig();
|
||||||
|
|
||||||
enforcerConfig.setEnforcementMode(null);
|
enforcerConfig.setEnforcementMode(null);
|
||||||
enforcerConfig.setPaths(null);
|
enforcerConfig.setCreateResources(null);
|
||||||
|
enforcerConfig.setOnlineIntrospection(null);
|
||||||
|
|
||||||
rep.setEnforcerConfig(enforcerConfig);
|
rep.setEnforcerConfig(enforcerConfig);
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,12 @@ public class OIDCConfigurationRepresentation {
|
||||||
@JsonProperty("scopes_supported")
|
@JsonProperty("scopes_supported")
|
||||||
private List<String> scopesSupported;
|
private List<String> scopesSupported;
|
||||||
|
|
||||||
|
@JsonProperty("request_parameter_supported")
|
||||||
|
private Boolean requestParameterSupported;
|
||||||
|
|
||||||
|
@JsonProperty("request_uri_parameter_supported")
|
||||||
|
private Boolean requestUriParameterSupported;
|
||||||
|
|
||||||
protected Map<String, Object> otherClaims = new HashMap<String, Object>();
|
protected Map<String, Object> otherClaims = new HashMap<String, Object>();
|
||||||
|
|
||||||
public String getIssuer() {
|
public String getIssuer() {
|
||||||
|
@ -242,6 +248,22 @@ public class OIDCConfigurationRepresentation {
|
||||||
this.scopesSupported = scopesSupported;
|
this.scopesSupported = scopesSupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getRequestParameterSupported() {
|
||||||
|
return requestParameterSupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequestParameterSupported(Boolean requestParameterSupported) {
|
||||||
|
this.requestParameterSupported = requestParameterSupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getRequestUriParameterSupported() {
|
||||||
|
return requestUriParameterSupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequestUriParameterSupported(Boolean requestUriParameterSupported) {
|
||||||
|
this.requestUriParameterSupported = requestUriParameterSupported;
|
||||||
|
}
|
||||||
|
|
||||||
@JsonAnyGetter
|
@JsonAnyGetter
|
||||||
public Map<String, Object> getOtherClaims() {
|
public Map<String, Object> getOtherClaims() {
|
||||||
return otherClaims;
|
return otherClaims;
|
||||||
|
|
|
@ -629,6 +629,12 @@ public class SamlProtocol implements LoginProtocol {
|
||||||
return logoutBuilder;
|
return logoutBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requireReauthentication(UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||||
|
// Not yet supported
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private JaxrsSAML2BindingBuilder createBindingBuilder(SamlClient samlClient) {
|
private JaxrsSAML2BindingBuilder createBindingBuilder(SamlClient samlClient) {
|
||||||
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder();
|
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder();
|
||||||
if (samlClient.requiresRealmSignature()) {
|
if (samlClient.requiresRealmSignature()) {
|
||||||
|
|
|
@ -419,6 +419,10 @@ public interface ServicesLogger extends BasicLogger {
|
||||||
void invalidParameter(String paramName);
|
void invalidParameter(String paramName);
|
||||||
|
|
||||||
@LogMessage(level = ERROR)
|
@LogMessage(level = ERROR)
|
||||||
@Message(id=94, value="Client is not allowed to initiate browser login with given response_type. %s flow is disabled for the client.")
|
@Message(id=94, value="Unsupported parameter: %s")
|
||||||
|
void unsupportedParameter(String paramName);
|
||||||
|
|
||||||
|
@LogMessage(level = ERROR)
|
||||||
|
@Message(id=95, value="Client is not allowed to initiate browser login with given response_type. %s flow is disabled for the client.")
|
||||||
void flowNotAllowed(String flowName);
|
void flowNotAllowed(String flowName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,11 @@ public abstract class AbstractPage {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AbstractPage removeUriParameter(String name) {
|
||||||
|
uriParameters.remove(name);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Object getUriParameter(String name) {
|
public Object getUriParameter(String name) {
|
||||||
return uriParameters.get(name);
|
return uriParameters.get(name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,9 @@ import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.common.Version;
|
import org.keycloak.common.Version;
|
||||||
|
import org.keycloak.common.util.Time;
|
||||||
import org.keycloak.constants.AdapterConstants;
|
import org.keycloak.constants.AdapterConstants;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.VersionRepresentation;
|
import org.keycloak.representations.VersionRepresentation;
|
||||||
|
@ -33,6 +35,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
|
import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
|
||||||
import org.keycloak.testsuite.adapter.filter.AdapterActionsFilter;
|
import org.keycloak.testsuite.adapter.filter.AdapterActionsFilter;
|
||||||
import org.keycloak.testsuite.adapter.page.*;
|
import org.keycloak.testsuite.adapter.page.*;
|
||||||
|
import org.keycloak.testsuite.util.URLUtils;
|
||||||
import org.keycloak.util.BasicAuthHelper;
|
import org.keycloak.util.BasicAuthHelper;
|
||||||
|
|
||||||
import javax.ws.rs.client.Client;
|
import javax.ws.rs.client.Client;
|
||||||
|
@ -448,5 +451,32 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd
|
||||||
setAdapterAndServerTimeOffset(0, tokenMinTTLPage.toString());
|
setAdapterAndServerTimeOffset(0, tokenMinTTLPage.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests forwarding of parameters like "prompt"
|
||||||
|
@Test
|
||||||
|
public void testOIDCParamsForwarding() {
|
||||||
|
// test login to customer-portal which does a bearer request to customer-db
|
||||||
|
securePortal.navigateTo();
|
||||||
|
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
|
||||||
|
testRealmLoginPage.form().login("bburke@redhat.com", "password");
|
||||||
|
assertCurrentUrlEquals(securePortal);
|
||||||
|
String pageSource = driver.getPageSource();
|
||||||
|
assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
|
||||||
|
|
||||||
|
int currentTime = Time.currentTime();
|
||||||
|
setAdapterAndServerTimeOffset(10, securePortal.toString());
|
||||||
|
|
||||||
|
// Test I need to reauthenticate with prompt=login
|
||||||
|
String appUri = tokenMinTTLPage.getUriBuilder().queryParam(OIDCLoginProtocol.PROMPT_PARAM, OIDCLoginProtocol.PROMPT_VALUE_LOGIN).build().toString();
|
||||||
|
URLUtils.navigateToUri(driver, appUri, true);
|
||||||
|
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
|
||||||
|
testRealmLoginPage.form().login("bburke@redhat.com", "password");
|
||||||
|
AccessToken token = tokenMinTTLPage.getAccessToken();
|
||||||
|
int authTime = token.getAuthTime();
|
||||||
|
Assert.assertTrue(currentTime + 10 <= authTime);
|
||||||
|
|
||||||
|
// Revert times
|
||||||
|
setAdapterAndServerTimeOffset(0, tokenMinTTLPage.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -289,6 +289,78 @@ public class OIDCAdvancedRequestParamsTest extends TestRealmKeycloakTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// prompt=consent
|
// NONCE
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nonceNotUsed() {
|
||||||
|
driver.navigate().to(oauth.getLoginFormUrl());
|
||||||
|
|
||||||
|
loginPage.assertCurrent();
|
||||||
|
loginPage.login("test-user@localhost", "password");
|
||||||
|
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
|
EventRepresentation loginEvent = events.expectLogin().detail(Details.USERNAME, "test-user@localhost").assertEvent();
|
||||||
|
IDToken idToken = sendTokenRequestAndGetIDToken(loginEvent);
|
||||||
|
|
||||||
|
Assert.assertNull(idToken.getNonce());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nonceMatches() {
|
||||||
|
driver.navigate().to(oauth.getLoginFormUrl() + "&nonce=abcdef123456");
|
||||||
|
|
||||||
|
loginPage.assertCurrent();
|
||||||
|
loginPage.login("test-user@localhost", "password");
|
||||||
|
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
|
EventRepresentation loginEvent = events.expectLogin().detail(Details.USERNAME, "test-user@localhost").assertEvent();
|
||||||
|
IDToken idToken = sendTokenRequestAndGetIDToken(loginEvent);
|
||||||
|
|
||||||
|
Assert.assertEquals("abcdef123456", idToken.getNonce());
|
||||||
|
}
|
||||||
|
|
||||||
|
// DISPLAY & OTHERS
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nonSupportedParams() {
|
||||||
|
driver.navigate().to(oauth.getLoginFormUrl() + "&display=popup&foo=foobar");
|
||||||
|
|
||||||
|
loginPage.assertCurrent();
|
||||||
|
loginPage.login("test-user@localhost", "password");
|
||||||
|
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
|
EventRepresentation loginEvent = events.expectLogin().detail(Details.USERNAME, "test-user@localhost").assertEvent();
|
||||||
|
IDToken idToken = sendTokenRequestAndGetIDToken(loginEvent);
|
||||||
|
|
||||||
|
Assert.assertNotNull(idToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// REQUEST & REQUEST_URI
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestParam() {
|
||||||
|
driver.navigate().to(oauth.getLoginFormUrl() + "&request=abc");
|
||||||
|
|
||||||
|
assertFalse(loginPage.isCurrent());
|
||||||
|
assertTrue(appPage.isCurrent());
|
||||||
|
|
||||||
|
// Assert error response was sent because not logged in
|
||||||
|
OAuthClient.AuthorizationCodeResponse resp = new OAuthClient.AuthorizationCodeResponse(oauth);
|
||||||
|
Assert.assertNull(resp.getCode());
|
||||||
|
Assert.assertEquals(OAuthErrorException.REQUEST_NOT_SUPPORTED, resp.getError());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestUriParam() {
|
||||||
|
driver.navigate().to(oauth.getLoginFormUrl() + "&request_uri=https%3A%2F%2Flocalhost%3A60784%2Fexport%2FqzHTG11W48.jwt");
|
||||||
|
|
||||||
|
assertFalse(loginPage.isCurrent());
|
||||||
|
assertTrue(appPage.isCurrent());
|
||||||
|
|
||||||
|
// Assert error response was sent because not logged in
|
||||||
|
OAuthClient.AuthorizationCodeResponse resp = new OAuthClient.AuthorizationCodeResponse(oauth);
|
||||||
|
Assert.assertNull(resp.getCode());
|
||||||
|
Assert.assertEquals(OAuthErrorException.REQUEST_URI_NOT_SUPPORTED, resp.getError());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,10 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
// Scopes supported
|
// Scopes supported
|
||||||
Assert.assertNames(oidcConfig.getScopesSupported(), OAuth2Constants.SCOPE_OPENID, OAuth2Constants.OFFLINE_ACCESS);
|
Assert.assertNames(oidcConfig.getScopesSupported(), OAuth2Constants.SCOPE_OPENID, OAuth2Constants.OFFLINE_ACCESS);
|
||||||
|
|
||||||
|
// Request and Request_Uri
|
||||||
|
Assert.assertFalse(oidcConfig.getRequestParameterSupported());
|
||||||
|
Assert.assertFalse(oidcConfig.getRequestUriParameterSupported());
|
||||||
} finally {
|
} finally {
|
||||||
client.close();
|
client.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,8 @@ module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $r
|
||||||
$scope.scopes = data;
|
$scope.scopes = data;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var $instance = this;
|
||||||
|
|
||||||
ResourceServer.get({
|
ResourceServer.get({
|
||||||
realm : $route.current.params.realm,
|
realm : $route.current.params.realm,
|
||||||
client : client.id
|
client : client.id
|
||||||
|
@ -131,9 +133,11 @@ module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $r
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
$scope.save = function() {
|
$scope.save = function() {
|
||||||
ResourceServerResource.save({realm : realm.realm, client : $scope.client.id}, $scope.resource, function(data) {
|
$instance.checkNameAvailability(function () {
|
||||||
$location.url("/realms/" + realm.realm + "/clients/" + $scope.client.id + "/authz/resource-server/resource/" + data._id);
|
ResourceServerResource.save({realm : realm.realm, client : $scope.client.id}, $scope.resource, function(data) {
|
||||||
Notifications.success("The resource has been created.");
|
$location.url("/realms/" + realm.realm + "/clients/" + $scope.client.id + "/authz/resource-server/resource/" + data._id);
|
||||||
|
Notifications.success("The resource has been created.");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,6 +157,10 @@ module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $r
|
||||||
$scope.resource.scopes[i] = $scope.resource.scopes[i].name;
|
$scope.resource.scopes[i] = $scope.resource.scopes[i].name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data = angular.copy($scope.resource);
|
||||||
|
|
||||||
|
$scope.originalResource = data;
|
||||||
|
|
||||||
$scope.$watch('resource', function() {
|
$scope.$watch('resource', function() {
|
||||||
if (!angular.equals($scope.resource, data)) {
|
if (!angular.equals($scope.resource, data)) {
|
||||||
$scope.changed = true;
|
$scope.changed = true;
|
||||||
|
@ -160,9 +168,11 @@ module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $r
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
$scope.save = function() {
|
$scope.save = function() {
|
||||||
ResourceServerResource.update({realm : realm.realm, client : $scope.client.id, rsrid : $scope.resource._id}, $scope.resource, function() {
|
$instance.checkNameAvailability(function () {
|
||||||
$route.reload();
|
ResourceServerResource.update({realm : realm.realm, client : $scope.client.id, rsrid : $scope.resource._id}, $scope.resource, function() {
|
||||||
Notifications.success("The resource has been updated.");
|
$route.reload();
|
||||||
|
Notifications.success("The resource has been updated.");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,12 +198,30 @@ module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $r
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.reset = function() {
|
$scope.reset = function() {
|
||||||
$scope.resource = angular.copy(data);
|
$route.reload();
|
||||||
$scope.changed = false;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.checkNewNameAvailability = function () {
|
||||||
|
$instance.checkNameAvailability(function () {});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checkNameAvailability = function (onSuccess) {
|
||||||
|
ResourceServerResource.search({
|
||||||
|
realm : $route.current.params.realm,
|
||||||
|
client : client.id,
|
||||||
|
rsrid : $route.current.params.rsrid,
|
||||||
|
name: $scope.resource.name
|
||||||
|
}, function(data) {
|
||||||
|
if (data && data._id && data._id != $scope.resource._id) {
|
||||||
|
Notifications.error("Name already in use by another resource, please choose another one.");
|
||||||
|
} else {
|
||||||
|
onSuccess();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('ResourceServerScopeCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerScope, client) {
|
module.controller('ResourceServerScopeCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerScope, client) {
|
||||||
|
@ -216,6 +244,8 @@ module.controller('ResourceServerScopeDetailCtrl', function($scope, $http, $rout
|
||||||
$scope.realm = realm;
|
$scope.realm = realm;
|
||||||
$scope.client = client;
|
$scope.client = client;
|
||||||
|
|
||||||
|
var $instance = this;
|
||||||
|
|
||||||
ResourceServer.get({
|
ResourceServer.get({
|
||||||
realm : $route.current.params.realm,
|
realm : $route.current.params.realm,
|
||||||
client : client.id
|
client : client.id
|
||||||
|
@ -230,7 +260,7 @@ module.controller('ResourceServerScopeDetailCtrl', function($scope, $http, $rout
|
||||||
|
|
||||||
var scope = {};
|
var scope = {};
|
||||||
|
|
||||||
$scope.resource = angular.copy(scope);
|
$scope.scope = angular.copy(scope);
|
||||||
|
|
||||||
$scope.$watch('scope', function() {
|
$scope.$watch('scope', function() {
|
||||||
if (!angular.equals($scope.scope, scope)) {
|
if (!angular.equals($scope.scope, scope)) {
|
||||||
|
@ -239,9 +269,11 @@ module.controller('ResourceServerScopeDetailCtrl', function($scope, $http, $rout
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
$scope.save = function() {
|
$scope.save = function() {
|
||||||
ResourceServerScope.save({realm : realm.realm, client : $scope.client.id}, $scope.scope, function(data) {
|
$instance.checkNameAvailability(function () {
|
||||||
$location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/scope/" + data.id);
|
ResourceServerScope.save({realm : realm.realm, client : $scope.client.id}, $scope.scope, function(data) {
|
||||||
Notifications.success("The scope has been created.");
|
$location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/scope/" + data.id);
|
||||||
|
Notifications.success("The scope has been created.");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -259,10 +291,14 @@ module.controller('ResourceServerScopeDetailCtrl', function($scope, $http, $rout
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
|
$scope.originalScope = angular.copy($scope.scope);
|
||||||
|
|
||||||
$scope.save = function() {
|
$scope.save = function() {
|
||||||
ResourceServerScope.update({realm : realm.realm, client : $scope.client.id, id : $scope.scope.id}, $scope.scope, function() {
|
$instance.checkNameAvailability(function () {
|
||||||
$scope.changed = false;
|
ResourceServerScope.update({realm : realm.realm, client : $scope.client.id, id : $scope.scope.id}, $scope.scope, function() {
|
||||||
Notifications.success("The scope has been updated.");
|
$scope.changed = false;
|
||||||
|
Notifications.success("The scope has been updated.");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,12 +324,29 @@ module.controller('ResourceServerScopeDetailCtrl', function($scope, $http, $rout
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.reset = function() {
|
$scope.reset = function() {
|
||||||
$scope.scope = angular.copy(data);
|
$route.reload();
|
||||||
$scope.changed = false;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.checkNewNameAvailability = function () {
|
||||||
|
$instance.checkNameAvailability(function () {});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checkNameAvailability = function (onSuccess) {
|
||||||
|
ResourceServerScope.search({
|
||||||
|
realm : $route.current.params.realm,
|
||||||
|
client : client.id,
|
||||||
|
name: $scope.scope.name
|
||||||
|
}, function(data) {
|
||||||
|
if (data && data.id && data.id != $scope.scope.id) {
|
||||||
|
Notifications.error("Name already in use by another scope, please choose another one.");
|
||||||
|
} else {
|
||||||
|
onSuccess();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPolicy, PolicyProvider, client) {
|
module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPolicy, PolicyProvider, client) {
|
||||||
|
@ -845,6 +898,8 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe
|
||||||
|
|
||||||
delegate.onInit();
|
delegate.onInit();
|
||||||
|
|
||||||
|
var $instance = this;
|
||||||
|
|
||||||
ResourceServer.get({
|
ResourceServer.get({
|
||||||
realm : $route.current.params.realm,
|
realm : $route.current.params.realm,
|
||||||
client : client.id
|
client : client.id
|
||||||
|
@ -876,17 +931,19 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
$scope.save = function() {
|
$scope.save = function() {
|
||||||
if (delegate.onCreate) {
|
$instance.checkNameAvailability(function () {
|
||||||
delegate.onCreate();
|
if (delegate.onCreate) {
|
||||||
}
|
delegate.onCreate();
|
||||||
ResourceServerPolicy.save({realm : realm.realm, client : client.id}, $scope.policy, function(data) {
|
|
||||||
if (delegate.isPermission()) {
|
|
||||||
$location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/permission/" + $scope.policy.type + "/" + data.id);
|
|
||||||
Notifications.success("The permission has been created.");
|
|
||||||
} else {
|
|
||||||
$location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/policy/" + $scope.policy.type + "/" + data.id);
|
|
||||||
Notifications.success("The policy has been created.");
|
|
||||||
}
|
}
|
||||||
|
ResourceServerPolicy.save({realm : realm.realm, client : client.id}, $scope.policy, function(data) {
|
||||||
|
if (delegate.isPermission()) {
|
||||||
|
$location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/permission/" + $scope.policy.type + "/" + data.id);
|
||||||
|
Notifications.success("The permission has been created.");
|
||||||
|
} else {
|
||||||
|
$location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/policy/" + $scope.policy.type + "/" + data.id);
|
||||||
|
Notifications.success("The policy has been created.");
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -903,6 +960,7 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe
|
||||||
client : client.id,
|
client : client.id,
|
||||||
id : $route.current.params.id,
|
id : $route.current.params.id,
|
||||||
}, function(data) {
|
}, function(data) {
|
||||||
|
$scope.originalPolicy = data;
|
||||||
var policy = angular.copy(data);
|
var policy = angular.copy(data);
|
||||||
|
|
||||||
if (delegate.onInitUpdate) {
|
if (delegate.onInitUpdate) {
|
||||||
|
@ -919,16 +977,18 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
$scope.save = function() {
|
$scope.save = function() {
|
||||||
if (delegate.onUpdate) {
|
$instance.checkNameAvailability(function () {
|
||||||
delegate.onUpdate();
|
if (delegate.onUpdate) {
|
||||||
}
|
delegate.onUpdate();
|
||||||
ResourceServerPolicy.update({realm : realm.realm, client : client.id, id : $scope.policy.id}, $scope.policy, function() {
|
|
||||||
$route.reload();
|
|
||||||
if (delegate.isPermission()) {
|
|
||||||
Notifications.success("The permission has been updated.");
|
|
||||||
} else {
|
|
||||||
Notifications.success("The policy has been updated.");
|
|
||||||
}
|
}
|
||||||
|
ResourceServerPolicy.update({realm : realm.realm, client : client.id, id : $scope.policy.id}, $scope.policy, function() {
|
||||||
|
$route.reload();
|
||||||
|
if (delegate.isPermission()) {
|
||||||
|
Notifications.success("The permission has been updated.");
|
||||||
|
} else {
|
||||||
|
Notifications.success("The policy has been updated.");
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -971,11 +1031,31 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.checkNewNameAvailability = function () {
|
||||||
|
$instance.checkNameAvailability(function () {});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checkNameAvailability = function (onSuccess) {
|
||||||
|
ResourceServerPolicy.search({
|
||||||
|
realm: $route.current.params.realm,
|
||||||
|
client: client.id,
|
||||||
|
name: $scope.policy.name
|
||||||
|
}, function(data) {
|
||||||
|
if (data && data.id && data.id != $scope.policy.id) {
|
||||||
|
Notifications.error("Name already in use by another policy or permission, please choose another one.");
|
||||||
|
} else {
|
||||||
|
onSuccess();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PolicyController;
|
return PolicyController;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $location, realm, clients, roles, ResourceServer, client, ResourceServerResource, ResourceServerScope, User, Notifications) {
|
module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $location, realm, clients, roles, ResourceServer, client, ResourceServerResource, ResourceServerScope, User, Notifications) {
|
||||||
$scope.realm = realm;
|
$scope.realm = realm;
|
||||||
$scope.client = client;
|
$scope.client = client;
|
||||||
|
|
|
@ -15,7 +15,8 @@ module.factory('ResourceServerResource', function($resource) {
|
||||||
client: '@client',
|
client: '@client',
|
||||||
rsrid : '@rsrid'
|
rsrid : '@rsrid'
|
||||||
}, {
|
}, {
|
||||||
'update' : {method : 'PUT'}
|
'update' : {method : 'PUT'},
|
||||||
|
'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/resource/search', method : 'GET'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -25,7 +26,8 @@ module.factory('ResourceServerScope', function($resource) {
|
||||||
client: '@client',
|
client: '@client',
|
||||||
id : '@id'
|
id : '@id'
|
||||||
}, {
|
}, {
|
||||||
'update' : {method : 'PUT'}
|
'update' : {method : 'PUT'},
|
||||||
|
'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/scope/search', method : 'GET'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -35,7 +37,8 @@ module.factory('ResourceServerPolicy', function($resource) {
|
||||||
client: '@client',
|
client: '@client',
|
||||||
id : '@id'
|
id : '@id'
|
||||||
}, {
|
}, {
|
||||||
'update' : {method : 'PUT'}
|
'update' : {method : 'PUT'},
|
||||||
|
'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/search', method : 'GET'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,18 +6,18 @@
|
||||||
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">{{:: 'authz-authorization' | translate}}</a></li>
|
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">{{:: 'authz-authorization' | translate}}</a></li>
|
||||||
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission">{{:: 'authz-permissions' | translate}}</a></li>
|
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission">{{:: 'authz-permissions' | translate}}</a></li>
|
||||||
<li data-ng-show="create">{{:: 'authz-add-resource-permission' | translate}}</li>
|
<li data-ng-show="create">{{:: 'authz-add-resource-permission' | translate}}</li>
|
||||||
<li data-ng-hide="create">{{policy.name}}</li>
|
<li data-ng-hide="create">{{originalPolicy.name}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h1 data-ng-show="create">{{:: 'authz-add-resource-permission' | translate}}</h1>
|
<h1 data-ng-show="create">{{:: 'authz-add-resource-permission' | translate}}</h1>
|
||||||
<h1 data-ng-hide="create">{{policy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-click="remove()"></i></h1>
|
<h1 data-ng-hide="create">{{originalPolicy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-click="remove()"></i></h1>
|
||||||
|
|
||||||
<form class="form-horizontal" name="clientForm" novalidate>
|
<form class="form-horizontal" name="clientForm" novalidate>
|
||||||
<fieldset class="border-top">
|
<fieldset class="border-top">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required>
|
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required data-ng-blur="checkNewNameAvailability()">
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'authz-permission-name.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'authz-permission-name.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,13 +83,9 @@
|
||||||
<input type="hidden" data-ng-model="policy.type"/>
|
<input type="hidden" data-ng-model="policy.type"/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" data-ng-show="access.manageAuthorization">
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="create">
|
<div class="col-md-10 col-md-offset-2">
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
||||||
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create">
|
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
|
||||||
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,18 +6,18 @@
|
||||||
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">{{:: 'authz-authorization' | translate}}</a></li>
|
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">{{:: 'authz-authorization' | translate}}</a></li>
|
||||||
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission">{{:: 'authz-permissions' | translate}}</a></li>
|
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission">{{:: 'authz-permissions' | translate}}</a></li>
|
||||||
<li data-ng-show="create">{{:: 'authz-add-scope-permission' | translate}}</li>
|
<li data-ng-show="create">{{:: 'authz-add-scope-permission' | translate}}</li>
|
||||||
<li data-ng-hide="create">{{policy.name}}</li>
|
<li data-ng-hide="create">{{originalPolicy.name}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h1 data-ng-show="create">{{:: 'authz-add-scope-permission' | translate}}</h1>
|
<h1 data-ng-show="create">{{:: 'authz-add-scope-permission' | translate}}</h1>
|
||||||
<h1 data-ng-hide="create">{{policy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-click="remove()"></i></h1>
|
<h1 data-ng-hide="create">{{originalPolicy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-click="remove()"></i></h1>
|
||||||
|
|
||||||
<form class="form-horizontal" name="clientForm" novalidate>
|
<form class="form-horizontal" name="clientForm" novalidate>
|
||||||
<fieldset class="border-top">
|
<fieldset class="border-top">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required>
|
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required data-ng-blur="checkNewNameAvailability()">
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'authz-permission-name.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'authz-permission-name.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -96,13 +96,9 @@
|
||||||
<input type="hidden" data-ng-model="policy.type"/>
|
<input type="hidden" data-ng-model="policy.type"/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" data-ng-show="access.manageAuthorization">
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="create">
|
<div class="col-md-10 col-md-offset-2">
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
||||||
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
|
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
|
||||||
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">{{:: 'authz-policies' | translate}}</a></li>
|
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">{{:: 'authz-policies' | translate}}</a></li>
|
||||||
<li data-ng-show="create">{{:: 'authz-add-aggregated-policy' | translate}}</li>
|
<li data-ng-show="create">{{:: 'authz-add-aggregated-policy' | translate}}</li>
|
||||||
<li data-ng-hide="create">{{:: 'authz-aggregated' | translate}}</li>
|
<li data-ng-hide="create">{{:: 'authz-aggregated' | translate}}</li>
|
||||||
<li data-ng-hide="create">{{policy.name}}</li>
|
<li data-ng-hide="create">{{originalPolicy.name}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h1 data-ng-show="create">{{:: 'authz-add-aggregated-policy' | translate}}</h1>
|
<h1 data-ng-show="create">{{:: 'authz-add-aggregated-policy' | translate}}</h1>
|
||||||
<h1 data-ng-hide="create">{{policy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
|
<h1 data-ng-hide="create">{{originalPolicy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
|
||||||
data-ng-click="remove()"></i></h1>
|
data-ng-click="remove()"></i></h1>
|
||||||
|
|
||||||
<form class="form-horizontal" name="clientForm" novalidate>
|
<form class="form-horizontal" name="clientForm" novalidate>
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required>
|
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required data-ng-blur="checkNewNameAvailability()">
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'authz-policy-name.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'authz-policy-name.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -72,13 +72,9 @@
|
||||||
<input type="hidden" data-ng-model="policy.type"/>
|
<input type="hidden" data-ng-model="policy.type"/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" data-ng-show="access.manageAuthorization">
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="create">
|
<div class="col-md-10 col-md-offset-2">
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
||||||
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
|
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
|
||||||
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">{{:: 'authz-policies' | translate}}</a></li>
|
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">{{:: 'authz-policies' | translate}}</a></li>
|
||||||
<li data-ng-show="create">{{:: 'authz-add-drools-policy' | translate}}</li>
|
<li data-ng-show="create">{{:: 'authz-add-drools-policy' | translate}}</li>
|
||||||
<li data-ng-hide="create">Drools</li>
|
<li data-ng-hide="create">Drools</li>
|
||||||
<li data-ng-hide="create">{{policy.name}}</li>
|
<li data-ng-hide="create">{{originalPolicy.name}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h1 data-ng-show="create">{{:: 'authz-add-drools-policy' | translate}}</h1>
|
<h1 data-ng-show="create">{{:: 'authz-add-drools-policy' | translate}}</h1>
|
||||||
<h1 data-ng-hide="create">{{policy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
|
<h1 data-ng-hide="create">{{originalPolicy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
|
||||||
data-ng-click="remove()"></i></h1>
|
data-ng-click="remove()"></i></h1>
|
||||||
|
|
||||||
<form class="form-horizontal" name="clientForm" novalidate>
|
<form class="form-horizontal" name="clientForm" novalidate>
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required>
|
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required data-ng-blur="checkNewNameAvailability()">
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'authz-policy-name.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'authz-policy-name.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -112,13 +112,9 @@
|
||||||
<input type="hidden" data-ng-model="policy.type"/>
|
<input type="hidden" data-ng-model="policy.type"/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" data-ng-show="access.manageAuthorization">
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="create">
|
<div class="col-md-10 col-md-offset-2">
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
||||||
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create">
|
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
|
||||||
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,18 +10,18 @@
|
||||||
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">{{:: 'authz-policies' | translate}}</a></li>
|
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">{{:: 'authz-policies' | translate}}</a></li>
|
||||||
<li data-ng-show="create">{{:: 'authz-add-js-policy' | translate}}</li>
|
<li data-ng-show="create">{{:: 'authz-add-js-policy' | translate}}</li>
|
||||||
<li data-ng-hide="create">JavaScript</li>
|
<li data-ng-hide="create">JavaScript</li>
|
||||||
<li data-ng-hide="create">{{policy.name}}</li>
|
<li data-ng-hide="create">{{originalPolicy.name}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h1 data-ng-show="create">{{:: 'authz-add-js-policy' | translate}}</h1>
|
<h1 data-ng-show="create">{{:: 'authz-add-js-policy' | translate}}</h1>
|
||||||
<h1 data-ng-hide="create">{{policy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-click="remove()"></i></h1>
|
<h1 data-ng-hide="create">{{originalPolicy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-click="remove()"></i></h1>
|
||||||
|
|
||||||
<form class="form-horizontal" name="clientForm" novalidate>
|
<form class="form-horizontal" name="clientForm" novalidate>
|
||||||
<fieldset class="border-top">
|
<fieldset class="border-top">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required>
|
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required data-ng-blur="checkNewNameAvailability()">
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'authz-policy-name.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'authz-policy-name.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,13 +55,9 @@
|
||||||
<input type="hidden" data-ng-model="policy.type"/>
|
<input type="hidden" data-ng-model="policy.type"/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" data-ng-show="access.manageAuthorization">
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="create">
|
<div class="col-md-10 col-md-offset-2">
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
||||||
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create">
|
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
|
||||||
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -25,11 +25,11 @@
|
||||||
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">{{:: 'authz-policies' | translate}}</a></li>
|
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">{{:: 'authz-policies' | translate}}</a></li>
|
||||||
<li data-ng-show="create">{{:: 'authz-add-role-policy' | translate}}</li>
|
<li data-ng-show="create">{{:: 'authz-add-role-policy' | translate}}</li>
|
||||||
<li data-ng-hide="create">{{:: 'roles' | translate}}</li>
|
<li data-ng-hide="create">{{:: 'roles' | translate}}</li>
|
||||||
<li data-ng-hide="create">{{policy.name}}</li>
|
<li data-ng-hide="create">{{originalPolicy.name}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h1 data-ng-show="create">{{:: 'authz-add-role-policy' | translate}}</h1>
|
<h1 data-ng-show="create">{{:: 'authz-add-role-policy' | translate}}</h1>
|
||||||
<h1 data-ng-hide="create">{{policy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
|
<h1 data-ng-hide="create">{{originalPolicy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
|
||||||
data-ng-click="remove()"></i></h1>
|
data-ng-click="remove()"></i></h1>
|
||||||
|
|
||||||
<form class="form-horizontal" name="clientForm" novalidate>
|
<form class="form-horizontal" name="clientForm" novalidate>
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required>
|
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required data-ng-blur="checkNewNameAvailability()">
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'authz-policy-name.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'authz-policy-name.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -99,13 +99,9 @@
|
||||||
<input type="hidden" data-ng-model="policy.type"/>
|
<input type="hidden" data-ng-model="policy.type"/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" data-ng-show="access.manageAuthorization">
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="create">
|
<div class="col-md-10 col-md-offset-2">
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
||||||
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
|
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
|
||||||
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,19 +10,19 @@
|
||||||
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">{{:: 'authz-policies' | translate}}</a></li>
|
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">{{:: 'authz-policies' | translate}}</a></li>
|
||||||
<li data-ng-show="create">{{:: 'authz-add-time-policy' | translate}}</li>
|
<li data-ng-show="create">{{:: 'authz-add-time-policy' | translate}}</li>
|
||||||
<li data-ng-hide="create">{{:: 'time' | translate}}</li>
|
<li data-ng-hide="create">{{:: 'time' | translate}}</li>
|
||||||
<li data-ng-hide="create">{{policy.name}}</li>
|
<li data-ng-hide="create">{{originalPolicy.name}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
|
||||||
<h1 data-ng-show="create">{{:: 'authz-add-time-policy' | translate}}</h1>
|
<h1 data-ng-show="create">{{:: 'authz-add-time-policy' | translate}}</h1>
|
||||||
<h1 data-ng-hide="create">{{policy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-click="remove()"></i></h1>
|
<h1 data-ng-hide="create">{{originalPolicy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-click="remove()"></i></h1>
|
||||||
|
|
||||||
<form class="form-horizontal" name="clientForm" novalidate>
|
<form class="form-horizontal" name="clientForm" novalidate>
|
||||||
<fieldset class="border-top">
|
<fieldset class="border-top">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required>
|
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required data-ng-blur="checkNewNameAvailability()">
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'authz-policy-name.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'authz-policy-name.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -65,13 +65,9 @@
|
||||||
<input type="hidden" data-ng-model="policy.type"/>
|
<input type="hidden" data-ng-model="policy.type"/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" data-ng-show="access.manageAuthorization">
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="create">
|
<div class="col-md-10 col-md-offset-2">
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
||||||
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create">
|
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
|
||||||
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">{{:: 'authz-policies' | translate}}</a></li>
|
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">{{:: 'authz-policies' | translate}}</a></li>
|
||||||
<li data-ng-show="create">{{:: 'authz-add-user-policy' | translate}}</li>
|
<li data-ng-show="create">{{:: 'authz-add-user-policy' | translate}}</li>
|
||||||
<li data-ng-hide="create">{{:: 'user' | translate}}</li>
|
<li data-ng-hide="create">{{:: 'user' | translate}}</li>
|
||||||
<li data-ng-hide="create">{{policy.name}}</li>
|
<li data-ng-hide="create">{{originalPolicy.name}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h1 data-ng-show="create">{{:: 'authz-add-user-policy' | translate}}</h1>
|
<h1 data-ng-show="create">{{:: 'authz-add-user-policy' | translate}}</h1>
|
||||||
<h1 data-ng-hide="create">{{policy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
|
<h1 data-ng-hide="create">{{originalPolicy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
|
||||||
data-ng-click="remove()"></i></h1>
|
data-ng-click="remove()"></i></h1>
|
||||||
|
|
||||||
<form class="form-horizontal" name="clientForm" novalidate>
|
<form class="form-horizontal" name="clientForm" novalidate>
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required>
|
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required data-ng-blur="checkNewNameAvailability()">
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'authz-policy-name.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'authz-policy-name.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -80,13 +80,9 @@
|
||||||
<input type="hidden" data-ng-model="policy.type"/>
|
<input type="hidden" data-ng-model="policy.type"/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" data-ng-show="access.manageAuthorization">
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="create">
|
<div class="col-md-10 col-md-offset-2">
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
||||||
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
|
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
|
||||||
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -43,13 +43,9 @@
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'authz-remote-resource-management.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'authz-remote-resource-management.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group" data-ng-show="access.manageAuthorization">
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="create">
|
<div class="col-md-10 col-md-offset-2">
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
||||||
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create">
|
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
|
||||||
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,11 +6,11 @@
|
||||||
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">{{:: 'authz-authorization' | translate}}</a></li>
|
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">{{:: 'authz-authorization' | translate}}</a></li>
|
||||||
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/resource">{{:: 'authz-resource' | translate}}</a></li>
|
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/resource">{{:: 'authz-resource' | translate}}</a></li>
|
||||||
<li data-ng-show="create">{{:: 'authz-add-resource' | translate}}</li>
|
<li data-ng-show="create">{{:: 'authz-add-resource' | translate}}</li>
|
||||||
<li data-ng-hide="create">{{resource.name}}</li>
|
<li data-ng-hide="create">{{originalResource.name}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h1 data-ng-show="create">{{:: 'authz-add-resource' | translate}}</h1>
|
<h1 data-ng-show="create">{{:: 'authz-add-resource' | translate}}</h1>
|
||||||
<h1 data-ng-hide="create">{{resource.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
|
<h1 data-ng-hide="create">{{originalResource.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
|
||||||
data-ng-click="remove()"></i></h1>
|
data-ng-click="remove()"></i></h1>
|
||||||
|
|
||||||
<form class="form-horizontal" name="clientForm" novalidate>
|
<form class="form-horizontal" name="clientForm" novalidate>
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required" data-ng-show="create">*</span></label>
|
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required" data-ng-show="create">*</span></label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input class="form-control" type="text" id="name" name="name" data-ng-model="resource.name" autofocus required>
|
<input class="form-control" type="text" id="name" name="name" data-ng-model="resource.name" autofocus required data-ng-blur="checkNewNameAvailability()">
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'authz-resource-name.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'authz-resource-name.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -63,13 +63,9 @@
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" data-ng-show="access.manageAuthorization">
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="create">
|
<div class="col-md-10 col-md-offset-2">
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
||||||
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create">
|
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
|
||||||
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,11 +6,11 @@
|
||||||
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">{{:: 'authz-authorization' | translate}}</a></li>
|
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">{{:: 'authz-authorization' | translate}}</a></li>
|
||||||
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope">{{:: 'authz-scope' | translate}}</a></li>
|
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope">{{:: 'authz-scope' | translate}}</a></li>
|
||||||
<li data-ng-show="create">{{:: 'authz-add-scope' | translate}}</li>
|
<li data-ng-show="create">{{:: 'authz-add-scope' | translate}}</li>
|
||||||
<li data-ng-hide="create">{{scope.name}}</li>
|
<li data-ng-hide="create">{{originalScope.name}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h1 data-ng-show="create">{{:: 'authz-add-scope' | translate}}</h1>
|
<h1 data-ng-show="create">{{:: 'authz-add-scope' | translate}}</h1>
|
||||||
<h1 data-ng-hide="create">{{scope.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
|
<h1 data-ng-hide="create">{{originalScope.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
|
||||||
data-ng-hide="changed" data-ng-click="remove()"></i></h1>
|
data-ng-hide="changed" data-ng-click="remove()"></i></h1>
|
||||||
|
|
||||||
<form class="form-horizontal" name="clientForm" novalidate>
|
<form class="form-horizontal" name="clientForm" novalidate>
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} </label>
|
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} </label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input class="form-control" type="text" id="name" name="name" data-ng-model="scope.name" autofocus>
|
<input class="form-control" type="text" id="name" name="name" data-ng-model="scope.name" autofocus data-ng-blur="checkNewNameAvailability()">
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'authz-scope-name.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'authz-scope-name.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,13 +31,9 @@
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" data-ng-show="access.manageAuthorization">
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="create">
|
<div class="col-md-10 col-md-offset-2">
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
||||||
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
|
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
|
||||||
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue