Forum Discussion

Rob_Stonham's avatar
Jan 23, 2017

Active Directory Logon Hours and APM session timeout

Hi,

 

Not really a question but documenting here incase anyone else wants to reuse this code.

 

I was asked to create a VPN connection on our F5 that would only allow access during a users normal working hours. As this information is already store in Active Directory under "User Logon Hours" I decided to use this to calculate when to terminate the APM session. Obviously a user can't authenticate against AD outside of their logon hours, so all I needed to do was to calculate how long left the user has until their logon hours expire. I used the following code in a Variable Assign to update the "Maximum Session Timeout" value for APM.

 

    if {[ expr { [mcget {session.ad.last.attr.logonHours}] != "" }] } {
                Get the value from AD
                set logonHours  [mcget {session.ad.last.attr.logonHours}]

                set default maximum session time
                set maximumSessionSeconds 604800

                set start of session times
                set startHour [clock format [mcget {session.user.starttime}] -format %H]
                set startTime [clock format [mcget {session.user.starttime}] -format %H%M]
                set startDay [clock format [mcget {session.user.starttime}] -format %w]
                set default session timeout
                set secondsRemaining 1
                set finishDay $startDay
                set rolledOver 0

                create array to store the binary list of permitted logon hours
                array set daysofweek {}
                for { set a 0} {$a < 7} {incr a} {
                                set hexHours "0x[string range $logonHours [expr 2+$a*6] [expr 7+$a*6] ]"
                                set hexHours "[string range $logonHours [expr 2+$a*6] [expr 7+$a*6] ]"
                               set daysofweek($a) [string cat [string reverse [binary format %08b "0x[string range $hexHours 2 3]"]] [string reverse [binary format %08b "0x[string range $hexHours 4 5]"]] [string reverse [binary format %08b "0x[string range $hexHours 6 7]"]]]
             binary scan [binary format H* $hexHours] b* tmp1
              set daysofweek($a) $tmp1
                }
                 set value so that it forces entry into the loop
                set finishHour 2359


                while { $finishHour == 2359 && $secondsRemaining < $maximumSessionSeconds } {
                                set finishHour [string first 0 $daysofweek($finishDay) $startHour]
                                if { [ expr $finishHour == "-1"] } {
                                                set finishHour 2359
                                                incr rolledOver

                                }
                                incr secondsRemaining [expr [clock scan $finishHour] - [clock scan $startTime]  ]
                                if { [expr $finishHour ==2359 ] } {
                                        if { [expr $finishDay < 6 ]} {
                                                 roll over to following day
                                                incr finishDay
                                        } else {
                                                 except Sat needs reseting to Sun
                                                set finishDay 0
                                        }
                                         we have rolled over days
                                         so reset the time to midnight
                                        set startTime 0000
                                        set startHour 0
                                 and give back the extra minute that we "borrowed" to make the time calculation work
                                incr secondsRemaining 60

                                }
                 If we have finally looped around a full week then we need to break out of the loop
                                if { $startDay == $finishDay && $rolledOver > 1 } {
                                                set finishHour [string first 0 $daysofweek($finishDay) $startHour]
                                                incr secondsRemaining [expr [clock scan $finishHour] - [clock scan $startTime]  ]
                                                break
                                }
                }
                return $secondsRemaining


} else {
       return $maximumSessionSeconds
}

This takes the blob from the the AD attribute logonHours and decodes it and then works out how many seconds it is from now until the next denied logon hour according to AD. We then set that number to the maximum session timeout, so that the APM will close the session when the user is denied access by AD.

 

Rob

 

1 Reply

  • Hi,

     

    Thanks for sharing. I did not know that AD attribute exists.

     

    When I read your code, I thought it is complicated for a simple need.

     

    I think this code is a quite simpler (not tested with AD)

     

    set maximumSessionSeconds 604800
    if {[set logonHours [mcget {session.ad.last.attr.logonHours}]] != ""} {
        convert string to binary string
        set logon_hours_binary_string "";
        for { set a 0} {$a < 21} {incr a} {
        binary scan [binary format H2 [string range $logonHours [expr {$a*2}] [expr {($a+1)*2 -1}]]] B8 hex2bin;
        append logon_hours_binary_string [string reverse $hex2bin];
        }
        unset hex2bin;
         evaluate the number of seconds from last sunday
        set time_from_sunday [expr {[clock seconds] - [clock scan "last sunday"]}];
         search in string next hours with 0 value
        set current_index [expr {$time_from_sunday / 3600}];
         convert the index to number of seconds from last sunday
        if { ([set next_denied_index [string first 0 $logon_hours_binary_string$logon_hours_binary_string $current_index]] == -1) || ($next_denied_index > [expr {168 + $current_index}]) } { set next_denied_index [expr {168 + $current_index}] };
         evaluate number on seconds to disconnect time
        return [expr { $next_denied_index*3600 - $time_from_sunday}]
    } else {
           return $maximumSessionSeconds
    }