1

what I want to do is to en-/disable an Active Directory Useraccount. In Active Directory that information is stored in a bit-register in an attribute called userAccessControl as a HEX-Value. In our Windows 2008 Server R1, that is 0x10200 for an enabled account with the option that a users password never expires, or 0x10202 for a disabled account with the option that a users password never expires.

Now to just touch the flag for the enabled/disabled information, I wrote the following method...

def set_account_active_flag(activate)
  success = false
  get_aduser if @aduser.nil?
  puts "#####################################"
  unless @uac.nil? || @uac.blank?
    tmpuac = @uac.to_i
    tmpuac = activate ? tmpuac & 2 == 0 ? tmpuac | 2
                                        : tmpuac
                      : tmpuac & 2 == 2 ? tmpuac ^ 2
                                        : tmpuac
    ldap_con = self.class.initialize_ldap_con
    # success = ldap_con.replace_attribute ldap_encode(@dn), :userAccountControl, tmpuac


   success = ldap_con.replace_attribute ldap_encode(@dn), :userAccountControl, ldap_encode(tmpuac.to_s)
  else
    puts ">>>>>\nuserAccessControl-Register is not available\n<<<<<"
  end
rescue Net::LDAP::LdapError => e
  puts "NET::LDAP::LdapError\n#{e}"
ensure
  puts "-------------------------------------"
  puts "LDAP operation failed (#{ldap_con.get_operation_result.code}):"
  puts "-------------------------------------"
  puts ldap_con.get_operation_result.message
  puts "#####################################"
  return success
end

ok... internals:

  • get_aduser is just a method that loads a set of ad-attributes (['dn','userPrincipalName', 'givenname','sn', 'mail', 'memberof', 'userAccountControl']) and stores them into instance-variables of the user trying to login (@dn, @user_principal_name, @first_name, @last_name, @groups, @uac)

That part works like a charm. @uac (returened as a string), I can transform to an integer

x = @uac.to_i

and then use that as a bit-register to check and modify flags

x &  2 # => 0 if unset, => 1 if set
x |= 2 # sets the flag
x ^= 2 # unsets the flag

That working I thought it as easy as to just write back that value to my Active Directory.

There comes my Problem: So far I tried to write back the new userAccountControl value as integer and as string, but both attempts fail, although the operation-result-message in both cases is {Code:0, Message:'Success'}

Trying to write back the new userAccessControl value as integer raises NET::LDAP::LdapError

#####################################
NET::LDAP::LdapError 
response missing or invalid
-------------------------------------
LDAP operation failed (0):
-------------------------------------
Success
#####################################
=> false 

Trying to write back the new userAccessControl value as string does not raise an error but still results in false

#####################################
-------------------------------------
LDAP operation failed (0):
-------------------------------------
Success
#####################################
=> false 

So I wonder, 'What am I doing wrong?'

Does anybody know how to write back userAccessControl to ActiveDirectory?

Do I have to transform the new userAccessControl-Value using something like that awkward algorithm needed to write back a user-password?

Thanks a lot in advance for any useful hint or even a solution.

best regards,

Ingo Gambin

Ingo
  • 1,805
  • 18
  • 31

1 Answers1

2

Ok, from toying around with all the ldap connection stuff and having tried several different approaches, like writing the whole show using php-scripts that I executed from within rails, it seems there is actually no real problem in writing the modified value back!

What did I do wrong? Nothing! ... well ... more or less ... that is ...

Why did I fail to realize the above approach 'kind of' worked?

  • Having the ActiveDirectory SnapIn to see the list of users does not actualize the tiny 'deactivated'-icon when hitting 'F5' ... actually I assumed F5 would actualize the list at all... but it doesn't. So I didn't see the change there.
  • The code above contains a little logical error. Instead of enabling the account it disables and vice versa as the method above assumes the AD-control-flag is called 'Account active' but that is not the case, the flag is 'account deactivated'. So setting the flag means DEACTIVATION which is the opposite of my method above.
  • In addition to the AD-Account-DEACTIVATION-Flag we have a similar flag in our User-Record. My test-user actually was disabled there and the LDAP-Modification was called directly without also setting the user-record setting => so even if the AD-Account was not disabled, my test-user-record still was disabled and did not allow login.
  • Last but not least: The return value of the actual attempt to replace the 'userAccountControl'-value kept returning false (and still does in the proper solution given below), even if the modification was successful:

     success = ldap_con.replace_attribute ldap_encode(@dn), :userAccountControl, ldap_encode(tmpuac.to_s)
    

Here the fixed method to set Account Deactivation (and the value to be written back is expected as a string):

def set_account_deactivation(deactivate)
  get_aduser if @dn.nil?
  success = false
  unless @uac.nil? || @uac.blank?
    tmpuac = @uac.to_i
    # deactivate = true means 'Account Deactivated'-flag => false
    tmpuac = deactivate ? tmpuac & 2 == 0 ? tmpuac | 2  # flag not set (active)  = account is not deactivated ? deactivate
                                          : tmpuac      #                                                     : leave as is
                        : tmpuac & 2 == 2 ? tmpuac ^ 2  # flag set (deactivated) = account is deactivated     ? activate
                                          : tmpuac      #                                                     : leave as is
    ldap_con = self.class.initialize_ldap_con
    success = ldap_con.replace_attribute ldap_encode(@dn), :useraccountcontrol, ldap_encode("#{tmpuac}")
  else
    puts "Failed to read userAccessControl-Register!"
  end
rescue Net::LDAP::LdapError => e
  puts "NET::LDAP::LdapError\n#{e}"
ensure
  return success
end

def ldap_encode(string)
  if string.encoding.name != 'ASCII-8BIT'
    string.dup.force_encoding 'ASCII-8BIT'
  else
    string
  end
end
Sandeep M
  • 2,942
  • 2
  • 13
  • 17
Ingo
  • 1,805
  • 18
  • 31