7.7. Add/Remove User and Query Capability interfaces
One thing we have not done with our example is allow it to add and remove users or change passwords. Users defined in our example are also not queryable or viewable in the administration console. To add these enhancements, our example provider must implement the UserQueryProvider
and UserRegistrationProvider
interfaces.
7.7.1. Implementing UserRegistrationProvider
To implement adding and removing users from this particular store, we first have to be able to save our properties file to disk.
PropertyFileUserStorageProvider
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); } }
Then, the implementation of the addUser()
and removeUser()
methods becomes simple.
PropertyFileUserStorageProvider
public static final String UNSET_PASSWORD="#$!-UNSET-PASSWORD"; @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; } }
Notice that when adding a user we set the password value of the property map to be UNSET_PASSWORD
. We do this as we can’t have null values for a property in the property value. We also have to modify the CredentialInputValidator
methods to reflect this.
The addUser()
method will be called if the provider implements the UserRegistrationProvider
interface. If your provider has a configuration switch to turn off adding a user, returning null
from this method will skip the provider and call the next one.
PropertyFileUserStorageProvider
@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()); }
Since we can now save our property file, it also makes sense to allow password updates.
PropertyFileUserStorageProvider
@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; }
We can now also implement disabling a password.
PropertyFileUserStorageProvider
@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; }
With these methods implemented, you’ll now be able to change and disable the password for the user in the administration console.