keycloak-aplcache
Changes
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java 22(+22 -0)
model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialAttributeEntity.java 111(+111 -0)
model/jpa/src/main/java/org/keycloak/storage/jpa/entity/FederatedUserCredentialAttributeEntity.java 113(+113 -0)
model/jpa/src/main/java/org/keycloak/storage/jpa/entity/FederatedUserCredentialEntity.java 15(+15 -0)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java 21(+21 -0)
Details
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
index 8a62c33..3d966d1 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
@@ -20,6 +20,7 @@ package org.keycloak.models.cache.infinispan;
import org.jboss.logging.Logger;
import org.keycloak.common.constants.ServiceAccountConstants;
import org.keycloak.component.ComponentModel;
+import org.keycloak.credential.CredentialInput;
import org.keycloak.models.ClientModel;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.FederatedIdentityModel;
@@ -669,4 +670,25 @@ public class UserCacheSession implements CacheUserProvider {
getDelegate().preRemove(realm, component);
}
+
+ @Override
+ public boolean isValid(RealmModel realm, UserModel user, List<CredentialInput> inputs) {
+ return getDelegate().isValid(realm, user, inputs);
+ }
+
+ @Override
+ public void updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
+ getDelegate().updateCredential(realm, user, input);
+ }
+
+ @Override
+ public boolean isConfiguredFor(RealmModel realm, UserModel user, String type) {
+ return getDelegate().isConfiguredFor(realm, user, type);
+ }
+
+ @Override
+ public Set<String> requiredActionsFor(RealmModel realm, UserModel user, String type) {
+ return getDelegate().requiredActionsFor(realm, user, type);
+ }
+
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialAttributeEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialAttributeEntity.java
new file mode 100755
index 0000000..f4ceb94
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialAttributeEntity.java
@@ -0,0 +1,111 @@
+/*
+ * 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.Access;
+import javax.persistence.AccessType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+ @NamedQuery(name="getCredentialAttribute", query="select attr from CredentialAttributeEntity attr where attr.credential = :credential"),
+ @NamedQuery(name="deleteCredentialAttributeByCredential", query="delete from CredentialAttributeEntity attr where attr.credential = :credential"),
+ @NamedQuery(name="deleteCredentialAttributeByRealm", query="delete from CredentialAttributeEntity attr where attr.credential IN (select cred from CredentialEntity cred where cred.user IN (select u from UserEntity u where u.realmId=:realmId))"),
+ @NamedQuery(name="deleteCredentialAttributeByRealmAndLink", query="delete from CredentialAttributeEntity attr where attr.credential IN (select cred from CredentialEntity cred where cred.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link))"),
+ @NamedQuery(name="deleteCredentialAttributeByUser", query="delete from CredentialAttributeEntity attr where attr.credential IN (select cred from CredentialEntity cred where cred.user = :user)"),
+})
+@Table(name="CREDENTIAL_ATTRIBUTE")
+@Entity
+public class CredentialAttributeEntity {
+
+ @Id
+ @Column(name="ID", length = 36)
+ @Access(AccessType.PROPERTY) // we do this because relationships often fetch id, but not entity. This avoids an extra SQL
+ protected String id;
+
+ @ManyToOne(fetch= FetchType.LAZY)
+ @JoinColumn(name = "CREDENTIAL_ID")
+ protected CredentialEntity credential;
+
+ @Column(name = "NAME")
+ protected String name;
+ @Column(name = "VALUE")
+ protected String value;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public CredentialEntity getCredential() {
+ return credential;
+ }
+
+ public void setCredential(CredentialEntity credential) {
+ this.credential = credential;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null) return false;
+ if (!(o instanceof CredentialAttributeEntity)) return false;
+
+ CredentialAttributeEntity that = (CredentialAttributeEntity) o;
+
+ if (!id.equals(that.getId())) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java
index ceb284c..2d048ee 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java
@@ -19,6 +19,7 @@ package org.keycloak.models.jpa.entities;
import javax.persistence.Access;
import javax.persistence.AccessType;
+import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
@@ -27,7 +28,10 @@ import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
import javax.persistence.Table;
+import java.util.ArrayList;
+import java.util.Collection;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -74,6 +78,8 @@ public class CredentialEntity {
@Column(name="PERIOD")
protected int period;
+ @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER, orphanRemoval = true, mappedBy="credential")
+ protected Collection<CredentialAttributeEntity> credentialAttributes = new ArrayList<>();
public String getId() {
return id;
@@ -171,6 +177,14 @@ public class CredentialEntity {
this.period = period;
}
+ public Collection<CredentialAttributeEntity> getCredentialAttributes() {
+ return credentialAttributes;
+ }
+
+ public void setCredentialAttributes(Collection<CredentialAttributeEntity> credentialAttributes) {
+ this.credentialAttributes = credentialAttributes;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
index 0d938d6..30752d3 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
@@ -18,6 +18,7 @@
package org.keycloak.models.jpa;
import org.keycloak.component.ComponentModel;
+import org.keycloak.credential.CredentialInput;
import org.keycloak.models.ClientModel;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.FederatedIdentityModel;
@@ -48,6 +49,7 @@ import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -367,6 +369,8 @@ public class JpaUserProvider implements UserProvider {
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteFederatedIdentityByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
+ num = em.createNamedQuery("deleteCredentialAttributeByRealm")
+ .setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteCredentialsByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteUserAttributesByRealm")
@@ -391,6 +395,10 @@ public class JpaUserProvider implements UserProvider {
.setParameter("realmId", realm.getId())
.setParameter("link", link.getId())
.executeUpdate();
+ num = em.createNamedQuery("deleteCredentialAttributeByRealmAndLink")
+ .setParameter("realmId", realm.getId())
+ .setParameter("link", link.getId())
+ .executeUpdate();
num = em.createNamedQuery("deleteCredentialsByRealmAndLink")
.setParameter("realmId", realm.getId())
.setParameter("link", link.getId())
@@ -716,4 +724,24 @@ public class JpaUserProvider implements UserProvider {
public void preRemove(RealmModel realm, ComponentModel component) {
}
+ @Override
+ public boolean isValid(RealmModel realm, UserModel user, List<CredentialInput> inputs) {
+ return false;
+ }
+
+ @Override
+ public void updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
+
+ }
+
+ @Override
+ public boolean isConfiguredFor(RealmModel realm, UserModel user, String type) {
+ return false;
+ }
+
+ @Override
+ public Set<String> requiredActionsFor(RealmModel realm, UserModel user, String type) {
+ return Collections.EMPTY_SET;
+ }
+
}
diff --git a/model/jpa/src/main/java/org/keycloak/storage/jpa/entity/FederatedUserCredentialAttributeEntity.java b/model/jpa/src/main/java/org/keycloak/storage/jpa/entity/FederatedUserCredentialAttributeEntity.java
new file mode 100755
index 0000000..c6a15b3
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/storage/jpa/entity/FederatedUserCredentialAttributeEntity.java
@@ -0,0 +1,113 @@
+/*
+ * 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.jpa.entity;
+
+import org.keycloak.models.jpa.entities.CredentialEntity;
+
+import javax.persistence.Access;
+import javax.persistence.AccessType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+ @NamedQuery(name="deleteFederatedCredentialAttributeByCredential", query="delete from FederatedUserCredentialAttributeEntity attr where attr.credential = :credential"),
+ @NamedQuery(name="deleteFederatedCredentialAttributeByStorageProvider", query="delete from FederatedUserCredentialAttributeEntity attr where attr.credential IN (select cred from FederatedUserCredentialEntity cred where cred.storageProviderId=:storageProviderId)"),
+ @NamedQuery(name="deleteFederatedCredentialAttributeByRealm", query="delete from FederatedUserCredentialAttributeEntity attr where attr.credential IN (select cred from FederatedUserCredentialEntity cred where cred.realmId=:realmId)"),
+ @NamedQuery(name="deleteFederatedCredentialAttributeByRealmAndLink", query="delete from FederatedUserCredentialAttributeEntity attr where attr.credential IN (select cred from FederatedUserCredentialEntity cred where cred.userId IN (select u.id from UserEntity u where u.realmId=:realmId and u.federationLink=:link))"),
+ @NamedQuery(name="deleteFederatedCredentialAttributeByUser", query="delete from FederatedUserCredentialAttributeEntity attr where attr.credential IN (select cred from FederatedUserCredentialEntity cred where cred.userId = :userId and cred.realmId = :realmId)"),
+})
+@Table(name="FED_CREDENTIAL_ATTRIBUTE")
+@Entity
+public class FederatedUserCredentialAttributeEntity {
+
+ @Id
+ @Column(name="ID", length = 36)
+ @Access(AccessType.PROPERTY) // we do this because relationships often fetch id, but not entity. This avoids an extra SQL
+ protected String id;
+
+ @ManyToOne(fetch= FetchType.LAZY)
+ @JoinColumn(name = "CREDENTIAL_ID")
+ protected FederatedUserCredentialEntity credential;
+
+ @Column(name = "NAME")
+ protected String name;
+ @Column(name = "VALUE")
+ protected String value;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public FederatedUserCredentialEntity getCredential() {
+ return credential;
+ }
+
+ public void setCredential(FederatedUserCredentialEntity credential) {
+ this.credential = credential;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null) return false;
+ if (!(o instanceof FederatedUserCredentialAttributeEntity)) return false;
+
+ FederatedUserCredentialAttributeEntity that = (FederatedUserCredentialAttributeEntity) o;
+
+ if (!id.equals(that.getId())) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/storage/jpa/entity/FederatedUserCredentialEntity.java b/model/jpa/src/main/java/org/keycloak/storage/jpa/entity/FederatedUserCredentialEntity.java
index 996608b..da305b4 100755
--- a/model/jpa/src/main/java/org/keycloak/storage/jpa/entity/FederatedUserCredentialEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/storage/jpa/entity/FederatedUserCredentialEntity.java
@@ -17,10 +17,12 @@
package org.keycloak.storage.jpa.entity;
+import org.keycloak.models.jpa.entities.CredentialEntity;
import org.keycloak.models.jpa.entities.UserEntity;
import javax.persistence.Access;
import javax.persistence.AccessType;
+import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
@@ -29,7 +31,10 @@ import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
import javax.persistence.Table;
+import java.util.ArrayList;
+import java.util.Collection;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -87,6 +92,8 @@ public class FederatedUserCredentialEntity {
protected int digits;
@Column(name="PERIOD")
protected int period;
+ @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER, orphanRemoval = true, mappedBy="credential")
+ protected Collection<FederatedUserCredentialAttributeEntity> credentialAttributes = new ArrayList<>();
public String getId() {
@@ -201,6 +208,14 @@ public class FederatedUserCredentialEntity {
this.period = period;
}
+ public Collection<FederatedUserCredentialAttributeEntity> getCredentialAttributes() {
+ return credentialAttributes;
+ }
+
+ public void setCredentialAttributes(Collection<FederatedUserCredentialAttributeEntity> credentialAttributes) {
+ this.credentialAttributes = credentialAttributes;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java b/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java
index f6710dd..d9d7de9 100644
--- a/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java
@@ -18,6 +18,7 @@ package org.keycloak.storage.jpa;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentModel;
+import org.keycloak.credential.CredentialModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.GroupModel;
@@ -607,6 +608,46 @@ public class JpaUserFederatedStorageProvider implements
}
@Override
+ public void updateCredential(RealmModel realm, UserModel user, CredentialModel cred) {
+
+ }
+
+ @Override
+ public CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred) {
+ return null;
+ }
+
+ @Override
+ public boolean removeCredential(RealmModel realm, String id) {
+ return false;
+ }
+
+ @Override
+ public CredentialModel getCredentialById(String id) {
+ return null;
+ }
+
+ @Override
+ public List<CredentialModel> getCredentials(RealmModel realm) {
+ return null;
+ }
+
+ @Override
+ public List<CredentialModel> getUserCredentials(RealmModel realm, UserModel user) {
+ return null;
+ }
+
+ @Override
+ public List<CredentialModel> getCredentialsByType(RealmModel realm, UserModel user, String type) {
+ return null;
+ }
+
+ @Override
+ public CredentialModel getCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type) {
+ return null;
+ }
+
+ @Override
public void preRemove(RealmModel realm) {
int num = em.createNamedQuery("deleteFederatedUserConsentRolesByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
@@ -620,6 +661,8 @@ public class JpaUserFederatedStorageProvider implements
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteBrokerLinkByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
+ num = em.createNamedQuery("deleteFederatedCredentialAttributeByRealm")
+ .setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteFederatedUserCredentialsByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteUserFederatedAttributesByRealm")
@@ -642,6 +685,10 @@ public class JpaUserFederatedStorageProvider implements
.setParameter("realmId", realm.getId())
.setParameter("link", link.getId())
.executeUpdate();
+ num = em.createNamedQuery("deleteFederatedCredentialAttributeByRealmAndLink")
+ .setParameter("realmId", realm.getId())
+ .setParameter("link", link.getId())
+ .executeUpdate();
num = em.createNamedQuery("deleteFederatedUserCredentialsByRealmAndLink")
.setParameter("realmId", realm.getId())
.setParameter("link", link.getId())
@@ -699,6 +746,10 @@ public class JpaUserFederatedStorageProvider implements
.setParameter("userId", user.getId())
.setParameter("realmId", realm.getId())
.executeUpdate();
+ em.createNamedQuery("deleteFederatedCredentialAttributeByUser")
+ .setParameter("userId", user.getId())
+ .setParameter("realmId", realm.getId())
+ .executeUpdate();
em.createNamedQuery("deleteFederatedUserCredentialByUser")
.setParameter("userId", user.getId())
.setParameter("realmId", realm.getId())
@@ -737,6 +788,9 @@ public class JpaUserFederatedStorageProvider implements
em.createNamedQuery("deleteFederatedUserConsentsByStorageProvider")
.setParameter("storageProviderId", model.getId())
.executeUpdate();
+ em.createNamedQuery("deleteFederatedCredentialAttributeByStorageProvider")
+ .setParameter("storageProviderId", model.getId())
+ .executeUpdate();
em.createNamedQuery("deleteFederatedUserCredentialsByStorageProvider")
.setParameter("storageProviderId", model.getId())
.executeUpdate();
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-2.2.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.2.0.xml
index 63afbb2..a9b6078 100755
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-2.2.0.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.2.0.xml
@@ -24,4 +24,39 @@
</addColumn>
</changeSet>
+ <changeSet author="bburke@redhat.com" id="2.2.0">
+ <createTable tableName="CREDENTIAL_ATTRIBUTE">
+ <column name="ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="CREDENTIAL_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="NAME" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="VALUE" type="VARCHAR(4000)"/>
+ </createTable>
+
+ <createTable tableName="FED_CREDENTIAL_ATTRIBUTE">
+ <column name="ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="CREDENTIAL_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="NAME" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="VALUE" type="VARCHAR(4000)"/>
+ </createTable>
+ <modifyDataType tableName="CREDENTIAL" columnName="VALUE" newDataType="VARCHAR(4000)"/>
+
+ <addForeignKeyConstraint baseColumnNames="CREDENTIAL_ID" baseTableName="FED_CREDENTIAL_ATTRIBUTE" constraintName="FK_FED_CRED_ATTR" referencedColumnNames="ID" referencedTableName="FED_USER_CREDENTIAL"/>
+ <addForeignKeyConstraint baseColumnNames="CREDENTIAL_ID" baseTableName="CREDENTIAL_ATTRIBUTE" constraintName="FK_CRED_ATTR" referencedColumnNames="ID" referencedTableName="CREDENTIAL"/>
+
+
+ </changeSet>
+
+
</databaseChangeLog>
\ No newline at end of file
diff --git a/model/jpa/src/main/resources/META-INF/persistence.xml b/model/jpa/src/main/resources/META-INF/persistence.xml
index 0b2ff23..6288fe5 100755
--- a/model/jpa/src/main/resources/META-INF/persistence.xml
+++ b/model/jpa/src/main/resources/META-INF/persistence.xml
@@ -22,6 +22,7 @@
<persistence-unit name="keycloak-default" transaction-type="RESOURCE_LOCAL">
<class>org.keycloak.models.jpa.entities.ClientEntity</class>
<class>org.keycloak.models.jpa.entities.CredentialEntity</class>
+ <class>org.keycloak.models.jpa.entities.CredentialAttributeEntity</class>
<class>org.keycloak.models.jpa.entities.RealmEntity</class>
<class>org.keycloak.models.jpa.entities.RealmAttributeEntity</class>
<class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
@@ -74,6 +75,7 @@
<class>org.keycloak.storage.jpa.entity.FederatedUserConsentRoleEntity</class>
<class>org.keycloak.storage.jpa.entity.FederatedUserConsentProtocolMapperEntity</class>
<class>org.keycloak.storage.jpa.entity.FederatedUserCredentialEntity</class>
+ <class>org.keycloak.storage.jpa.entity.FederatedUserCredentialAttributeEntity</class>
<class>org.keycloak.storage.jpa.entity.FederatedUserGroupMembershipEntity</class>
<class>org.keycloak.storage.jpa.entity.FederatedUserRequiredActionEntity</class>
<class>org.keycloak.storage.jpa.entity.FederatedUserRoleMappingEntity</class>
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
index c0537ba..3888c7a 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
@@ -24,6 +24,7 @@ import com.mongodb.QueryBuilder;
import org.keycloak.component.ComponentModel;
import org.keycloak.connections.mongo.api.MongoStore;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.credential.CredentialInput;
import org.keycloak.models.ClientModel;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.FederatedIdentityModel;
@@ -635,4 +636,24 @@ public class MongoUserProvider implements UserProvider {
public void preRemove(RealmModel realm, ComponentModel component) {
}
+
+ @Override
+ public boolean isValid(RealmModel realm, UserModel user, List<CredentialInput> inputs) {
+ return false;
+ }
+
+ @Override
+ public void updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
+
+ }
+
+ @Override
+ public boolean isConfiguredFor(RealmModel realm, UserModel user, String type) {
+ return false;
+ }
+
+ @Override
+ public Set<String> requiredActionsFor(RealmModel realm, UserModel user, String type) {
+ return Collections.EMPTY_SET;
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/component/PrioritizedComponentModel.java b/server-spi/src/main/java/org/keycloak/component/PrioritizedComponentModel.java
new file mode 100644
index 0000000..7a0393a
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/component/PrioritizedComponentModel.java
@@ -0,0 +1,58 @@
+/*
+ * 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.component;
+
+import org.keycloak.component.ComponentModel;
+import org.keycloak.storage.UserStorageProviderModel;
+
+import java.util.Comparator;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class PrioritizedComponentModel extends ComponentModel {
+ public static Comparator<ComponentModel> comparator = new Comparator<ComponentModel>() {
+ @Override
+ public int compare(ComponentModel o1, ComponentModel o2) {
+ return parsePriority(o1) - parsePriority(o2);
+ }
+ };
+
+ public PrioritizedComponentModel(ComponentModel copy) {
+ super(copy);
+ }
+
+ public PrioritizedComponentModel() {
+ }
+
+ public static int parsePriority(ComponentModel component) {
+ String priority = component.getConfig().getFirst("priority");
+ if (priority == null) return 0;
+ return Integer.valueOf(priority);
+
+ }
+
+ public int getPriority() {
+ return parsePriority(this);
+
+ }
+
+ public void setPriority(int priority) {
+ getConfig().putSingle("priority", Integer.toString(priority));
+ }
+}
diff --git a/server-spi/src/main/java/org/keycloak/credential/CredentialInput.java b/server-spi/src/main/java/org/keycloak/credential/CredentialInput.java
new file mode 100644
index 0000000..805fb25
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/credential/CredentialInput.java
@@ -0,0 +1,25 @@
+/*
+ * 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.credential;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface CredentialInput {
+ String getType();
+}
diff --git a/server-spi/src/main/java/org/keycloak/credential/CredentialInputUpdater.java b/server-spi/src/main/java/org/keycloak/credential/CredentialInputUpdater.java
new file mode 100644
index 0000000..e24870c
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/credential/CredentialInputUpdater.java
@@ -0,0 +1,32 @@
+/*
+ * 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.credential;
+
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface CredentialInputUpdater {
+ boolean supportsCredentialType(String credentialType);
+ Set<String> requiredActionsFor(RealmModel realm, UserModel user, String credentialType);
+ void updateCredential(RealmModel realm, UserModel user, CredentialInput input);
+}
diff --git a/server-spi/src/main/java/org/keycloak/credential/CredentialInputValidator.java b/server-spi/src/main/java/org/keycloak/credential/CredentialInputValidator.java
new file mode 100644
index 0000000..a7a4c6d
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/credential/CredentialInputValidator.java
@@ -0,0 +1,35 @@
+/*
+ * 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.credential;
+
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.provider.Provider;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface CredentialInputValidator {
+ boolean supportsCredentialType(String credentialType);
+ boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType);
+ boolean isValid(RealmModel realm, UserModel user, CredentialInput input);
+
+}
diff --git a/server-spi/src/main/java/org/keycloak/credential/CredentialModel.java b/server-spi/src/main/java/org/keycloak/credential/CredentialModel.java
new file mode 100755
index 0000000..082c64b
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/credential/CredentialModel.java
@@ -0,0 +1,144 @@
+/*
+ * 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.credential;
+
+import org.keycloak.common.util.MultivaluedHashMap;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Used just in cases when we want to "directly" update or retrieve the hash or salt of user credential (For example during export/import)
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class CredentialModel implements Serializable {
+ private String id;
+ private String type;
+ private String value;
+ private String device;
+ private byte[] salt;
+ private int hashIterations;
+ private Long createdDate;
+
+ // otp stuff
+ private int counter;
+ private String algorithm;
+ private int digits;
+ private int period;
+ private MultivaluedHashMap<String, String> config;
+
+
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getDevice() {
+ return device;
+ }
+
+ public void setDevice(String device) {
+ this.device = device;
+ }
+
+ public byte[] getSalt() {
+ return salt;
+ }
+
+ public void setSalt(byte[] salt) {
+ this.salt = salt;
+ }
+
+ public int getHashIterations() {
+ return hashIterations;
+ }
+
+ public void setHashIterations(int iterations) {
+ this.hashIterations = iterations;
+ }
+
+ public Long getCreatedDate() {
+ return createdDate;
+ }
+
+ public void setCreatedDate(Long createdDate) {
+ this.createdDate = createdDate;
+ }
+
+ public int getCounter() {
+ return counter;
+ }
+
+ public void setCounter(int counter) {
+ this.counter = counter;
+ }
+
+ public String getAlgorithm() {
+ return algorithm;
+ }
+
+ public void setAlgorithm(String algorithm) {
+ this.algorithm = algorithm;
+ }
+
+ public int getDigits() {
+ return digits;
+ }
+
+ public void setDigits(int digits) {
+ this.digits = digits;
+ }
+
+ public int getPeriod() {
+ return period;
+ }
+
+ public void setPeriod(int period) {
+ this.period = period;
+ }
+
+ public MultivaluedHashMap<String, String> getConfig() {
+ return config;
+ }
+
+ public void setConfig(MultivaluedHashMap<String, String> config) {
+ this.config = config;
+ }
+}
diff --git a/server-spi/src/main/java/org/keycloak/credential/CredentialProvider.java b/server-spi/src/main/java/org/keycloak/credential/CredentialProvider.java
new file mode 100644
index 0000000..a830433
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/credential/CredentialProvider.java
@@ -0,0 +1,31 @@
+/*
+ * 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.credential;
+
+import org.keycloak.provider.Provider;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface CredentialProvider extends Provider {
+ @Override
+ default
+ void close() {
+
+ }
+}
diff --git a/server-spi/src/main/java/org/keycloak/credential/CredentialProviderFactory.java b/server-spi/src/main/java/org/keycloak/credential/CredentialProviderFactory.java
new file mode 100755
index 0000000..480dd1c
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/credential/CredentialProviderFactory.java
@@ -0,0 +1,85 @@
+/*
+ * 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.credential;
+
+import org.keycloak.Config;
+import org.keycloak.component.ComponentFactory;
+import org.keycloak.component.ComponentModel;
+import org.keycloak.component.ComponentValidationException;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.storage.UserStorageProvider;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface CredentialProviderFactory<T extends CredentialProvider> extends ComponentFactory<T, CredentialProvider> {
+ /**
+ * called per Keycloak transaction.
+ *
+ * @param session
+ * @param model
+ * @return
+ */
+ T create(KeycloakSession session, ComponentModel model);
+
+ /**
+ * This is the name of the provider and will be showed in the admin console as an option.
+ *
+ * @return
+ */
+ @Override
+ String getId();
+
+ @Override
+ default void init(Config.Scope config) {
+
+ }
+
+ @Override
+ default void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ @Override
+ default void close() {
+
+ }
+
+ @Override
+ default String getHelpText() {
+ return "";
+ }
+
+ @Override
+ default List<ProviderConfigProperty> getConfigProperties() {
+ return Collections.EMPTY_LIST;
+ }
+
+ @Override
+ default void validateConfiguration(KeycloakSession session, ComponentModel config) throws ComponentValidationException {
+
+ }
+
+}
diff --git a/server-spi/src/main/java/org/keycloak/credential/UserCredentialStore.java b/server-spi/src/main/java/org/keycloak/credential/UserCredentialStore.java
new file mode 100644
index 0000000..df497f1
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/credential/UserCredentialStore.java
@@ -0,0 +1,38 @@
+/*
+ * 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.credential;
+
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.provider.Provider;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface UserCredentialStore extends Provider {
+ void updateCredential(RealmModel realm, UserModel user, CredentialModel cred);
+ CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred);
+ boolean removeCredential(RealmModel realm, String id);
+ CredentialModel getCredentialById(String id);
+ List<CredentialModel> getCredentials(RealmModel realm);
+ List<CredentialModel> getCredentials(RealmModel realm, UserModel user);
+ List<CredentialModel> getCredentialsByType(RealmModel realm, UserModel user, String type);
+ CredentialModel getCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type);
+}
diff --git a/server-spi/src/main/java/org/keycloak/models/UserCredentialModel.java b/server-spi/src/main/java/org/keycloak/models/UserCredentialModel.java
index 3e03508..fa96707 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserCredentialModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserCredentialModel.java
@@ -17,13 +17,15 @@
package org.keycloak.models;
+import org.keycloak.credential.CredentialInput;
+
import java.util.UUID;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
-public class UserCredentialModel {
+public class UserCredentialModel implements CredentialInput {
public static final String PASSWORD = "password";
public static final String PASSWORD_HISTORY = "password-history";
public static final String PASSWORD_TOKEN = "password-token";
diff --git a/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java b/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java
index cb6867a..46c748e 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java
@@ -19,6 +19,7 @@ package org.keycloak.models;
import org.jboss.logging.Logger;
import org.keycloak.component.ComponentModel;
+import org.keycloak.credential.CredentialInput;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.policy.PasswordPolicyManagerProvider;
import org.keycloak.policy.PolicyError;
@@ -445,6 +446,27 @@ public class UserFederationManager implements UserProvider {
}
@Override
+ public boolean isValid(RealmModel realm, UserModel user, List<CredentialInput> inputs) {
+ return session.userStorage().isValid(realm, user, inputs);
+ }
+
+ @Override
+ public void updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
+ session.userStorage().updateCredential(realm, user, input);
+
+ }
+
+ @Override
+ public boolean isConfiguredFor(RealmModel realm, UserModel user, String type) {
+ return session.userStorage().isConfiguredFor(realm, user, type);
+ }
+
+ @Override
+ public Set<String> requiredActionsFor(RealmModel realm, UserModel user, String type) {
+ return session.userStorage().requiredActionsFor(realm, user, type);
+ }
+
+ @Override
public void preRemove(RealmModel realm) {
for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
UserFederationProvider fed = getFederationProvider(federation);
diff --git a/server-spi/src/main/java/org/keycloak/models/UserProvider.java b/server-spi/src/main/java/org/keycloak/models/UserProvider.java
index d6ef2cd..5dbc04e 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserProvider.java
@@ -18,6 +18,7 @@
package org.keycloak.models;
import org.keycloak.component.ComponentModel;
+import org.keycloak.credential.CredentialInput;
import org.keycloak.provider.Provider;
import org.keycloak.storage.user.UserCredentialValidatorProvider;
import org.keycloak.storage.user.UserLookupProvider;
@@ -85,4 +86,12 @@ public interface UserProvider extends Provider,
void close();
void preRemove(RealmModel realm, ComponentModel component);
+
+ boolean isValid(RealmModel realm, UserModel user, List<CredentialInput> inputs);
+
+ void updateCredential(RealmModel realm, UserModel user, CredentialInput input);
+
+ boolean isConfiguredFor(RealmModel realm, UserModel user, String type);
+
+ Set<String> requiredActionsFor(RealmModel realm, UserModel user, String type);
}
diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserCredentialsFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserCredentialsFederatedStorage.java
index 7239182..2202f62 100644
--- a/server-spi/src/main/java/org/keycloak/storage/federated/UserCredentialsFederatedStorage.java
+++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserCredentialsFederatedStorage.java
@@ -16,6 +16,7 @@
*/
package org.keycloak.storage.federated;
+import org.keycloak.credential.CredentialModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
@@ -28,8 +29,20 @@ import java.util.List;
* @version $Revision: 1 $
*/
public interface UserCredentialsFederatedStorage {
+ // deprecated
void updateCredential(RealmModel realm, UserModel user, UserCredentialModel cred);
void updateCredential(RealmModel realm, UserModel user, UserCredentialValueModel cred);
void removeCredential(RealmModel realm, UserModel user, UserCredentialValueModel cred);
List<UserCredentialValueModel> getCredentials(RealmModel realm, UserModel user);
+
+ // new
+ void updateCredential(RealmModel realm, UserModel user, CredentialModel cred);
+ CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred);
+ boolean removeCredential(RealmModel realm, String id);
+ CredentialModel getCredentialById(String id);
+ List<CredentialModel> getCredentials(RealmModel realm);
+ List<CredentialModel> getUserCredentials(RealmModel realm, UserModel user);
+ List<CredentialModel> getCredentialsByType(RealmModel realm, UserModel user, String type);
+ CredentialModel getCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type);
+
}
diff --git a/server-spi/src/main/java/org/keycloak/storage/UserStorageManager.java b/server-spi/src/main/java/org/keycloak/storage/UserStorageManager.java
index 1a1fca5..6620f49 100755
--- a/server-spi/src/main/java/org/keycloak/storage/UserStorageManager.java
+++ b/server-spi/src/main/java/org/keycloak/storage/UserStorageManager.java
@@ -20,6 +20,12 @@ package org.keycloak.storage;
import org.jboss.logging.Logger;
import org.keycloak.common.util.reflections.Types;
import org.keycloak.component.ComponentModel;
+import org.keycloak.component.PrioritizedComponentModel;
+import org.keycloak.credential.CredentialInput;
+import org.keycloak.credential.CredentialInputUpdater;
+import org.keycloak.credential.CredentialInputValidator;
+import org.keycloak.credential.CredentialProvider;
+import org.keycloak.credential.CredentialProviderFactory;
import org.keycloak.models.ClientModel;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.FederatedIdentityModel;
@@ -85,7 +91,7 @@ public class UserStorageManager implements UserProvider {
return null;
}
- private UserStorageProvider getStorageProviderInstance(UserStorageProviderModel model, UserStorageProviderFactory factory) {
+ protected UserStorageProvider getStorageProviderInstance(UserStorageProviderModel model, UserStorageProviderFactory factory) {
UserStorageProvider instance = (UserStorageProvider)session.getAttribute(model.getId());
if (instance != null) return instance;
instance = factory.create(session, model);
@@ -609,4 +615,151 @@ public class UserStorageManager implements UserProvider {
@Override
public void close() {
}
+
+ @Override
+ public boolean isValid(RealmModel realm, UserModel user, List<CredentialInput> inputs) {
+
+ List<CredentialInput> toValidate = new LinkedList<>();
+ toValidate.addAll(inputs);
+ if (!StorageId.isLocalStorage(user)) {
+ String providerId = StorageId.resolveProviderId(user);
+ UserStorageProvider provider = getStorageProvider(realm, providerId);
+ if (provider instanceof CredentialInputValidator) {
+ Iterator<CredentialInput> it = toValidate.iterator();
+ while (it.hasNext()) {
+ CredentialInput input = it.next();
+ CredentialInputValidator validator = (CredentialInputValidator)provider;
+ if (validator.supportsCredentialType(input.getType()) && validator.isValid(realm, user, input)) {
+ it.remove();
+ }
+ }
+ }
+ }
+
+ if (toValidate.isEmpty()) return true;
+
+ List<ComponentModel> components = getCredentialProviderComponents(realm);
+ for (ComponentModel component : components) {
+ CredentialProviderFactory factory = (CredentialProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(CredentialProvider.class, component.getProviderId());
+ if (!Types.supports(CredentialInputValidator.class, factory, CredentialProviderFactory.class)) continue;
+ Iterator<CredentialInput> it = toValidate.iterator();
+ while (it.hasNext()) {
+ CredentialInput input = it.next();
+ CredentialInputValidator validator = (CredentialInputValidator)session.getAttribute(component.getId());
+ if (validator == null) {
+ validator = (CredentialInputValidator)factory.create(session, component);
+ session.setAttribute(component.getId(), validator);
+ }
+ if (validator.supportsCredentialType(input.getType()) && validator.isValid(realm, user, input)) {
+ it.remove();
+ }
+ }
+ }
+
+ return toValidate.isEmpty();
+ }
+
+ protected List<ComponentModel> getCredentialProviderComponents(RealmModel realm) {
+ List<ComponentModel> components = realm.getComponents(realm.getId(), CredentialProvider.class.getName());
+ Collections.sort(components, PrioritizedComponentModel.comparator);
+ return components;
+ }
+
+ @Override
+ public void updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
+ if (!StorageId.isLocalStorage(user)) {
+ String providerId = StorageId.resolveProviderId(user);
+ UserStorageProvider provider = getStorageProvider(realm, providerId);
+ if (provider instanceof CredentialInputUpdater) {
+ CredentialInputUpdater updater = (CredentialInputUpdater)provider;
+ if (updater.supportsCredentialType(input.getType())) {
+ updater.updateCredential(realm, user, input);
+ return;
+ }
+ }
+ }
+
+ List<ComponentModel> components = getCredentialProviderComponents(realm);
+ for (ComponentModel component : components) {
+ CredentialProviderFactory factory = (CredentialProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(CredentialProvider.class, component.getProviderId());
+ if (!Types.supports(CredentialInputUpdater.class, factory, CredentialProviderFactory.class)) continue;
+ CredentialInputUpdater updater = (CredentialInputUpdater)session.getAttribute(component.getId());
+ if (updater == null) {
+ updater = (CredentialInputUpdater)factory.create(session, component);
+ session.setAttribute(component.getId(), updater);
+ }
+ if (!updater.supportsCredentialType(input.getType())) continue;
+ updater.updateCredential(realm, user, input);
+ return;
+ }
+
+ }
+ @Override
+ public boolean isConfiguredFor(RealmModel realm, UserModel user, String type) {
+ if (!StorageId.isLocalStorage(user)) {
+ String providerId = StorageId.resolveProviderId(user);
+ UserStorageProvider provider = getStorageProvider(realm, providerId);
+ if (provider instanceof CredentialInputValidator) {
+ CredentialInputValidator validator = (CredentialInputValidator)provider;
+ if (validator.supportsCredentialType(type) && validator.isConfiguredFor(realm, user, type)) {
+ return true;
+ }
+ }
+ }
+
+ List<ComponentModel> components = getCredentialProviderComponents(realm);
+ for (ComponentModel component : components) {
+ CredentialProviderFactory factory = (CredentialProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(CredentialProvider.class, component.getProviderId());
+ if (!Types.supports(CredentialInputUpdater.class, factory, CredentialProviderFactory.class)) continue;
+ CredentialInputValidator validator = (CredentialInputValidator)session.getAttribute(component.getId());
+ if (validator == null) {
+ validator = (CredentialInputValidator)factory.create(session, component);
+ session.setAttribute(component.getId(), validator);
+ }
+ if (validator.supportsCredentialType(type) && validator.isConfiguredFor(realm, user, type)) {
+ return true;
+ }
+ }
+ return false;
+
+ }
+
+ @Override
+ public Set<String> requiredActionsFor(RealmModel realm, UserModel user, String type) {
+ Set<String> requiredActionsFor = Collections.EMPTY_SET;
+ if (!StorageId.isLocalStorage(user)) {
+ String providerId = StorageId.resolveProviderId(user);
+ UserStorageProvider provider = getStorageProvider(realm, providerId);
+ if (provider instanceof CredentialInputUpdater) {
+ CredentialInputUpdater updater = (CredentialInputUpdater)provider;
+ if (updater.supportsCredentialType(type)) {
+ requiredActionsFor = updater.requiredActionsFor(realm, user, type);
+ if (!requiredActionsFor.isEmpty()) {
+ return requiredActionsFor;
+ }
+ }
+ }
+ }
+
+ List<ComponentModel> components = getCredentialProviderComponents(realm);
+ for (ComponentModel component : components) {
+ CredentialProviderFactory factory = (CredentialProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(CredentialProvider.class, component.getProviderId());
+ if (!Types.supports(CredentialInputUpdater.class, factory, CredentialProviderFactory.class)) continue;
+ CredentialInputUpdater updater = (CredentialInputUpdater)session.getAttribute(component.getId());
+ if (updater == null) {
+ updater = (CredentialInputUpdater)factory.create(session, component);
+ session.setAttribute(component.getId(), updater);
+ }
+ if (updater.supportsCredentialType(type)) {
+ requiredActionsFor = updater.requiredActionsFor(realm, user, type);
+ if (!requiredActionsFor.isEmpty()) {
+ return requiredActionsFor;
+ }
+ }
+ }
+ return requiredActionsFor;
+
+ }
+
+
}
diff --git a/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderModel.java b/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderModel.java
index 351107f..42ad397 100755
--- a/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderModel.java
+++ b/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderModel.java
@@ -17,14 +17,8 @@
package org.keycloak.storage;
-import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentModel;
-import org.keycloak.models.RealmModel;
-
-import java.io.Serializable;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Map;
+import org.keycloak.component.PrioritizedComponentModel;
/**
* Stored configuration of a User Storage provider instance.
@@ -32,14 +26,7 @@ import java.util.Map;
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
* @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
*/
-public class UserStorageProviderModel extends ComponentModel {
-
- public static Comparator<UserStorageProviderModel> comparator = new Comparator<UserStorageProviderModel>() {
- @Override
- public int compare(UserStorageProviderModel o1, UserStorageProviderModel o2) {
- return o1.getPriority() - o2.getPriority();
- }
- };
+public class UserStorageProviderModel extends PrioritizedComponentModel {
public UserStorageProviderModel() {
setProviderType(UserStorageProvider.class.getName());
@@ -49,14 +36,4 @@ public class UserStorageProviderModel extends ComponentModel {
super(copy);
}
- public int getPriority() {
- String priority = getConfig().getFirst("priority");
- if (priority == null) return 0;
- return Integer.valueOf(priority);
-
- }
-
- public void setPriority(int priority) {
- getConfig().putSingle("priority", Integer.toString(priority));
- }
}