问题
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
回答1:
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
来源:https://stackoverflow.com/questions/5349593/how-to-modify-useraccountcontrol-in-activedirectory-from-rubyonrails3-with-ruby1