done 1st iteration
This commit is contained in:
parent
ddad1cb8af
commit
6b84b9b4b6
22 changed files with 1157 additions and 171 deletions
|
@ -67,6 +67,7 @@ public class ClientRepresentation {
|
|||
private Boolean useTemplateMappers;
|
||||
private ResourceServerRepresentation authorizationSettings;
|
||||
private Map<String, Boolean> access;
|
||||
protected String origin;
|
||||
|
||||
|
||||
public String getId() {
|
||||
|
@ -384,4 +385,19 @@ public class ClientRepresentation {
|
|||
public void setAccess(Map<String, Boolean> access) {
|
||||
this.access = access;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns id of ClientStorageProvider that loaded this user
|
||||
*
|
||||
* @return NULL if user stored locally
|
||||
*/
|
||||
public String getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public void setOrigin(String origin) {
|
||||
this.origin = origin;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -479,13 +479,13 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
@Override
|
||||
public ClientModel addClient(RealmModel realm, String clientId) {
|
||||
ClientModel client = getClientDelegate().addClient(realm, clientId);
|
||||
ClientModel client = getRealmDelegate().addClient(realm, clientId);
|
||||
return addedClient(realm, client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(RealmModel realm, String id, String clientId) {
|
||||
ClientModel client = getClientDelegate().addClient(realm, id, clientId);
|
||||
ClientModel client = getRealmDelegate().addClient(realm, id, clientId);
|
||||
return addedClient(realm, client);
|
||||
}
|
||||
|
||||
|
@ -550,7 +550,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
if (client == null) {
|
||||
// TODO: Handle with cluster invalidations too
|
||||
invalidations.add(cacheKey);
|
||||
return getClientDelegate().getClients(realm);
|
||||
return getRealmDelegate().getClients(realm);
|
||||
}
|
||||
list.add(client);
|
||||
}
|
||||
|
@ -573,7 +573,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
for (RoleModel role : client.getRoles()) {
|
||||
roleRemovalInvalidations(role.getId(), role.getName(), client.getId());
|
||||
}
|
||||
return getClientDelegate().removeClient(id, realm);
|
||||
return getRealmDelegate().removeClient(id, realm);
|
||||
}
|
||||
|
||||
|
||||
|
@ -636,7 +636,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
String cacheKey = getRolesCacheKey(client.getId());
|
||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(client.getId());
|
||||
if (queryDB) {
|
||||
return getClientDelegate().getClientRoles(realm, client);
|
||||
return getRealmDelegate().getClientRoles(realm, client);
|
||||
}
|
||||
|
||||
RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
|
||||
|
@ -646,7 +646,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
Set<RoleModel> model = getClientDelegate().getClientRoles(realm, client);
|
||||
Set<RoleModel> model = getRealmDelegate().getClientRoles(realm, client);
|
||||
if (model == null) return null;
|
||||
Set<String> ids = new HashSet<>();
|
||||
for (RoleModel role : model) ids.add(role.getId());
|
||||
|
@ -660,7 +660,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
RoleModel role = session.realms().getRoleById(id, realm);
|
||||
if (role == null) {
|
||||
invalidations.add(cacheKey);
|
||||
return getClientDelegate().getClientRoles(realm, client);
|
||||
return getRealmDelegate().getClientRoles(realm, client);
|
||||
}
|
||||
list.add(role);
|
||||
}
|
||||
|
@ -674,7 +674,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
@Override
|
||||
public RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name) {
|
||||
RoleModel role = getClientDelegate().addClientRole(realm, client, id, name);
|
||||
RoleModel role = getRealmDelegate().addClientRole(realm, client, id, name);
|
||||
addedRole(role.getId(), client.getId());
|
||||
return role;
|
||||
}
|
||||
|
@ -714,7 +714,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
String cacheKey = getRoleByNameCacheKey(client.getId(), name);
|
||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(client.getId());
|
||||
if (queryDB) {
|
||||
return getClientDelegate().getClientRole(realm, client, name);
|
||||
return getRealmDelegate().getClientRole(realm, client, name);
|
||||
}
|
||||
|
||||
RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
|
||||
|
@ -724,7 +724,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
RoleModel model = getClientDelegate().getClientRole(realm, client, name);
|
||||
RoleModel model = getRealmDelegate().getClientRole(realm, client, name);
|
||||
if (model == null) return null;
|
||||
query = new RoleListQuery(loaded, cacheKey, realm, model.getId(), client.getClientId());
|
||||
logger.tracev("adding client role cache miss: client {0} key {1}", client.getClientId(), cacheKey);
|
||||
|
@ -734,7 +734,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
RoleModel role = getRoleById(query.getRoles().iterator().next(), realm);
|
||||
if (role == null) {
|
||||
invalidations.add(cacheKey);
|
||||
return getClientDelegate().getClientRole(realm, client, name);
|
||||
return getRealmDelegate().getClientRole(realm, client, name);
|
||||
}
|
||||
return role;
|
||||
}
|
||||
|
@ -1025,7 +1025,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
logger.tracev("adding client by id cache miss: {0}", cached.getClientId());
|
||||
cache.addRevisioned(cached, startupRevision);
|
||||
} else if (invalidations.contains(id)) {
|
||||
return getClientDelegate().getClientById(id, realm);
|
||||
return getRealmDelegate().getClientById(id, realm);
|
||||
} else if (managedApplications.containsKey(id)) {
|
||||
return managedApplications.get(id);
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.keycloak.authorization.store.ScopeStore;
|
|||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
|
||||
import org.keycloak.models.cache.infinispan.authorization.entities.CachedPolicy;
|
||||
import org.keycloak.models.cache.infinispan.authorization.entities.CachedResource;
|
||||
|
@ -64,6 +65,7 @@ import org.keycloak.models.cache.infinispan.authorization.events.ScopeRemovedEve
|
|||
import org.keycloak.models.cache.infinispan.authorization.events.ScopeUpdatedEvent;
|
||||
import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
|
||||
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
||||
import org.keycloak.storage.StorageId;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -348,6 +350,9 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
|||
protected class ResourceServerCache implements ResourceServerStore {
|
||||
@Override
|
||||
public ResourceServer create(String clientId) {
|
||||
if (!StorageId.isLocalStorage(clientId)) {
|
||||
throw new ModelException("Creating resource server from federated ClientModel not supported");
|
||||
}
|
||||
ResourceServer server = getResourceServerStoreDelegate().create(clientId);
|
||||
registerResourceServerInvalidation(server.getId());
|
||||
return server;
|
||||
|
|
|
@ -25,6 +25,8 @@ import org.keycloak.authorization.jpa.entities.ScopeEntity;
|
|||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.store.ResourceServerStore;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.storage.StorageId;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
@ -46,6 +48,9 @@ public class JPAResourceServerStore implements ResourceServerStore {
|
|||
|
||||
@Override
|
||||
public ResourceServer create(String clientId) {
|
||||
if (!StorageId.isLocalStorage(clientId)) {
|
||||
throw new ModelException("Creating resource server from federated ClientModel not supported");
|
||||
}
|
||||
ResourceServerEntity entity = new ResourceServerEntity();
|
||||
|
||||
entity.setId(clientId);
|
||||
|
|
|
@ -125,9 +125,6 @@ public class ClientEntity {
|
|||
@CollectionTable(name="CLIENT_AUTH_FLOW_BINDINGS", joinColumns={ @JoinColumn(name="CLIENT_ID") })
|
||||
protected Map<String, String> authFlowBindings = new HashMap<String, String>();
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "client", cascade = CascadeType.REMOVE)
|
||||
Collection<ClientIdentityProviderMappingEntity> identityProviders = new ArrayList<ClientIdentityProviderMappingEntity>();
|
||||
|
||||
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "client")
|
||||
Collection<ProtocolMapperEntity> protocolMappers = new ArrayList<ProtocolMapperEntity>();
|
||||
|
||||
|
@ -322,14 +319,6 @@ public class ClientEntity {
|
|||
this.frontchannelLogout = frontchannelLogout;
|
||||
}
|
||||
|
||||
public Collection<ClientIdentityProviderMappingEntity> getIdentityProviders() {
|
||||
return this.identityProviders;
|
||||
}
|
||||
|
||||
public void setIdentityProviders(Collection<ClientIdentityProviderMappingEntity> identityProviders) {
|
||||
this.identityProviders = identityProviders;
|
||||
}
|
||||
|
||||
public Collection<ProtocolMapperEntity> getProtocolMappers() {
|
||||
return protocolMappers;
|
||||
}
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.jpa.entities;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author pedroigor
|
||||
*/
|
||||
@Table(name="CLIENT_IDENTITY_PROV_MAPPING")
|
||||
@Entity
|
||||
@IdClass(ClientIdentityProviderMappingEntity.Key.class)
|
||||
public class ClientIdentityProviderMappingEntity {
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "CLIENT_ID")
|
||||
private ClientEntity client;
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "IDENTITY_PROVIDER_ID")
|
||||
private IdentityProviderEntity identityProvider;
|
||||
|
||||
@Column(name = "RETRIEVE_TOKEN")
|
||||
private boolean retrieveToken;
|
||||
|
||||
public ClientEntity getClient() {
|
||||
return this.client;
|
||||
}
|
||||
|
||||
public void setClient(ClientEntity client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public IdentityProviderEntity getIdentityProvider() {
|
||||
return this.identityProvider;
|
||||
}
|
||||
|
||||
public void setIdentityProvider(IdentityProviderEntity identityProvider) {
|
||||
this.identityProvider = identityProvider;
|
||||
}
|
||||
|
||||
public void setRetrieveToken(boolean retrieveToken) {
|
||||
this.retrieveToken = retrieveToken;
|
||||
}
|
||||
|
||||
public boolean isRetrieveToken() {
|
||||
return retrieveToken;
|
||||
}
|
||||
|
||||
public static class Key implements Serializable {
|
||||
|
||||
private ClientEntity client;
|
||||
private IdentityProviderEntity identityProvider;
|
||||
|
||||
public Key() {
|
||||
}
|
||||
|
||||
public Key(ClientEntity client, IdentityProviderEntity identityProvider) {
|
||||
this.client = client;
|
||||
this.identityProvider = identityProvider;
|
||||
}
|
||||
|
||||
public ClientEntity getUser() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public IdentityProviderEntity getIdentityProvider() {
|
||||
return identityProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Key key = (Key) o;
|
||||
|
||||
if (identityProvider != null ? !identityProvider.getAlias().equals(key.identityProvider.getAlias()) : key.identityProvider != null)
|
||||
return false;
|
||||
if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = client != null ? client.getId().hashCode() : 0;
|
||||
result = 31 * result + (identityProvider != null ? identityProvider.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null) return false;
|
||||
if (!(o instanceof ClientIdentityProviderMappingEntity)) return false;
|
||||
|
||||
ClientIdentityProviderMappingEntity key = (ClientIdentityProviderMappingEntity) o;
|
||||
|
||||
if (identityProvider != null ? !identityProvider.getAlias().equals(key.identityProvider.getAlias()) : key.identityProvider != null)
|
||||
return false;
|
||||
if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = client != null ? client.getId().hashCode() : 0;
|
||||
result = 31 * result + (identityProvider != null ? identityProvider.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -29,4 +29,36 @@
|
|||
</createTable>
|
||||
<addPrimaryKey columnNames="CLIENT_ID, BINDING_NAME" constraintName="C_CLI_FLOW_BIND" tableName="CLIENT_AUTH_FLOW_BINDINGS"/>
|
||||
</changeSet>
|
||||
<changeSet author="bburke@redhat.com" id="4.0.0-CLEANUP-UNUSED-TABLE">
|
||||
<dropIndex tableName="CLIENT_IDENTITY_PROV_MAPPING" indexName="IDX_CLIENT_ID_PROV_MAP_CLIENT"/>
|
||||
<dropPrimaryKey tableName="CLIENT_IDENTITY_PROV_MAPPING" constraintName="CONSTR_CLIENT_IDEN_PROV_MAP"/>
|
||||
<dropUniqueConstraint tableName="CLIENT_IDENTITY_PROV_MAPPING" constraintName="UK_7CAELWNIBJI49AVXSRTUF6XJ12"/>
|
||||
<dropTable tableName="CLIENT_IDENTITY_PROV_MAPPING"/>
|
||||
</changeSet>
|
||||
<changeSet author="bburke@redhat.com" id="4.0.0-KEYCLOAK-6228">
|
||||
<!-- Modifying some columns so that CLIENT_ID is 255. Drop foreign key constraints too that referenced CLIENT tablename.
|
||||
This is needed for client storage SPI but only needed for tables that might reference a federated client -->
|
||||
|
||||
<!-- Modify USER_CONSENT -->
|
||||
<dropUniqueConstraint constraintName="UK_JKUWUVD56ONTGSUHOGM8UEWRT" tableName="USER_CONSENT"/>
|
||||
<modifyDataType tableName="USER_CONSENT" columnName="CLIENT_ID" newDataType="VARCHAR(255)"/>
|
||||
<addUniqueConstraint columnNames="CLIENT_ID, USER_ID" constraintName="UK_JKUWUVD56ONTGSUHOGM8UEWRT" tableName="USER_CONSENT"/>
|
||||
|
||||
<!-- Modify CLIENT_NODE_REGISTRATIONS -->
|
||||
<dropForeignKeyConstraint constraintName="FK4129723BA992F594" baseTableName="CLIENT"/>
|
||||
<modifyDataType tableName="CLIENT_NODE_REGISTRATIONS" columnName="CLIENT_ID" newDataType="VARCHAR(255)"/>
|
||||
|
||||
<!-- Modify OFFLINE_CLIENT_SESSION -->
|
||||
<dropPrimaryKey tableName="OFFLINE_CLIENT_SESSION" constraintName="CONSTRAINT_OFFL_CL_SES_PK3"/>
|
||||
<modifyDataType tableName="OFFLINE_CLIENT_SESSION" columnName="CLIENT_ID" newDataType="VARCHAR(255)"/>
|
||||
<addPrimaryKey columnNames="USER_SESSION_ID,CLIENT_ID, OFFLINE_FLAG" constraintName="CONSTRAINT_OFFL_CL_SES_PK3" tableName="OFFLINE_CLIENT_SESSION"/>
|
||||
|
||||
<!-- FED_USER_CONSENT -->
|
||||
<dropIndex tableName="FED_USER_CONSENT" indexName="IDX_FU_CONSENT"/>
|
||||
<modifyDataType tableName="FED_USER_CONSENT" columnName="CLIENT_ID" newDataType="VARCHAR(255)"/>
|
||||
<createIndex tableName="FED_USER_CONSENT" indexName="IDX_FU_CONSENT">
|
||||
<column name="USER_ID" type="VARCHAR(255)" />
|
||||
<column name="CLIENT_ID" type="VARCHAR(36)" />
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
<class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.IdentityProviderEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.IdentityProviderMapperEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.ClientIdentityProviderMappingEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.ProtocolMapperEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserConsentEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserConsentRoleEntity</class>
|
||||
|
|
|
@ -485,6 +485,8 @@ public class ModelToRepresentation {
|
|||
public static ClientRepresentation toRepresentation(ClientModel clientModel) {
|
||||
ClientRepresentation rep = new ClientRepresentation();
|
||||
rep.setId(clientModel.getId());
|
||||
String providerId = StorageId.resolveProviderId(clientModel);
|
||||
rep.setOrigin(providerId);
|
||||
rep.setClientId(clientModel.getClientId());
|
||||
rep.setName(clientModel.getName());
|
||||
rep.setDescription(clientModel.getDescription());
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.storage.client;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.storage.StorageId;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Helper base class for ClientModel implementations for ClientStorageProvider implementations.
|
||||
*
|
||||
* Contains default implementations of some methods
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public abstract class AbstractClientStorageAdapter extends UnsupportedOperationsClientStorageAdapter {
|
||||
protected KeycloakSession session;
|
||||
protected RealmModel realm;
|
||||
protected ClientStorageProviderModel component;
|
||||
private StorageId storageId;
|
||||
|
||||
|
||||
public AbstractClientStorageAdapter(KeycloakSession session, RealmModel realm, ClientStorageProviderModel component) {
|
||||
this.session = session;
|
||||
this.realm = realm;
|
||||
this.component = component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates federated id based on getClientId() method
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getId() {
|
||||
if (storageId == null) {
|
||||
storageId = new StorageId(component.getId(), getClientId());
|
||||
}
|
||||
return storageId.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final RealmModel getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method really isn't used by anybody anywhere. Legacy feature never supported.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean isSurrogateAuthRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method really isn't used by anybody anywhere. Legacy feature never supported.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
|
||||
// do nothing, we don't do anything with this.
|
||||
}
|
||||
|
||||
/**
|
||||
* This is for logout. Empty implementation for now. Can override if you can store this information somewhere.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Integer> getRegisteredNodes() {
|
||||
return Collections.EMPTY_MAP;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is for logout. Empty implementation for now. Can override if you can store this information somewhere.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public void registerNode(String nodeHost, int registrationTime) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* This is for logout. Empty implementation for now. Can override if you can store this information somewhere.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public void unregisterNode(String nodeHost) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Overriding implementations should call super.updateClient() as this fires off an update event.
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void updateClient() {
|
||||
session.getKeycloakSessionFactory().publish(new RealmModel.ClientUpdatedEvent() {
|
||||
|
||||
@Override
|
||||
public ClientModel getUpdatedClient() {
|
||||
return AbstractClientStorageAdapter.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakSession getKeycloakSession() {
|
||||
return session;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.storage.client;
|
||||
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.storage.ReadOnlyException;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public abstract class AbstractReadOnlyClientStorageAdapter extends AbstractClientStorageAdapter {
|
||||
public AbstractReadOnlyClientStorageAdapter(KeycloakSession session, RealmModel realm, ClientStorageProviderModel component) {
|
||||
super(session, realm, component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientId(String clientId) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDescription(String description) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWebOrigins(Set<String> webOrigins) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWebOrigin(String webOrigin) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWebOrigin(String webOrigin) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRedirectUris(Set<String> redirectUris) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRedirectUri(String redirectUri) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRedirectUri(String redirectUri) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setManagementUrl(String url) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRootUrl(String url) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBaseUrl(String url) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBearerOnly(boolean only) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNodeReRegistrationTimeout(int timeout) {
|
||||
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientAuthenticatorType(String clientAuthenticatorType) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSecret(String secret) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegistrationToken(String registrationToken) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProtocol(String protocol) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String name, String value) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(String name) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAuthenticationFlowBindingOverride(String binding) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthenticationFlowBindingOverride(String binding, String flowId) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrontchannelLogout(boolean flag) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPublicClient(boolean flag) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConsentRequired(boolean consentRequired) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStandardFlowEnabled(boolean standardFlowEnabled) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImplicitFlowEnabled(boolean implicitFlowEnabled) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientTemplate(ClientTemplateModel template) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseTemplateScope(boolean flag) {
|
||||
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseTemplateMappers(boolean flag) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseTemplateConfig(boolean flag) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotBefore(int notBefore) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeProtocolMapper(ProtocolMapperModel mapping) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProtocolMapper(ProtocolMapperModel mapping) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFullScopeAllowed(boolean value) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addScopeMapping(RoleModel role) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteScopeMapping(RoleModel role) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
}
|
|
@ -33,7 +33,7 @@ public class ClientStorageProviderSpi implements Spi {
|
|||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.storage.client;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Base helper class. Unsupported operations are implemented here that throw exception on invocation.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public abstract class UnsupportedOperationsClientStorageAdapter implements ClientModel {
|
||||
@Override
|
||||
public final RoleModel getRole(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final RoleModel addRole(String name) {
|
||||
throw new ModelException("Unsupported operation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final RoleModel addRole(String id, String name) {
|
||||
throw new ModelException("Unsupported operation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean removeRole(RoleModel role) {
|
||||
throw new ModelException("Unsupported operation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Set<RoleModel> getRoles() {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List<String> getDefaultRoles() {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void addDefaultRole(String name) {
|
||||
throw new ModelException("Unsupported operation");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void updateDefaultRoles(String... defaultRoles) {
|
||||
throw new ModelException("Unsupported operation");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void removeDefaultRoles(String... defaultRoles) {
|
||||
throw new ModelException("Unsupported operation");
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -70,4 +70,5 @@ org.keycloak.transaction.TransactionManagerLookupSpi
|
|||
org.keycloak.credential.hash.PasswordHashSpi
|
||||
org.keycloak.credential.CredentialSpi
|
||||
org.keycloak.keys.PublicKeyStorageSpi
|
||||
org.keycloak.keys.KeySpi
|
||||
org.keycloak.keys.KeySpi
|
||||
org.keycloak.storage.client.ClientStorageProviderSpi
|
|
@ -137,7 +137,7 @@ public class CacheableStorageProviderModel extends PrioritizedComponentModel {
|
|||
getConfig().putSingle(CACHE_INVALID_BEFORE, Long.toString(cacheInvalidBefore));
|
||||
}
|
||||
|
||||
public static enum CachePolicy {
|
||||
public enum CachePolicy {
|
||||
NO_CACHE,
|
||||
DEFAULT,
|
||||
EVICT_DAILY,
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.keycloak.storage.client.ClientStorageProviderFactory;
|
|||
import org.keycloak.storage.client.ClientStorageProviderModel;
|
||||
import org.keycloak.storage.user.UserLookupProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -186,7 +187,8 @@ public class ClientStorageManager implements ClientProvider {
|
|||
@Override
|
||||
public RoleModel getClientRole(RealmModel realm, ClientModel client, String name) {
|
||||
if (!StorageId.isLocalStorage(client.getId())) {
|
||||
throw new RuntimeException("Federated clients do not support this operation");
|
||||
//throw new RuntimeException("Federated clients do not support this operation");
|
||||
return null;
|
||||
}
|
||||
return session.clientLocalStorage().getClientRole(realm, client, name);
|
||||
}
|
||||
|
@ -194,11 +196,17 @@ public class ClientStorageManager implements ClientProvider {
|
|||
@Override
|
||||
public Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client) {
|
||||
if (!StorageId.isLocalStorage(client.getId())) {
|
||||
throw new RuntimeException("Federated clients do not support this operation");
|
||||
//throw new RuntimeException("Federated clients do not support this operation");
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
return session.clientLocalStorage().getClientRoles(realm, client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeClient(String id, RealmModel realm) {
|
||||
if (!StorageId.isLocalStorage(id)) {
|
||||
|
@ -207,4 +215,6 @@ public class ClientStorageManager implements ClientProvider {
|
|||
return session.clientLocalStorage().removeClient(id, realm);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.testsuite.federation;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.client.AbstractClientStorageAdapter;
|
||||
import org.keycloak.storage.client.AbstractReadOnlyClientStorageAdapter;
|
||||
import org.keycloak.storage.client.ClientLookupProvider;
|
||||
import org.keycloak.storage.client.ClientStorageProvider;
|
||||
import org.keycloak.storage.client.ClientStorageProviderModel;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class HardcodedClientStorageProvider implements ClientStorageProvider, ClientLookupProvider {
|
||||
protected KeycloakSession session;
|
||||
protected ClientStorageProviderModel component;
|
||||
protected String clientId;
|
||||
protected String redirectUri;
|
||||
|
||||
public HardcodedClientStorageProvider(KeycloakSession session, ClientStorageProviderModel component) {
|
||||
this.session = session;
|
||||
this.component = component;
|
||||
this.clientId = component.getConfig().getFirst(HardcodedClientStorageProviderFactory.CLIENT_ID);
|
||||
this.redirectUri = component.getConfig().getFirst(HardcodedClientStorageProviderFactory.REDIRECT_URI);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClientById(String id, RealmModel realm) {
|
||||
StorageId storageId = new StorageId(id);
|
||||
final String clientId = storageId.getExternalId();
|
||||
if (clientId.equals(clientId)) return new ClientAdapter(realm);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClientByClientId(String clientId, RealmModel realm) {
|
||||
if (clientId.equals(clientId)) return new ClientAdapter(realm);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
public class ClientAdapter extends AbstractReadOnlyClientStorageAdapter {
|
||||
|
||||
public ClientAdapter(RealmModel realm) {
|
||||
super(HardcodedClientStorageProvider.this.session, realm, HardcodedClientStorageProvider.this.component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Federated Client";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Pulled in from client storage provider";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getWebOrigins() {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRedirectUris() {
|
||||
HashSet<String> set = new HashSet<>();
|
||||
set.add(redirectUri);
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManagementUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRootUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBaseUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBearerOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNodeReRegistrationTimeout() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientAuthenticatorType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateSecret(String secret) {
|
||||
return "password".equals(secret);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSecret() {
|
||||
return "password";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRegistrationToken() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttribute(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAttributes() {
|
||||
return Collections.EMPTY_MAP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthenticationFlowBindingOverride(String binding) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAuthenticationFlowBindingOverrides() {
|
||||
return Collections.EMPTY_MAP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFrontchannelLogout() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPublicClient() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConsentRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStandardFlowEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImplicitFlowEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectAccessGrantsEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isServiceAccountsEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientTemplateModel getClientTemplate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useTemplateScope() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useTemplateMappers() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useTemplateConfig() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNotBefore() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ProtocolMapperModel> getProtocolMappers() {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolMapperModel getProtocolMapperById(String id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolMapperModel getProtocolMapperByName(String protocol, String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullScopeAllowed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getScopeMappings() {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getRealmScopeMappings() {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasScope(RoleModel role) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.testsuite.federation;
|
||||
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
import org.keycloak.storage.client.ClientStorageProviderFactory;
|
||||
import org.keycloak.storage.client.ClientStorageProviderModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class HardcodedClientStorageProviderFactory implements ClientStorageProviderFactory<HardcodedClientStorageProvider> {
|
||||
@Override
|
||||
public HardcodedClientStorageProvider create(KeycloakSession session, ComponentModel model) {
|
||||
return new HardcodedClientStorageProvider(session, new ClientStorageProviderModel(model));
|
||||
}
|
||||
|
||||
|
||||
public static final String PROVIDER_ID = "hardcoded-client";
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
protected static final List<ProviderConfigProperty> CONFIG_PROPERTIES;
|
||||
|
||||
public static final String CLIENT_ID = "client_id";
|
||||
|
||||
public static final String REDIRECT_URI = "redirect_uri";
|
||||
|
||||
static {
|
||||
CONFIG_PROPERTIES = ProviderConfigurationBuilder.create()
|
||||
.property().name(CLIENT_ID)
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.label("Hardcoded Client Id")
|
||||
.helpText("Only this client id is available for lookup")
|
||||
.defaultValue("hardcoded-client")
|
||||
.add()
|
||||
.property().name(REDIRECT_URI)
|
||||
.type(ProviderConfigProperty.BOOLEAN_TYPE)
|
||||
.label("Redirect Uri")
|
||||
.helpText("Valid redirect uri. Only one allowed")
|
||||
.defaultValue("http://localhost:8180/*")
|
||||
.add()
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return CONFIG_PROPERTIES;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.testsuite.federation.HardcodedClientStorageProviderFactory
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright 2017 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.testsuite.federation.storage;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.resource.ClientsResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.AuthenticationFlowBindings;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.client.ClientStorageProvider;
|
||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.authentication.PushButtonAuthenticatorFactory;
|
||||
import org.keycloak.testsuite.federation.HardcodedClientStorageProviderFactory;
|
||||
import org.keycloak.testsuite.federation.UserMapStorageFactory;
|
||||
import org.keycloak.testsuite.federation.UserPropertyFileStorageFactory;
|
||||
import org.keycloak.testsuite.forms.UsernameOnlyAuthenticator;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.ErrorPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.keycloak.util.BasicAuthHelper;
|
||||
import org.openqa.selenium.By;
|
||||
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Form;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Test that clients can override auth flows
|
||||
*
|
||||
* @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
|
||||
*/
|
||||
public class ClientStorageTest extends AbstractTestRealmKeycloakTest {
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(this);
|
||||
|
||||
@Page
|
||||
protected AppPage appPage;
|
||||
|
||||
@Page
|
||||
protected LoginPage loginPage;
|
||||
|
||||
@Page
|
||||
protected ErrorPage errorPage;
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
}
|
||||
|
||||
@Deployment
|
||||
public static WebArchive deploy() {
|
||||
return RunOnServerDeployment.create(UserResource.class)
|
||||
.addPackages(true, "org.keycloak.testsuite");
|
||||
}
|
||||
|
||||
protected String addComponent(ComponentRepresentation component) {
|
||||
Response resp = adminClient.realm("test").components().add(component);
|
||||
resp.close();
|
||||
String id = ApiUtil.getCreatedId(resp);
|
||||
getCleanup().addComponentId(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void addProvidersBeforeTest() throws URISyntaxException, IOException {
|
||||
ComponentRepresentation provider = new ComponentRepresentation();
|
||||
provider.setName("client-storage-hardcoded");
|
||||
provider.setProviderId(HardcodedClientStorageProviderFactory.PROVIDER_ID);
|
||||
provider.setProviderType(ClientStorageProvider.class.getName());
|
||||
provider.setConfig(new MultivaluedHashMap<>());
|
||||
provider.getConfig().putSingle(HardcodedClientStorageProviderFactory.CLIENT_ID, "hardcoded-client");
|
||||
provider.getConfig().putSingle(HardcodedClientStorageProviderFactory.REDIRECT_URI, oauth.getRedirectUri());
|
||||
|
||||
String providerId = addComponent(provider);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//@Test
|
||||
public void testRunConsole() throws Exception {
|
||||
Thread.sleep(10000000);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testBrowser() throws Exception {
|
||||
String clientId = "hardcoded-client";
|
||||
testBrowser(clientId);
|
||||
}
|
||||
|
||||
private void testBrowser(String clientId) {
|
||||
oauth.clientId(clientId);
|
||||
String loginFormUrl = oauth.getLoginFormUrl();
|
||||
log.info("loginFormUrl: " + loginFormUrl);
|
||||
|
||||
//Thread.sleep(10000000);
|
||||
|
||||
driver.navigate().to(loginFormUrl);
|
||||
|
||||
loginPage.assertCurrent();
|
||||
|
||||
// Fill username+password. I am successfully authenticated
|
||||
oauth.fillLoginForm("test-user@localhost", "password");
|
||||
appPage.assertCurrent();
|
||||
|
||||
events.expectLogin().client(clientId).detail(Details.USERNAME, "test-user@localhost").assertEvent();
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
|
||||
Assert.assertNotNull(tokenResponse.getAccessToken());
|
||||
Assert.assertNotNull(tokenResponse.getRefreshToken());
|
||||
|
||||
events.clear();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGrantAccessTokenNoOverride() throws Exception {
|
||||
testDirectGrant("hardcoded-client");
|
||||
}
|
||||
|
||||
private void testDirectGrant(String clientId) {
|
||||
Client httpClient = javax.ws.rs.client.ClientBuilder.newClient();
|
||||
String grantUri = oauth.getResourceOwnerPasswordCredentialGrantUrl();
|
||||
WebTarget grantTarget = httpClient.target(grantUri);
|
||||
|
||||
{ // test no password
|
||||
String header = BasicAuthHelper.createHeader(clientId, "password");
|
||||
Form form = new Form();
|
||||
form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
|
||||
form.param("username", "test-user@localhost");
|
||||
Response response = grantTarget.request()
|
||||
.header(HttpHeaders.AUTHORIZATION, header)
|
||||
.post(Entity.form(form));
|
||||
assertEquals(401, response.getStatus());
|
||||
response.close();
|
||||
}
|
||||
|
||||
{ // test invalid password
|
||||
String header = BasicAuthHelper.createHeader(clientId, "password");
|
||||
Form form = new Form();
|
||||
form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
|
||||
form.param("username", "test-user@localhost");
|
||||
form.param("password", "invalid");
|
||||
Response response = grantTarget.request()
|
||||
.header(HttpHeaders.AUTHORIZATION, header)
|
||||
.post(Entity.form(form));
|
||||
assertEquals(401, response.getStatus());
|
||||
response.close();
|
||||
}
|
||||
|
||||
{ // test valid password
|
||||
String header = BasicAuthHelper.createHeader(clientId, "password");
|
||||
Form form = new Form();
|
||||
form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
|
||||
form.param("username", "test-user@localhost");
|
||||
form.param("password", "password");
|
||||
Response response = grantTarget.request()
|
||||
.header(HttpHeaders.AUTHORIZATION, header)
|
||||
.post(Entity.form(form));
|
||||
assertEquals(200, response.getStatus());
|
||||
response.close();
|
||||
}
|
||||
|
||||
httpClient.close();
|
||||
events.clear();
|
||||
}
|
||||
}
|
|
@ -41,7 +41,7 @@ import org.keycloak.representations.idm.RoleRepresentation;
|
|||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import static org.keycloak.storage.UserStorageProviderModel.CACHE_POLICY;
|
||||
import org.keycloak.storage.UserStorageProviderModel.CachePolicy;
|
||||
import org.keycloak.storage.CacheableStorageProviderModel.CachePolicy;
|
||||
import static org.keycloak.storage.UserStorageProviderModel.EVICTION_DAY;
|
||||
import static org.keycloak.storage.UserStorageProviderModel.EVICTION_HOUR;
|
||||
import static org.keycloak.storage.UserStorageProviderModel.EVICTION_MINUTE;
|
||||
|
|
Loading…
Reference in a new issue