Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ Download `keycloak-user-group-based-password-policy-3.0.0.jar` from Releases pag
#### Keycloak v.19.x to up
Download `keycloak-user-group-based-password-policy-4.0.0.jar` from Releases page (or from jar directory). Then deploy it into `$KEYCLOAK_HOME/standalone/deployments/ directory`.

#### Keycloak v.21.x to up
Download `keycloak-user-group-based-password-policy-5.0.0.jar` from Releases page (or from jar directory). Then deploy it into `$KEYCLOAK_HOME/standalone/deployments/ directory`.

### Creating a new Password policy with type **Group Based Policy**

<img src="media/add_new_group_based_policy_01.png" width="300">
Expand Down
4 changes: 2 additions & 2 deletions build.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#/bin/sh
#!/bin/sh
mvn clean
mvn install
#mvn compile
mvn package
#VERSION=3.0.0
#VERSION=5.0.0
#echo 'Copying new jar ./../cx0/keycloak/privacyidea-plugin/deployment/'
#cp ./target/keycloak-user-group-based-password-policy-${VERSION}.jar ./../cx0/keycloak/plugins/deployment/
Binary file not shown.
58 changes: 50 additions & 8 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.sayedcsekuet.keycloak.policy</groupId>
<artifactId>keycloak-user-group-based-password-policy</artifactId>
<version>4.0.0</version>
<version>5.0.0</version>
<packaging>jar</packaging>

<properties>
Expand All @@ -13,8 +13,10 @@
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>

<keycloak.version>19.0.1</keycloak.version>
<auto-service.version>1.0-rc5</auto-service.version>
<keycloak.version>21.1.2</keycloak.version>
<auto-service.version>1.1.1</auto-service.version>
<junit-jupiter.version>5.9.1</junit-jupiter.version>
<mockito.version>4.8.0</mockito.version>
</properties>

