fixes/refactoring after testing

This commit is contained in:
2021-11-02 10:55:20 -04:00
parent 05d12fd94f
commit 7028aa55dc
6 changed files with 51 additions and 13 deletions

View File

@@ -21,11 +21,17 @@ by navigating to the "Authentication" menu item in the vertical menu on the left
realm's user interface. You will then need to navigate to the "Password Policy" tab along the
menu of tabs on the top of the page.
This interface provides you the OOTB ability to specify password policies for all users. This
is still true with the plugin installed. You will also have an additional option: **Group
This interface provides you the OOTB ability to specify password policies for **all** users.
This is still true with the plugin installed. You will now have an additional option: **Group
Policy**. To use the plugin, you must add that password policy. The "Policy Value" should be
set to the ID we came up with earlier: `passwordPolicy`.
If you intend to use group-specific password expiration (`forceExpiredPasswordChange`), you will
need to perform an additional step in the configuration. In the same "Authentication" section,
navigate to the "Required Actions" tab along the menu of tabs on the top of the page. Use the
"Register" button to add the "Group-based Expired Password" action. Move it up in the order to
after the "Update Password" action.
At this point, you will need to add an attribute (with key `passwordPolicy`) to each group you
want to have additional password policies. The format of that text is defined by Keycloak
documentation and covered in the section below.
@@ -50,7 +56,8 @@ The [policies provided with KeyCloak](https://www.keycloak.org/docs/6.0/server_a
| `regexPattern(string)` | regular expression | ✓ |
| `notUsername()` | | ✓ |
| `passwordBlacklist(string)` | file name | - |
| `passwordHistory(int)` | number of last used passwords to disallow | - |
| `passwordHistory(int)` | number of last used passwords to disallow | |
| `forceExpiredPasswordChange(string)` | number of days to expire password after | ✓ |
On the realm model the password policy attribute is also used for other purposes.
There are some registered "policies", that do not actually implement a policy that
@@ -60,7 +67,6 @@ If these currently work is completely untested.
| Identifier | Description | Tested |
| ------------- |:------------------------------------ | ------ |
| `forceExpiredPasswordChange(string)` | number of days to expire password after | - |
| `hashAlgorithm(string)` | hash algorithm to use when hashing the password | - |
| `hashIterations(int)` | number of hash iterations | - |

View File

@@ -73,6 +73,12 @@
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<classifier>keycloak-v${keycloak.majorVersion}</classifier>
<archive>
<manifestEntries>
<!-- This is required in order for the classloader of this JAR to access the specified library at runtime -->
<Dependencies>org.keycloak.keycloak-services</Dependencies>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>

View File

@@ -29,12 +29,12 @@ import com.google.auto.service.AutoService;
* @author brian@inteligr8.com
*/
@AutoService(RequiredActionFactory.class)
public class GroupRequiredActionFactory implements RequiredActionFactory {
public class GroupExpiredPasswordRequiredActionFactory implements RequiredActionFactory {
private final Logger logger = Logger.getLogger(GroupRequiredActionFactory.class);
private final Logger logger = Logger.getLogger(GroupExpiredPasswordRequiredActionFactory.class);
private static final String ID = "groupRequiredAction";
private static final String DISPLAY = "Group Action";
private static final String DISPLAY = "Group-based Expired Password";
@Override
public String getId() {
@@ -43,7 +43,8 @@ public class GroupRequiredActionFactory implements RequiredActionFactory {
@Override
public RequiredActionProvider create(KeycloakSession session) {
return new GroupRequiredActionProvider(session);
this.logger.trace("create()");
return new GroupExpiredPasswordRequiredActionProvider(session);
}
@Override

View File

@@ -27,25 +27,31 @@ import org.keycloak.models.UserModel;
/**
* @author brian@inteligr8.com
*/
public class GroupRequiredActionProvider extends RequiredActionMultiplexer {
public class GroupExpiredPasswordRequiredActionProvider extends RequiredActionMultiplexer {
private final Logger logger = Logger.getLogger(RequiredActionMultiplexer.class);
private final GroupPasswordPolicyFinder finder = new GroupPasswordPolicyFinder();
private final KeycloakSession session;
public GroupRequiredActionProvider(KeycloakSession session) {
public GroupExpiredPasswordRequiredActionProvider(KeycloakSession session) {
this.session = session;
}
@Override
protected int findDaysToExpire(RealmModel realm, UserModel user) {
if (this.logger.isTraceEnabled())
this.logger.tracef("findDaysToExpire(%s, %s)", realm == null ? null : realm.getName(), user == null ? null : user.getId());
List<String> policyStrs = this.finder.findPolicies(realm, user);
if (policyStrs == null || policyStrs.isEmpty())
return -1;
this.logger.debugf("found policies: [%s]", policyStrs.toString());
Integer minDaysToExpire = null;
for (String policyStr : policyStrs) {
this.logger.tracef("inspecting policy: %s", policyStr);
PasswordPolicy policy = PasswordPolicy.parse(this.session, policyStr);
int daysToExpire = policy.getDaysToExpirePassword();
if (daysToExpire < 0)
@@ -63,6 +69,7 @@ public class GroupRequiredActionProvider extends RequiredActionMultiplexer {
}
}
this.logger.debugf("determined password expiration policy: %d days", minDaysToExpire);
return minDaysToExpire == null ? -1 : minDaysToExpire;
}

View File

@@ -17,6 +17,8 @@
package com.github.jpicht.keycloak.policy;
import com.google.auto.service.AutoService;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
@@ -26,6 +28,8 @@ import org.keycloak.policy.PasswordPolicyProviderFactory;
@AutoService(PasswordPolicyProviderFactory.class)
public class GroupPasswordPolicyProviderFactory implements PasswordPolicyProviderFactory {
private final Logger logger = Logger.getLogger(GroupPasswordPolicyProviderFactory.class);
static final String ID = "groupPasswordPolicy";
@Override
@@ -40,6 +44,7 @@ public class GroupPasswordPolicyProviderFactory implements PasswordPolicyProvide
@Override
public void init(Config.Scope config) {
this.logger.trace("init()");
}
@Override
@@ -68,5 +73,6 @@ public class GroupPasswordPolicyProviderFactory implements PasswordPolicyProvide
@Override
public void close() {
this.logger.trace("close()");
}
}

View File

@@ -19,6 +19,7 @@ package com.github.jpicht.keycloak.policy;
import java.util.concurrent.TimeUnit;
import org.jboss.logging.Logger;
import org.keycloak.authentication.InitiatedActionSupport;
import org.keycloak.authentication.RequiredActionContext;
import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.common.util.Time;
@@ -38,6 +39,11 @@ abstract class RequiredActionMultiplexer implements RequiredActionProvider {
protected abstract int findDaysToExpire(RealmModel realm, UserModel user);
@Override
public InitiatedActionSupport initiatedActionSupport() {
return InitiatedActionSupport.SUPPORTED;
}
/**
* This is a re-implementation of what is found in the default
* implementation in Keycloak. It just makes days-to-expire abstract so it
@@ -51,20 +57,26 @@ abstract class RequiredActionMultiplexer implements RequiredActionProvider {
int daysToExpirePassword = this.findDaysToExpire(context.getRealm(), context.getUser());
if (daysToExpirePassword > -1) {
this.logger.debugf("Found password expiration: %d days", daysToExpirePassword);
PasswordCredentialProvider passwordProvider = (PasswordCredentialProvider)context.getSession()
.getProvider(CredentialProvider.class, PasswordCredentialProviderFactory.PROVIDER_ID);
CredentialModel password = passwordProvider.getPassword(context.getRealm(), context.getUser());
if (password != null) {
this.logger.tracef("Found password credentials; created: %d ms", password.getCreatedDate());
if(password.getCreatedDate() == null) {
context.getUser().addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
logger.debug("User is required to update password");
this.logger.debug("User is required to update password");
} else {
long timeElapsed = Time.currentTimeMillis() - password.getCreatedDate();
long timeToExpire = TimeUnit.DAYS.toMillis(daysToExpirePassword);
if(timeElapsed > timeToExpire) {
context.getUser().addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
logger.debug("User is required to update password");
this.logger.debug("User is required to update password");
} else {
this.logger.tracef("Password credentials expire in %d ms", timeToExpire);
}
}
}