Tag: idrepo

Fun with Active Directory

As we all know Active Directory can cause quite a few headscratching moments in general, now I’m trying to conclude some of my findings so maybe others won’t suffer from the same thing.

Creating user fails with LDAP 53

LDAP 53 is UNWILLING_TO_PERFORM, now there is two main candidate reason for this to happen during an ADD operation:

  • SSL isn’t used. AD will not process LDAP operations that involves setting/modifying the unicodePwd attribute if the request was made on a non-secure connection. Always make sure you use LDAPS when connecting to AD.
  • The provided password does not satisfy the password policies configured in AD. This is a bit harder to identify, but for the sake of testing try to use complex passwords and see if the user gets created like that.

Changing password fails

As stated already: AD requires SSL connection when sending the unicodePwd attribute. If this wouldn’t be already enough you have to also enclose the password value with double quote characters and the password should be in a UTF-16LE encoded format. A sample code snippet would look something like:

public byte[] encodePassword(String password) {
    return (""" + password + """).getBytes(Charset.forName("UTF-16LE"));
}

There is two main way to change passwords by the way:

  • A non-administrative password change: you BIND as the user whose password needs to be changed, and then you send a ModifyRequest with a DELETE and an ADD ModificationItem containing the old and the new password respectively (in the encoded format of course).
  • Administrative password reset: in this case you BIND as an admin user and then send a ModifyRequest with a REPLACE ModificationItem only containing the new password value.

Easy, right?

UPDATE

It appears that the DELETE/ADD combination can fail with the following error:

Constraint Violation: 0000052D: AtrErr: DSID-03190F80, #1:
	0: 0000052D: DSID-03190F80, problem 1005 (CONSTRAINT_ATT_TYPE), data 0, Att 9005a (unicodePwd)

In our case the root cause was the default password minimum age policy in AD (1 days). Once I’ve ran the following command from an Administrator CMD:

net accounts /MINPWAGE:0

AD started to like my ModifyRequests.

Proper way of changing password in OpenAM

There is always a point, when an OpenAM user ends up asking the question: ‘but how can my users change their passwords?’. The answer is quite tricky: there are several ways to do this, but you’re going to have problems with almost each one of them. But first things first, let’s see what do we want exactly:

  • Show an own form for the user, where he/she can give the old password, new password / confirmation of the new password combo and press the button, which changes the password with help of OpenAM.
  • Change the users password in LDAP without enabling the force-reset password LDAP control. (Note this is only important if you use LDAP datastore, JDBC datastore is in early access stage, this article will not cover that).

This second point is not mandatory, but this feature is used often (by the LDAP module for example).
Note: your Directory Server configuration should contain a force-change-on-reset option with true value. The force-change-on-reset mode means, that if the password was resetted by an LDAP administrator, then the user HAS TO change his/her password to be able to do anything.
Note 2: if your DS uses this option, and you’re using the Datastore module (so something that’s not based on the LDAP module), then your module will genuinely fail, if you’re not handling this special case.

So what’s the proper way?

The proper way is when you have the old password, you validate it (by doing a BIND request) to make sure, that the user is who he tell he is. If the BIND was successful, then we execute a MODIFY command in the name of the just logged in user to modify the userPassword field. This is the proper way, because with this logic you are not resetting your password with the help of an administrator, so the force-reset stays false.

The REST API way

The REST API does not have a specific changepassword function, but it does have an update function as I mentioned in my earlier post. This also means, that because you don’t supply the old password, you just can’t reset the password as yourself, it has to run in the LDAP admins name, so this is a force-reset always! So the magic here is that you call the following REST function:

https://<FQDNSSO>/openam/identity/update?identity_name=aldaris&
identity_realm=/&identity_attribute_names=userPassword&
identity_attribute_values_userPassword=newPass

Doing this as the logged in user

If you do have the users sessionid, then you could call this resource as the user, IF your DS does not forbid modifying the userPassword for example with an ACI. OpenDS by default has the following global-aci:

(targetattr="audio||authPassword||description||displayName||givenName||
homePhone||homePostalAddress||initials||jpegPhoto||labeledURI||mobile||
pager||postalAddress||postalCode||preferredLanguage||telephoneNumber||
userPassword")(version 3.0; acl "Self entry modification"; allow (write)
userdn="ldap:///self";)

This will enable you to modify your own password.

Doing this as admin

Since you’re doing this as admin, the password will be certainly overwritten.

The WebService way

The WebService way and the REST API way does not differ from each other, since they are both using the exact same code.

The ClientSDK way

The ClientSDK has several way accessing an Identity, for example using AMIdentityRepository:

SSOToken token = //getting an SSOToken object
AMIdentityRepository idRepo = new AMIdentityRepository(token, "/realm");
IdSearchResults results = idRepo.searchIdentities(IdType.USER, "aldaris",
    new IdSearchControl());