<dependencies>
Expand Down Expand Up @@ -56,11 +58,6 @@
<artifactId>keycloak-services</artifactId>
<version>${keycloak.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${keycloak.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-common</artifactId>
Expand All @@ -77,6 +74,32 @@
<version>RELEASE</version>
<scope>compile</scope>
</dependency>

<!-- Testing dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.12.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand All @@ -95,6 +118,25 @@
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.7</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ public static String collectPolicies(KeycloakSession session, RealmModel realm,
}

public static PasswordPolicy parsePolicy(KeycloakSession session, String policy) {
PasswordPolicy parsedPolicy = PasswordPolicy.parse(session, policy);
return parsedPolicy;
return PasswordPolicy.parse(session, policy);
}

public static PasswordPolicy createGroupPolicy(KeycloakSession session, RealmModel realm, UserModel user) {
Expand All @@ -56,7 +55,7 @@ public static PasswordPolicy createGroupPolicy(KeycloakSession session, RealmMod

public static PasswordPolicy mergeGroupPolicy(KeycloakSession session, RealmModel realm, UserModel user) {
String groupPolicy = PolicyCollector.collectPolicies(session, realm, user);
if (groupPolicy.equals("")) {
if (groupPolicy.isEmpty()) {
return realm.getPasswordPolicy();
}
String mergedPolicy = realm.getPasswordPolicy().toString() + " and " + groupPolicy;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
import com.google.auto.service.AutoService;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.authentication.*;
import org.keycloak.authentication.RequiredActionContext;
import org.keycloak.authentication.RequiredActionFactory;
import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.common.util.Resteasy;
import org.keycloak.common.util.ResteasyProvider;
import org.keycloak.common.util.Time;
import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.CredentialProvider;
Expand All @@ -21,6 +25,7 @@
import org.keycloak.services.messages.Messages;
import org.keycloak.services.validation.Validation;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.jboss.resteasy.spi.HttpRequest;

import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
Expand All @@ -31,6 +36,7 @@
@AutoService(RequiredActionFactory.class)
public class GroupUpdatePassword implements RequiredActionProvider, RequiredActionFactory {
private static final Logger logger = Logger.getLogger(GroupUpdatePasswordFactory.class);
private ResteasyProvider resteasyProvider;

@Override
public void evaluateTriggers(RequiredActionContext context) {
Expand Down Expand Up @@ -73,7 +79,7 @@ public void processAction(RequiredActionContext context) {
RealmModel realm = context.getRealm();
UserModel user = context.getUser();
KeycloakSession session = context.getSession();
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
MultivaluedMap<String, String> formData = resteasyProvider.getContextData(HttpRequest.class).getDecodedFormParameters();
event.event(EventType.UPDATE_PASSWORD);
String passwordNew = formData.getFirst("password-new");
String passwordConfirm = formData.getFirst("password-confirm");
Expand Down Expand Up @@ -101,11 +107,10 @@ public void processAction(RequiredActionContext context) {
}

if (getId().equals(authSession.getClientNote(Constants.KC_ACTION_EXECUTING))
&& "on".equals(formData.getFirst("logout-sessions")))
{
&& "on".equals(formData.getFirst("logout-sessions"))) {
session.sessions().getUserSessionsStream(realm, user)
.filter(s -> !Objects.equals(s.getId(), authSession.getParentSession().getId()))
.collect(Collectors.toList()) // collect to avoid concurrent modification as backchannelLogout removes the user sessions.
.collect(Collectors.toList()) // collect to avoid concurrent modification as back channelLogout removes the user sessions.
.forEach(s -> AuthenticationManager.backchannelLogout(session, realm, s, session.getContext().getUri(),
context.getConnection(), context.getHttpRequest().getHttpHeaders(), true));
}
Expand All @@ -124,22 +129,21 @@ public void processAction(RequiredActionContext context) {
.setError(me.getMessage(), me.getParameters())
.createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
context.challenge(challenge);
return;
} catch (Exception ape) {
errorEvent.detail(Details.REASON, ape.getMessage()).error(Errors.PASSWORD_REJECTED);
Response challenge = context.form()
.setAttribute("username", authSession.getAuthenticatedUser().getUsername())
.setError(ape.getMessage())
.createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
context.challenge(challenge);
return;
} finally {
realm.setPasswordPolicy(originalPolicy);
}
}

@Override
public RequiredActionProvider create(KeycloakSession session) {
setResteasyProvider(Resteasy.getProvider());
return this;
}

Expand All @@ -162,6 +166,7 @@ public void close() {
public String getId() {
return UserModel.RequiredAction.UPDATE_PASSWORD.name();
}

@Override
public boolean isOneTimeAction() {
return true;
Expand All @@ -171,4 +176,8 @@ public boolean isOneTimeAction() {
public String getDisplayText() {
return "Update Password";
}

void setResteasyProvider(ResteasyProvider resteasyProvider) {
this.resteasyProvider = resteasyProvider;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,6 @@ public Stream<String> getDefaultRolesStream() {
throw new UnsupportedOperationException("Not supported yet.");
}


@Override
public void addDefaultRole(String name) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
Expand Down Expand Up @@ -1058,11 +1057,6 @@ public void setEmailTheme(String name) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}

/**
* Time in seconds since epoc
*
* @return
*/
@Override
public int getNotBefore() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
Expand Down Expand Up @@ -1093,7 +1087,6 @@ public void setEventsExpiration(long expiration) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}


@Override
public Stream<String> getEventsListenersStream() {
throw new UnsupportedOperationException("Not supported yet.");
Expand Down Expand Up @@ -1226,7 +1219,6 @@ public Stream<GroupModel> getTopLevelGroupsStream(Integer integer, Integer integ
throw new UnsupportedOperationException("Not supported yet.");
}


@Override
public Stream<GroupModel> searchForGroupByNameStream(String s, Integer integer, Integer integer1) {
throw new UnsupportedOperationException("Not supported yet.");
Expand Down