[KEYCLOAK-8285] Remove user-storage-jpa and user-storage-simple from Keycloak repository
This commit is contained in:
parent
01051016f5
commit
8b6db21e56
20 changed files with 0 additions and 1561 deletions
|
@ -36,7 +36,5 @@
|
|||
<module>authenticator</module>
|
||||
<module>rest</module>
|
||||
<module>domain-extension</module>
|
||||
<module>user-storage-simple</module>
|
||||
<module>user-storage-jpa</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
Example User Storage Provider with EJB and JPA
|
||||
===================================================
|
||||
|
||||
This is an example of the User Storage SPI implemented using EJB and JPA. You must first deploy the datasource it uses.
|
||||
Start up the Keycloak server. Then in the directory of this example type the following maven command:
|
||||
|
||||
mvn -Padd-datasource install
|
||||
|
||||
You only need to execute this maven command once. If you execute this again, then you will get an error message that the datasource
|
||||
already exists.
|
||||
|
||||
If you open the pom.xml file you'll see that the add-datasource profile creates an XA datasource using the built
|
||||
in H2 database that comes with the server. An XA datasource is required because you cannot use two non-xa datasources
|
||||
in the same transaction. The Keycloak database is non-xa.
|
||||
|
||||
Another thing to note is that the xa-datasource created is in-memory only. If you reboot the server, any users you've
|
||||
added or changes you've made to users loaded by this provider will be wiped clean.
|
||||
|
||||
To deploy the provider, run the following maven command:
|
||||
|
||||
mvn clean install wildfly:deploy
|
||||
|
||||
You can run as many times as you want and the provider will be redeployed.
|
||||
|
||||
Login and go to the User Federation tab and you should now see your deployed provider in the add-provider list box.
|
||||
Add the provider, save it, then any new user you create will be stored and in the custom store you implemented. You
|
||||
can modify the example and hot deploy it using the above maven command again.
|
||||
|
|
@ -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.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-examples-providers-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>User Storage JPA Provider Example</name>
|
||||
<description/>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>user-storage-jpa-example</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
<artifactId>hibernate-jpa-2.1-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-entitymanager</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.spec.javax.ejb</groupId>
|
||||
<artifactId>jboss-ejb-api_3.2_spec</artifactId>
|
||||
<version>1.0.0.Final</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>user-storage-jpa-example</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.wildfly.plugins</groupId>
|
||||
<artifactId>wildfly-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>false</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<!-- this profile deploys an XA H2 datasource connection. It is in-memory only, so rebooting the server will lose
|
||||
your changes. Run type: mvn -Padd-datasource install. THis can only be run once. If you run it again
|
||||
you'll get an error that the datasource already exists -->
|
||||
<profile>
|
||||
<id>add-datasource</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.wildfly.plugins</groupId>
|
||||
<artifactId>wildfly-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>false</skip>
|
||||
<force>true</force>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-datasource</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>add-resource</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<force/>
|
||||
<address>subsystem=datasources</address>
|
||||
<resources>
|
||||
<resource>
|
||||
<address>xa-data-source=java:jboss/datasources/ExampleXADS</address>
|
||||
<properties>
|
||||
<jndi-name>java:jboss/datasources/ExampleXADS</jndi-name>
|
||||
<enabled>true</enabled>
|
||||
<driver-name>h2</driver-name>
|
||||
</properties>
|
||||
<resources>
|
||||
<resource>
|
||||
<address>
|
||||
xa-datasource-properties=URL
|
||||
</address>
|
||||
<properties>
|
||||
<value>jdbc:h2:mem:test</value>
|
||||
</properties>
|
||||
</resource>
|
||||
</resources>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
|
@ -1,316 +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.examples.storage.user;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.credential.CredentialInput;
|
||||
import org.keycloak.credential.CredentialInputUpdater;
|
||||
import org.keycloak.credential.CredentialInputValidator;
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.cache.CachedUserModel;
|
||||
import org.keycloak.models.cache.OnUserCache;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.user.UserLookupProvider;
|
||||
import org.keycloak.storage.user.UserQueryProvider;
|
||||
import org.keycloak.storage.user.UserRegistrationProvider;
|
||||
|
||||
import javax.ejb.Local;
|
||||
import javax.ejb.Remove;
|
||||
import javax.ejb.Stateful;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.TypedQuery;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@Stateful
|
||||
@Local(EjbExampleUserStorageProvider.class)
|
||||
public class EjbExampleUserStorageProvider implements UserStorageProvider,
|
||||
UserLookupProvider,
|
||||
UserRegistrationProvider,
|
||||
UserQueryProvider,
|
||||
CredentialInputUpdater,
|
||||
CredentialInputValidator,
|
||||
OnUserCache
|
||||
{
|
||||
private static final Logger logger = Logger.getLogger(EjbExampleUserStorageProvider.class);
|
||||
public static final String PASSWORD_CACHE_KEY = UserAdapter.class.getName() + ".password";
|
||||
|
||||
@PersistenceContext
|
||||
protected EntityManager em;
|
||||
|
||||
protected ComponentModel model;
|
||||
protected KeycloakSession session;
|
||||
|
||||
public void setModel(ComponentModel model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public void setSession(KeycloakSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, GroupModel group) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, RoleModel role) {
|
||||
|
||||
}
|
||||
|
||||
@Remove
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserById(String id, RealmModel realm) {
|
||||
logger.info("getUserById: " + id);
|
||||
String persistenceId = StorageId.externalId(id);
|
||||
UserEntity entity = em.find(UserEntity.class, persistenceId);
|
||||
if (entity == null) {
|
||||
logger.info("could not find user by id: " + id);
|
||||
return null;
|
||||
}
|
||||
return new UserAdapter(session, realm, model, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByUsername(String username, RealmModel realm) {
|
||||
logger.info("getUserByUsername: " + username);
|
||||
TypedQuery<UserEntity> query = em.createNamedQuery("getUserByUsername", UserEntity.class);
|
||||
query.setParameter("username", username);
|
||||
List<UserEntity> result = query.getResultList();
|
||||
if (result.isEmpty()) {
|
||||
logger.info("could not find username: " + username);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new UserAdapter(session, realm, model, result.get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByEmail(String email, RealmModel realm) {
|
||||
TypedQuery<UserEntity> query = em.createNamedQuery("getUserByEmail", UserEntity.class);
|
||||
query.setParameter("email", email);
|
||||
List<UserEntity> result = query.getResultList();
|
||||
if (result.isEmpty()) return null;
|
||||
return new UserAdapter(session, realm, model, result.get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel addUser(RealmModel realm, String username) {
|
||||
UserEntity entity = new UserEntity();
|
||||
entity.setId(UUID.randomUUID().toString());
|
||||
entity.setUsername(username);
|
||||
em.persist(entity);
|
||||
logger.info("added user: " + username);
|
||||
return new UserAdapter(session, realm, model, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeUser(RealmModel realm, UserModel user) {
|
||||
String persistenceId = StorageId.externalId(user.getId());
|
||||
UserEntity entity = em.find(UserEntity.class, persistenceId);
|
||||
if (entity == null) return false;
|
||||
em.remove(entity);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCache(RealmModel realm, CachedUserModel user, UserModel delegate) {
|
||||
String password = ((UserAdapter)delegate).getPassword();
|
||||
if (password != null) {
|
||||
user.getCachedWith().put(PASSWORD_CACHE_KEY, password);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCredentialType(String credentialType) {
|
||||
return CredentialModel.PASSWORD.equals(credentialType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
|
||||
if (!supportsCredentialType(input.getType()) || !(input instanceof UserCredentialModel)) return false;
|
||||
UserCredentialModel cred = (UserCredentialModel)input;
|
||||
UserAdapter adapter = getUserAdapter(user);
|
||||
adapter.setPassword(cred.getValue());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public UserAdapter getUserAdapter(UserModel user) {
|
||||
UserAdapter adapter = null;
|
||||
if (user instanceof CachedUserModel) {
|
||||
adapter = (UserAdapter)((CachedUserModel)user).getDelegateForUpdate();
|
||||
} else {
|
||||
adapter = (UserAdapter)user;
|
||||
}
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
|
||||
if (!supportsCredentialType(credentialType)) return;
|
||||
|
||||
getUserAdapter(user).setPassword(null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
|
||||
if (getUserAdapter(user).getPassword() != null) {
|
||||
Set<String> set = new HashSet<>();
|
||||
set.add(CredentialModel.PASSWORD);
|
||||
return set;
|
||||
} else {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
|
||||
return supportsCredentialType(credentialType) && getPassword(user) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
|
||||
if (!supportsCredentialType(input.getType()) || !(input instanceof UserCredentialModel)) return false;
|
||||
UserCredentialModel cred = (UserCredentialModel)input;
|
||||
String password = getPassword(user);
|
||||
return password != null && password.equals(cred.getValue());
|
||||
}
|
||||
|
||||
public String getPassword(UserModel user) {
|
||||
String password = null;
|
||||
if (user instanceof CachedUserModel) {
|
||||
password = (String)((CachedUserModel)user).getCachedWith().get(PASSWORD_CACHE_KEY);
|
||||
} else if (user instanceof UserAdapter) {
|
||||
password = ((UserAdapter)user).getPassword();
|
||||
}
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUsersCount(RealmModel realm) {
|
||||
Object count = em.createNamedQuery("getUserCount")
|
||||
.getSingleResult();
|
||||
return ((Number)count).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getUsers(RealmModel realm) {
|
||||
return getUsers(realm, -1, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
|
||||
|
||||
TypedQuery<UserEntity> query = em.createNamedQuery("getAllUsers", UserEntity.class);
|
||||
if (firstResult != -1) {
|
||||
query.setFirstResult(firstResult);
|
||||
}
|
||||
if (maxResults != -1) {
|
||||
query.setMaxResults(maxResults);
|
||||
}
|
||||
List<UserEntity> results = query.getResultList();
|
||||
List<UserModel> users = new LinkedList<>();
|
||||
for (UserEntity entity : results) users.add(new UserAdapter(session, realm, model, entity));
|
||||
return users;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(String search, RealmModel realm) {
|
||||
return searchForUser(search, realm, -1, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
|
||||
TypedQuery<UserEntity> query = em.createNamedQuery("searchForUser", UserEntity.class);
|
||||
query.setParameter("search", "%" + search.toLowerCase() + "%");
|
||||
if (firstResult != -1) {
|
||||
query.setFirstResult(firstResult);
|
||||
}
|
||||
if (maxResults != -1) {
|
||||
query.setMaxResults(maxResults);
|
||||
}
|
||||
List<UserEntity> results = query.getResultList();
|
||||
List<UserModel> users = new LinkedList<>();
|
||||
for (UserEntity entity : results) users.add(new UserAdapter(session, realm, model, entity));
|
||||
return users;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(Map<String, String> params, RealmModel realm) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(Map<String, String> params, RealmModel realm, int firstResult, int maxResults) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getRoleMembers(RealmModel realm, RoleModel role, int firstResult, int maxResults) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getRoleMembers(RealmModel realm, RoleModel role) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
}
|
|
@ -1,62 +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.examples.storage.user;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.storage.UserStorageProviderFactory;
|
||||
|
||||
import javax.naming.InitialContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class EjbExampleUserStorageProviderFactory implements UserStorageProviderFactory<EjbExampleUserStorageProvider> {
|
||||
private static final Logger logger = Logger.getLogger(EjbExampleUserStorageProviderFactory.class);
|
||||
|
||||
|
||||
@Override
|
||||
public EjbExampleUserStorageProvider create(KeycloakSession session, ComponentModel model) {
|
||||
try {
|
||||
InitialContext ctx = new InitialContext();
|
||||
EjbExampleUserStorageProvider provider = (EjbExampleUserStorageProvider)ctx.lookup("java:global/user-storage-jpa-example/" + EjbExampleUserStorageProvider.class.getSimpleName());
|
||||
provider.setModel(model);
|
||||
provider.setSession(session);
|
||||
return provider;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "example-user-storage-jpa";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "JPA Example User Storage Provider";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
logger.info("<<<<<< Closing factory");
|
||||
|
||||
}
|
||||
}
|
|
@ -1,135 +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.examples.storage.user;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UserAdapter extends AbstractUserAdapterFederatedStorage {
|
||||
private static final Logger logger = Logger.getLogger(UserAdapter.class);
|
||||
protected UserEntity entity;
|
||||
protected String keycloakId;
|
||||
|
||||
public UserAdapter(KeycloakSession session, RealmModel realm, ComponentModel model, UserEntity entity) {
|
||||
super(session, realm, model);
|
||||
this.entity = entity;
|
||||
keycloakId = StorageId.keycloakId(model, entity.getId());
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return entity.getPassword();
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
entity.setPassword(password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return entity.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsername(String username) {
|
||||
entity.setUsername(username);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEmail(String email) {
|
||||
entity.setEmail(email);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEmail() {
|
||||
return entity.getEmail();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return keycloakId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSingleAttribute(String name, String value) {
|
||||
if (name.equals("phone")) {
|
||||
entity.setPhone(value);
|
||||
} else {
|
||||
super.setSingleAttribute(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(String name) {
|
||||
if (name.equals("phone")) {
|
||||
entity.setPhone(null);
|
||||
} else {
|
||||
super.removeAttribute(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String name, List<String> values) {
|
||||
if (name.equals("phone")) {
|
||||
entity.setPhone(values.get(0));
|
||||
} else {
|
||||
super.setAttribute(name, values);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFirstAttribute(String name) {
|
||||
if (name.equals("phone")) {
|
||||
return entity.getPhone();
|
||||
} else {
|
||||
return super.getFirstAttribute(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
Map<String, List<String>> attrs = super.getAttributes();
|
||||
MultivaluedHashMap<String, String> all = new MultivaluedHashMap<>();
|
||||
all.putAll(attrs);
|
||||
all.add("phone", entity.getPhone());
|
||||
return all;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAttribute(String name) {
|
||||
if (name.equals("phone")) {
|
||||
List<String> phone = new LinkedList<>();
|
||||
phone.add(entity.getPhone());
|
||||
return phone;
|
||||
} else {
|
||||
return super.getAttribute(name);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,86 +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.examples.storage.user;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="getUserByUsername", query="select u from UserEntity u where u.username = :username"),
|
||||
@NamedQuery(name="getUserByEmail", query="select u from UserEntity u where u.email = :email"),
|
||||
@NamedQuery(name="getUserCount", query="select count(u) from UserEntity u"),
|
||||
@NamedQuery(name="getAllUsers", query="select u from UserEntity u"),
|
||||
@NamedQuery(name="searchForUser", query="select u from UserEntity u where " +
|
||||
"( lower(u.username) like :search or u.email like :search ) order by u.username"),
|
||||
})
|
||||
@Entity
|
||||
public class UserEntity {
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
|
||||
private String username;
|
||||
private String email;
|
||||
private String password;
|
||||
private String phone;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<persistence version="2.0"
|
||||
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://java.sun.com/xml/ns/persistence
|
||||
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
|
||||
<persistence-unit name="user-storage-jpa-example" transaction-type="JTA">
|
||||
<jta-data-source>java:jboss/datasources/ExampleXADS</jta-data-source>
|
||||
|
||||
<class>org.keycloak.examples.storage.user.UserEntity</class>
|
||||
|
||||
<properties>
|
||||
<property name="hibernate.hbm2ddl.auto" value="update" />
|
||||
<property name="hibernate.show_sql" value="false" />
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
</persistence>
|
|
@ -1 +0,0 @@
|
|||
org.keycloak.examples.storage.user.EjbExampleUserStorageProviderFactory
|
|
@ -1,16 +0,0 @@
|
|||
Example User Storage Provider
|
||||
===================================================
|
||||
|
||||
This is an example of user storage backed by a simple properties file. This properties file only contains username/password
|
||||
key pairs. To deploy this provider you must have Keycloak running in standalone or standalone-ha mode. Then type the follow maven command:
|
||||
|
||||
mvn clean install wildfly:deploy
|
||||
|
||||
|
||||
The "readonly-property-file" provider is hardcoded to look within the users.properties file embeded in the deployment jar
|
||||
THere is one user 'tbrady' with a password of 'superbowl'
|
||||
|
||||
The "writeable-property-file" provider can be configured to point to a property file on disk. It uses federated
|
||||
storage to augment the property file with any other information the user wants.
|
||||
|
||||
Our developer guide walks through the implementation of both of these providers.
|
|
@ -1,95 +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.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-examples-providers-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.5.0.Final-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>UserStorageProvider Simple Example</name>
|
||||
<description/>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>user-storage-properties-example</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>user-storage-properties-example</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.wildfly.plugins</groupId>
|
||||
<artifactId>wildfly-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>false</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>product</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- The example needs to be deployed during the product build, because it's testsuite's dependency.
|
||||
We need to override setting from parent module. -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>false</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
|
@ -1,147 +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.examples.userstorage.readonly;
|
||||
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.credential.CredentialInput;
|
||||
import org.keycloak.credential.CredentialInputUpdater;
|
||||
import org.keycloak.credential.CredentialInputValidator;
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.storage.ReadOnlyException;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.adapter.AbstractUserAdapter;
|
||||
import org.keycloak.storage.user.UserLookupProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class PropertyFileUserStorageProvider implements
|
||||
UserStorageProvider,
|
||||
UserLookupProvider,
|
||||
CredentialInputValidator,
|
||||
CredentialInputUpdater
|
||||
{
|
||||
protected KeycloakSession session;
|
||||
protected Properties properties;
|
||||
protected ComponentModel model;
|
||||
// map of loaded users in this transaction
|
||||
protected Map<String, UserModel> loadedUsers = new HashMap<>();
|
||||
|
||||
public PropertyFileUserStorageProvider(KeycloakSession session, ComponentModel model, Properties properties) {
|
||||
this.session = session;
|
||||
this.model = model;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
// UserLookupProvider methods
|
||||
|
||||
@Override
|
||||
public UserModel getUserByUsername(String username, RealmModel realm) {
|
||||
UserModel adapter = loadedUsers.get(username);
|
||||
if (adapter == null) {
|
||||
String password = properties.getProperty(username);
|
||||
if (password != null) {
|
||||
adapter = createAdapter(realm, username);
|
||||
loadedUsers.put(username, adapter);
|
||||
}
|
||||
}
|
||||
return adapter;
|
||||
}
|
||||
|
||||
protected UserModel createAdapter(RealmModel realm, String username) {
|
||||
return new AbstractUserAdapter(session, realm, model) {
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserById(String id, RealmModel realm) {
|
||||
StorageId storageId = new StorageId(id);
|
||||
String username = storageId.getExternalId();
|
||||
return getUserByUsername(username, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByEmail(String email, RealmModel realm) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// CredentialInputValidator methods
|
||||
|
||||
@Override
|
||||
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
|
||||
String password = properties.getProperty(user.getUsername());
|
||||
return credentialType.equals(CredentialModel.PASSWORD) && password != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCredentialType(String credentialType) {
|
||||
return credentialType.equals(CredentialModel.PASSWORD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
|
||||
if (!supportsCredentialType(input.getType()) || !(input instanceof UserCredentialModel)) return false;
|
||||
|
||||
UserCredentialModel cred = (UserCredentialModel)input;
|
||||
String password = properties.getProperty(user.getUsername());
|
||||
if (password == null) return false;
|
||||
return password.equals(cred.getValue());
|
||||
}
|
||||
|
||||
// CredentialInputUpdater methods
|
||||
|
||||
@Override
|
||||
public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
|
||||
if (input.getType().equals(CredentialModel.PASSWORD)) throw new ReadOnlyException("user is read only for this update");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,69 +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.examples.userstorage.readonly;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.storage.UserStorageProviderFactory;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class PropertyFileUserStorageProviderFactory implements UserStorageProviderFactory<PropertyFileUserStorageProvider> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(PropertyFileUserStorageProviderFactory.class);
|
||||
|
||||
public static final String PROVIDER_NAME = "readonly-property-file";
|
||||
|
||||
protected Properties properties = new Properties();
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_NAME;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
InputStream is = getClass().getClassLoader().getResourceAsStream("/users.properties");
|
||||
|
||||
if (is == null) {
|
||||
logger.warn("Could not find users.properties in classpath");
|
||||
} else {
|
||||
try {
|
||||
properties.load(is);
|
||||
} catch (IOException ex) {
|
||||
logger.error("Failed to load users.properties file", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyFileUserStorageProvider create(KeycloakSession session, ComponentModel model) {
|
||||
return new PropertyFileUserStorageProvider(session, model, properties);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,313 +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.examples.userstorage.writeable;
|
||||
|
||||
import org.keycloak.common.util.EnvUtil;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.credential.CredentialInput;
|
||||
import org.keycloak.credential.CredentialInputUpdater;
|
||||
import org.keycloak.credential.CredentialInputValidator;
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
|
||||
import org.keycloak.storage.user.UserLookupProvider;
|
||||
import org.keycloak.storage.user.UserQueryProvider;
|
||||
import org.keycloak.storage.user.UserRegistrationProvider;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class PropertyFileUserStorageProvider implements
|
||||
UserStorageProvider,
|
||||
UserLookupProvider,
|
||||
CredentialInputValidator,
|
||||
CredentialInputUpdater,
|
||||
UserRegistrationProvider,
|
||||
UserQueryProvider {
|
||||
|
||||
|
||||
public static final String UNSET_PASSWORD="#$!-UNSET-PASSWORD";
|
||||
|
||||
protected KeycloakSession session;
|
||||
protected Properties properties;
|
||||
protected ComponentModel model;
|
||||
// map of loaded users in this transaction
|
||||
protected Map<String, UserModel> loadedUsers = new HashMap<>();
|
||||
|
||||
public PropertyFileUserStorageProvider(KeycloakSession session, ComponentModel model, Properties properties) {
|
||||
this.session = session;
|
||||
this.model = model;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
// UserLookupProvider methods
|
||||
|
||||
@Override
|
||||
public UserModel getUserByUsername(String username, RealmModel realm) {
|
||||
UserModel adapter = loadedUsers.get(username);
|
||||
if (adapter == null) {
|
||||
String password = properties.getProperty(username);
|
||||
if (password != null) {
|
||||
adapter = createAdapter(realm, username);
|
||||
loadedUsers.put(username, adapter);
|
||||
}
|
||||
}
|
||||
return adapter;
|
||||
}
|
||||
|
||||
protected UserModel createAdapter(RealmModel realm, String username) {
|
||||
return new AbstractUserAdapterFederatedStorage(session, realm, model) {
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsername(String username) {
|
||||
String pw = (String)properties.remove(username);
|
||||
if (pw != null) {
|
||||
properties.put(username, pw);
|
||||
save();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserById(String id, RealmModel realm) {
|
||||
StorageId storageId = new StorageId(id);
|
||||
String username = storageId.getExternalId();
|
||||
return getUserByUsername(username, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByEmail(String email, RealmModel realm) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// UserQueryProvider methods
|
||||
|
||||
@Override
|
||||
public int getUsersCount(RealmModel realm) {
|
||||
return properties.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getUsers(RealmModel realm) {
|
||||
return getUsers(realm, 0, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
|
||||
List<UserModel> users = new LinkedList<>();
|
||||
int i = 0;
|
||||
for (Object obj : properties.keySet()) {
|
||||
if (i++ < firstResult) continue;
|
||||
String username = (String)obj;
|
||||
UserModel user = getUserByUsername(username, realm);
|
||||
users.add(user);
|
||||
if (users.size() >= maxResults) break;
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
// UserQueryProvider method implementations
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(String search, RealmModel realm) {
|
||||
return searchForUser(search, realm, 0, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
|
||||
List<UserModel> users = new LinkedList<>();
|
||||
int i = 0;
|
||||
for (Object obj : properties.keySet()) {
|
||||
String username = (String)obj;
|
||||
if (!username.contains(search)) continue;
|
||||
if (i++ < firstResult) continue;
|
||||
UserModel user = getUserByUsername(username, realm);
|
||||
users.add(user);
|
||||
if (users.size() >= maxResults) break;
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(Map<String, String> params, RealmModel realm) {
|
||||
return searchForUser(params, realm, 0, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(Map<String, String> params, RealmModel realm, int firstResult, int maxResults) {
|
||||
// only support searching by username
|
||||
String usernameSearchString = params.get("username");
|
||||
if (usernameSearchString == null) return Collections.EMPTY_LIST;
|
||||
return searchForUser(usernameSearchString, realm, firstResult, maxResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
|
||||
// runtime automatically handles querying UserFederatedStorage
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group) {
|
||||
// runtime automatically handles querying UserFederatedStorage
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getRoleMembers(RealmModel realm, RoleModel role, int firstResult, int maxResults) {
|
||||
// Not supported in federated storage
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getRoleMembers(RealmModel realm, RoleModel role) {
|
||||
// Not supported in federated storage
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) {
|
||||
// runtime automatically handles querying UserFederatedStorage
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
|
||||
// UserRegistrationProvider method implementations
|
||||
|
||||
public void save() {
|
||||
String path = model.getConfig().getFirst("path");
|
||||
path = EnvUtil.replace(path);
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(path);
|
||||
properties.store(fos, "");
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel addUser(RealmModel realm, String username) {
|
||||
synchronized (properties) {
|
||||
properties.setProperty(username, UNSET_PASSWORD);
|
||||
save();
|
||||
}
|
||||
return createAdapter(realm, username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeUser(RealmModel realm, UserModel user) {
|
||||
synchronized (properties) {
|
||||
if (properties.remove(user.getUsername()) == null) return false;
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// CredentialInputValidator methods
|
||||
|
||||
@Override
|
||||
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
|
||||
String password = properties.getProperty(user.getUsername());
|
||||
return credentialType.equals(CredentialModel.PASSWORD) && password != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCredentialType(String credentialType) {
|
||||
return credentialType.equals(CredentialModel.PASSWORD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
|
||||
if (!supportsCredentialType(input.getType()) || !(input instanceof UserCredentialModel)) return false;
|
||||
|
||||
UserCredentialModel cred = (UserCredentialModel)input;
|
||||
String password = properties.getProperty(user.getUsername());
|
||||
if (password == null || UNSET_PASSWORD.equals(password)) return false;
|
||||
return password.equals(cred.getValue());
|
||||
}
|
||||
|
||||
// CredentialInputUpdater methods
|
||||
|
||||
@Override
|
||||
public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
|
||||
if (!(input instanceof UserCredentialModel)) return false;
|
||||
if (!input.getType().equals(CredentialModel.PASSWORD)) return false;
|
||||
UserCredentialModel cred = (UserCredentialModel)input;
|
||||
synchronized (properties) {
|
||||
properties.setProperty(user.getUsername(), cred.getValue());
|
||||
save();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
|
||||
if (!credentialType.equals(CredentialModel.PASSWORD)) return;
|
||||
synchronized (properties) {
|
||||
properties.setProperty(user.getUsername(), UNSET_PASSWORD);
|
||||
save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final Set<String> disableableTypes = new HashSet<>();
|
||||
|
||||
static {
|
||||
disableableTypes.add(CredentialModel.PASSWORD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
|
||||
|
||||
return disableableTypes;
|
||||
}
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,99 +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.examples.userstorage.writeable;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.common.util.EnvUtil;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.component.ComponentValidationException;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
import org.keycloak.storage.UserStorageProviderFactory;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class PropertyFileUserStorageProviderFactory implements UserStorageProviderFactory<PropertyFileUserStorageProvider> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(PropertyFileUserStorageProviderFactory.class);
|
||||
|
||||
public static final String PROVIDER_NAME = "writeable-property-file";
|
||||
|
||||
protected static final List<ProviderConfigProperty> configMetadata;
|
||||
|
||||
static {
|
||||
configMetadata = ProviderConfigurationBuilder.create()
|
||||
.property().name("path")
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.label("Path")
|
||||
.defaultValue("${jboss.server.config.dir}/example-users.properties")
|
||||
.helpText("File path to properties file")
|
||||
.add().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateConfiguration(KeycloakSession session, RealmModel realm, ComponentModel config) throws ComponentValidationException {
|
||||
String fp = config.getConfig().getFirst("path");
|
||||
if (fp == null) throw new ComponentValidationException("user property file does not exist");
|
||||
fp = EnvUtil.replace(fp);
|
||||
File file = new File(fp);
|
||||
if (!file.exists()) {
|
||||
throw new ComponentValidationException("user property file does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyFileUserStorageProvider create(KeycloakSession session, ComponentModel model) {
|
||||
String path = model.getConfig().getFirst("path");
|
||||
path = EnvUtil.replace(path);
|
||||
|
||||
Properties props = new Properties();
|
||||
try {
|
||||
InputStream is = new FileInputStream(path);
|
||||
props.load(is);
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return new PropertyFileUserStorageProvider(session, model, props);
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
org.keycloak.examples.userstorage.readonly.PropertyFileUserStorageProviderFactory
|
||||
org.keycloak.examples.userstorage.writeable.PropertyFileUserStorageProviderFactory
|
|
@ -1,18 +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.
|
||||
#
|
||||
|
||||
tbrady=superbowl
|
5
pom.xml
5
pom.xml
|
@ -1399,11 +1399,6 @@
|
|||
<classifier>classes</classifier>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>user-storage-properties-example</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.example.demo</groupId>
|
||||
<artifactId>cxf-jaxws-example</artifactId>
|
||||
|
|
|
@ -158,10 +158,6 @@
|
|||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-jaxrs-oauth-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>user-storage-properties-example</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-testsuite-utils</artifactId>
|
||||
|
|
|
@ -171,11 +171,6 @@
|
|||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-jaxrs-oauth-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>user-storage-properties-example</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Dependency on services from integration-arquillian -->
|
||||
<dependency>
|
||||
<groupId>org.keycloak.testsuite</groupId>
|
||||
|
|
Loading…
Reference in a new issue