Set identities = results.getSearchResults();
AMIdentity identity = (AMIdentity) identities.iterator().next();

Using AMIdentity#store

The idea here is to use the setAttributes method to update the desirable attributes and then call the store, something like this:

AMIdentity identity = ...
Map attrs = new HashMap();
Set values = new HashSet(1);
values.add(newPass);
attrs.put("userPassword", values);
identity.setAttributes(attrs);
identity.store();

The problem with AMIdentity#store is the same as with the update function: you don’t supply the old password for the function, so there is no way, that this could end up without a force-reset. Also this solution worked for me with an IdRepo created with user token.

Using AMIdentity#changePassword

The AMIdentity class also has a changePassword method, which does exactly what we want, so let’s see some code:

AMIdentity identity = ...
identity.changePassword(oldPass, newPass);

but I had one problem with this: when I accessed the AMIdentity from an IdRepo with a usertoken, then I had the following error message:

Permission to perform the read operation denied to
id=aldaris,ou=user,dc=opensso,dc=java,dc=net

For me this does not make any sense, since I’m giving the old password and the new one, the username is supplied by the AMIdentity itself, then why do I need read permission and for what?? Anyway, when I did this with admintoken, then everything was working great without force-reset.

The OpenAM console way

There is also a way to change your password with help of OpenAM console. Just point your browser to

https://<FQDNSSO>/openam/user/UMChangeUserPassword

I tried it many times, but the Old password field was always disabled for me, so this password change method also sets the force-reset bit…

Summary

As you can see there are many ways to change a users password, but the best way I think is to use ClientSDK with AMIdentity#changePassword or LDAP API directly. For testing the pwdReset flag I’ve used the following command:

ldapsearch -D "cn=Directory Manager" -b "ou=forgerock,o=org" -h localhost
-p 1389 -w password "uid=aldaris" "pwdReset"

If you don’t need the force-change-on-reset functionality, then probably you can disable it in your Directory Server. If you want to hack, then maybe you can try out to delete the pwdReset attribute with the REST API, maybe it will work, I haven’t tried it.

Using the REST API for Identity Management

It’s not that well known, but OpenAM has a REST interface for login/logging/authorization and also for basic identity Management too. This post will describe the IDM functionality of the REST API, so it’s about creating, updating and deleting users in the DataStores.

When you google the term ‘OpenAM REST’, you won’t find much thing, because it’s not really well documented part of OpenAM, but if you google hard enough you will find this link to Docteger’s blog. This post is just GREAT, everything in one place, but it’s missing the answer for the ‘how-can-I-handle-realms-with-this’ type of question. So here is my result of few hours reading of IdentityServicesImpl:

Create Identity

https://<FQDNSSO>/openam/identity/create?identity_name=username&identity_realm=/&
identity_type=user&identity_attribute_names=cn&identity_attribute_values_cn=
MyNewCn&identity_attribute_names=userPassword&
identity_attribute_values_userPassword=password

Gotcha #1:
The password length needs to be at least 8 characters by default, if you want to change this, read this mail.

Read Identity

https://<FQDNSSO>/openam/identity/read?name=username&attributes_names=realm&
attributes_values_realm=/

Tip #1:
You could use the attributes_names parameter to ask specific parameters of the given identity.

Update Identity

https://<FQDNSSO>/openam/identity/update?identity_name=username&identity_realm=/&
identity_attribute_names=cn&identity_attribute_values_cn=NewerCn

Here you only have to add the parameters to the query, which are actually changed.
Gotcha #2:
If you want to change the password like this, then you need an authenticated admin token, since the user can’t (always?) update it’s own password.

Delete Identity

https://<FQDNSSO>/openam/identity/delete?identity_name=username&identity_realm=/&
identity_type=user

Gotcha #3:
There’s no really Gotcha here, you just have to specify, that the deletable item is actually a user.

Conclusion

This is great and everything, but you can’t do these stuff without login & search the user, so here are these calls too:

Authenticate

https://<FQDNSSO>/openam/identity/authenticate?username=username&
password=password&uri=realm%3D/%26service%3DldapService

Gotcha #4:
Note the %3D (‘=’) and %26 (‘&’) characters, they are url-encoded, since it’s a single value for the ‘uri’ param.

Search Identity

https://<FQDNSSO>/openam/identity/search?filter=*)(|(inetUserStatus=Active)&
attributes_names=realm&attributes_values_realm=/

Tip #2:
You can use the filter with some dirty hack to give OR filters too as the previous URL shows.

Summary

The REST interface is great and FAST, so use it whenever you have the chance. The only problem with it this weird parameter-handling, one time it’s ‘identity_name’, another time it’s ‘username’, so you probably going to need a few parser for using it, but I think it’s worth it. If I heard right, it’s going to use JSON-format parameters in the future, so it’s going to be much better. 🙂