Event Handler Ideas

Working through a POC and looking for ideas on event handlers that are useful to setup. I’ve got a new token notification configured that sends an email with the QR code embedded. What else has proven useful for everyone?

I use them to call scripts on the server. We use LDAP Authentication in our environment, but don’t use PI to add two factor to every instance where that authentication is needed (yet…ultimately that’s my goal), so I noticed in lab testing that if a user reached the Max Fail limit, it would lock them out of their token, but the LDAP server that it was using for a resolver would be unaffected. So, a user could be locked out of access to devices that were configured to use PI but not devices that only used LDAP authentication.

Because of what script parameters are sent when certain events happen, I had to come up with four scripts. Below is the order of scripts over a token’s lifetime. 1 and 4 typically only happen once, 2 and 3 can happen many times over a token life time.

1: Register. When a user enrolls, or has an existing token assigned to them, an event handler fires off that runs a script that adds the user’s token serial to the description attribute of their LDAP account

  1. Lock. When a user reaches the Max Fail Counter limit, an event handler fires that runs a script that locks their LDAP account.

  2. Unlock. When an admin clicks the “Reset Fail Counter” button for a token, an event handler fires that runs a script that unlocks the user’s LDAP account.

  3. Unregister. When a token is deleted or unassigned from a user, an event handler fires that runs a script that removes the serial number from the user’s LDAP description attribute.


I really like 2-3…now I just need to figure out how to issue those commands from a Linux box to AD, lol. For your registration event, what value does your organization get out of having the token serial in the user’s description attribute?

The Register/Unregister part came about through trial/error trying to get the Lock/Unlock scripts to work.

When a user would reach the Max Fail Limit, the event handler would receive an event with the username only. When the Reset Max Fail button was pressed, it triggered an event that sent only the serial of the token. I needed a way to correlate the two. There are probably other ways I could have done this, but this is what I came up with and it works. I have no idea what you know about OpenLDAP, so sorry if this is old news. When you use scripts/CLI to make changes to OpenLDAP 2.4, you have to create a text file called an LDIF file that has structured text in it, which when combined with the ldapmodify command, makes updates to the LDAP Directory.

When they enroll/assign a token, the description field is populated with the serial.

When they are locked out, two LDIF files are created, one that locks the user account, the other that unlocks it. I have a Lock and Unlock directory those files are created in. Only the “lock” LDIF file is pushed to the LDAP server at that time. It then moves the “lock” LDIF file to a history subdirectory of the Lock directory. The “unlock” LDIF file is created and the name used for the file is the user’s token serial number, which the script finds by taking the username parameter passed to it and searching LDAP for that account, and grabbing the info from the Description attribute.

When the Reset Max Counter button is pushed, the only parameter passed to the event handler is the serial number, so the script takes that parameter, then searches the “unlock” directory I created for any file with a name of that serial. It finds it, and pushes it to the LDAP server. Once an account is unlocked, that LDIF file is moved to the history subdirectory of the Unlock directory .

By using history subdirectories, it guarantees that the unlock folder will only ever contain LDIF files for currently locked accounts. A bonus is I can keep a record of locks/unlocks. I know the audit log does that too, but I thought it might be useful. They are small text files, so it’s not like they take up a lot of space.

Then, when a user unassigns/deletes a token, their LDAP account has that attribute cleared. This is to prevent a scenario where two users have the same serial assigned, which would cause the script to fail. That probably isn’t likely, but since it’s technically possible, I wrote that script to make sure it wasn’t an issue.

1 Like

As for your Linux to AD requirement, this should probably help do the trick.

1 Like

I didn’t even think about looking for a PowerShell port into Linux, that makes the scripting part a breeze.

I don’t think I’m fully understanding this. What attribute does the event handler pass, the cn?

Or, is it PrivacyIDEA’s attribute mapping for the username field? If that’s the case it works out perfectly as I have that mapped to sAMAccountName, essentially a UUID in AD.

When the Max Fail Limit was reached, it would trigger the event, but the only parameter it would send would be the username of the person who failed. That was fine for locking the account, since the username came from our LDAP resolver, it could then be used to find the LDAP account and lock it.

The problem came with unlocking. When the Reset Max Fail button was pushed, it didn’t send the username with the event, because technically, the user isn’t what was locked, the token was. So, the script couldn’t tell what account in LDAP to unlock. I got around this by creating the LDIF file for unlocking during the lock event. The lock event, after locking the account in LDAP, would create an unlock file with the username it had just received from the Lock event in it’s contents. Then, it would take that username, look up it’s serial number in LDAP, and use that as the file name.

Then, when the unlock event was triggered, and the only parameter it received was the serial number of the token, it would search the folder with Unlock files for any file that matched that serial number parameter it had just received.

I know I’m being verbose, and it’s making this appear muddled. Here’s a simple (I hope) break down of what each script does.

Register Token - This event receives both the token serial and username as event parameters.
Create two files.

File name /register/history/(serial parameter)
File Contents: add serial to (user parameter) LDAP Description field

File name /unregister/(serial parameter)
File Contents: remove serial from (user parameter) LDAP Description field

Lock - Create two LDIF files.
Search LDAP for user received from Lock event parameter, find registered serial in Description attribute, put that in serial variable in script

File Path: /lock/(serial variable).ldif
File Contents: lock user received from Lock event parameter

File Path: /unlock/(serial variable).ldif
File Contents: unlock user received from Lock event parameter

ldapmodify /lock/(serial variable).ldif
move /lock/(serial variable).ldif /lock/history/

Unlock This event only receives serial event parameter.
ldapmodify /unlock/(serial event parameter).ldif
move /unlock/(serial event parameter).ldif /unlock/history

Only serial parameter is passed here
ldapmodify /unregister/(serial parameter)
move /unregister/(serial parameter) /unregister/history/(serial parameter)

The script receives the serial number of the token.
You could also use a privacyIDEA API call to determine the owner of the token.
Your script could call

 GET /token?serial=<serialnumber>

an receive the JSON response with the owner of the token.
Thus you would not need to save anything in addition.
See https://privacyidea.readthedocs.io/en/latest/modules/api/token.html#get--token-

Yes, when I was figuring this all out, I was pretty sure I could use the PI API to get that information, but APIs are not my strong suite. I went with what I know. If @wwalker is strong with API usage, then that’s definitely the cleaner, simpler way to go.

As long as the PowerShell in Linux port works as well as it does in Windows, it will be a pretty simple process to make the call and pick apart the response data as needed. If I get to it, I will post the code here.

You need a service account (like an internal admin) to fetch this information via API.

  1. POST /auth
    will give you an authorization token, that can be used in the authorization header of the second request
  2. GET /token?serial= (with the authorization token)
    will return the token information.

Might want to take a look at the new script repository we started:

That can give an idea, how the script handlers can be used.