Showing posts with label code. Show all posts
Showing posts with label code. Show all posts

Wednesday, January 23, 2013

Enforcing Active Directory password history when resetting passwords using PHP

The code:

$ctrl1 = array(
    // LDAP_SERVER_POLICY_HINTS_OID for Windows 2012 and above
    "oid" => "1.2.840.113556.1.4.2239",
    "value" => sprintf("%c%c%c%c%c", 48, 3, 2, 1, 1));
$ctrl2 = array(
    // LDAP_SERVER_POLICY_HINTS_DEPRECATED_OID for Windows 2008 R2 SP1 and above
    "oid" => "1.2.840.113556.1.4.2066",
    "value" => sprintf("%c%c%c%c%c", 48, 3, 2, 1, 1));
if (!ldap_set_option($ds, LDAP_OPT_SERVER_CONTROLS, array($ctrl1, $ctrl2))) {
    error_log("ERROR: Failed to set server controls");
}

$result = ldap_mod_replace($ds, $dn, $entry);
...


Details:

There are a couple of ways to reset Active Directory passwords using LDAP:
  1. A delete operation on the unicodePwd attribute immediately followed by an add, which is a password change

  2. A modification of the unicodePwd attribute, which is considered an administrative password reset
When doing the latter, password history requirements may not be enforced. Apparently this is expected behavior. The solution is a server control:
  1. First, the control must be available on the server. It's present in Windows Server 2008 R2 Service Pack 1 and above. It can also be installed in 2008 R2 using this hotfix: http://support.microsoft.com/?id=2386717

  2. Next, the client must use the control to tell the AD server to enforce password history requirements.
If you happen to be doing this in PHP, the solution for the second part is the code I've posted. The tricky part was to get the BER encoding for the value correct.

I put both controls (the new one as well as the deprecated one) in my code and also didn't set the iscritical flag (which defaults to false) in order to make the code as flexible as possible.

In case you're looking to implement a solution to reset AD passwords using PHP, you may find these helpful:

Monday, January 9, 2012

Using LIMIT with android ContentProvider

So you're using a content provider in your android app, and you want to supply a LIMIT parameter. The problem is, ContentProvider's query method doesn't have a limit parameter. Well, fortunately there's an easy solution; put the limit into a query parameter:
  1. First, in your content provider, create a constant for the query parameter key:
    public class MyProvider extends ContentProvider {
        public static final String QUERY_PARAMETER_LIMIT = "limit";

  2. In the query method of your content provider, get the limit from the Uri:
    public Cursor query(...
        String limit = uri.getQueryParameter(QUERY_PARAMETER_LIMIT);

  3. Still in the query method of your content provider, you can pass the limit on when creating the cursor:
    SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
    Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder, limit);

  4. Lastly, when calling your content provider (via getContentResolver.query(), a CursorLoader, etc), you can append the limit to the content uri like so:
    String someLimit = "15";
    cursor = getContentResolver().query(
        // add the limit to the content uri
        MyProvider.CONTENT_URI.buildUpon().appendQueryParameter(
            MyProvider.QUERY_PARAMETER_LIMIT,
            someLimit).build(),
            ...
The cool thing is you can use this same technique to pass other parameters to your query, like DISTINCT, for instance.

Monday, July 18, 2011

Python mysqldb UnicodeDecodeError: 'ascii' codec can't decode byte

Solution:

If you run into the error mentioned in the title of this post using python's mysqldb module version 1.2.1 or less, decode your data/query first:

mydata.decode('utf8')

(modifying 'utf8' to whatever encoding your data happens to be in)

Details:

So I was writing some code in python on Ubuntu, and it was working just fine. When I went to run it in RHEL, I got this error:

Traceback (most recent call last):
File "", line 50, in ?
File "/usr/lib64/python2.4/site-packages/MySQLdb/cursors.py", line 146, in execute
query = query.encode(charset)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 223: ordinal not in range(128)


My first thought was that it was due to the incredibly old version of Python that ships with RHEL 5 (Python 2.4), but it didn't take me long to realize the problem was with the MySQLdb module itself. Ubuntu 10.10 ships with version 1.2.2 of that module, while RHEL 5 ships with version 1.2.1. A minor difference, but apparently in that time this bug was fixed:
http://sourceforge.net/tracker/index.php?func=detail&aid=1521274&group_id=22307&atid=374932

Apparently MySQLdb 1.2.1 tries to indiscriminately encode the data to be put into the database to utf8 (well, at least when you specify utf8 as the database character set), without checking whether the string is already utf8 or not. My solution was just to decode my data from utf8 (to unicode) before passing it to my mysql query, at which point the encoding works just fine.

Like so (the first line's the relevant one):

mydata.decode('utf8')
query = ('INSERT INTO %(database)s (%(column)s) VALUES (%(value)s)' % {'database': database, 'column': column, 'value': mydata})
cursor.execute(query)


Of course, you should modify the 'utf8' part to whatever encoding your data is in.

Edit: If you're using MySQLdb.escape_string(), make sure you run that first before doing the decode, like so:

MySQLdb.escape_string(mydata).decode('utf8')

Tuesday, March 8, 2011

Python and MySQL autocommit

Solution:

If your MySQL database is using the InnoDB engine, commit your changes after database transactions:
cursor.connection.commit()

Or just turn on autocommit to automatically commit after every database transaction:
cursor.connection.autocommit(True)

Details:

So I was using python's MySQLdb module to edit a mysql database, and I noticed that even though python was telling me my modifications were taking place, I wasn't seeing any changes. in addition, when I would log onto the database from something other than python and try to make changes, I would get this error:

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

Well apparently, the mysqldb module now disables mysql autocommit by default. so now, when I'm done with my transactions to the database, I need to commit my changes by calling the commit() function of the connection object. the connection object is what's returned when you call the MySQLdb.connect() function. I haven't really been using that object in my code other than to create the cursor object, which is what I normally use:

db = MySQLdb.connect(...)
cursor = db.cursor()


But the cursor can access the connection object, which I can then use to commit the changes:

cursor.connection.commit()

The strange thing with all of this is that I just started noticing this problem, even though according to the MySQLdb module authors, this functionality (disabling autocommit by default) has been in place since version 1.2.0 of the module. but when I look at the package versions of python-mysqldb for Ubuntu (what I'm currently using and have been for a while), it looks like it's been past version 1.2.0 for the last several years:
http://packages.ubuntu.com/search?keywords=python-mysqldb&searchon=names&suite=all&section=all

Maybe this "feature" has just recently made it into Ubuntu's package for this module. or maybe I'm missing something else here. at any rate, at least I know what's going on.

You can see here for more information:
http://mysql-python.sourceforge.net/FAQ.html#my-data-disappeared-or-won-t-go-away

Edit:
Okay, so apparently what happened is in my other code where I was modifying mysql databases, they used the default engine (MyISAM). the database I was having problems with was using the InnoDB engine, which is a transactional storage engine. this explains why I just now saw this issue.

More information here:
http://stackoverflow.com/questions/1617637/pythons-mysqldb-not-getting-updated-row

As well as an alternate solution: instead of committing after every transaction, I can turn autocommit on when I'm working with databases using the InnoDB engine, so they'll function just like the rest:

cursor.connection.autocommit(True)

Wednesday, December 9, 2009

PHP trim/strip non-breaking space

I was just writing some PHP and having the hardest time getting rid of a non-breaking space ( ) character in some HTML I was parsing.  I finally figured it out:  the page was encoded in UTF-8, and the encoding for a non-breaking space is 0xC2 0xA0.  This is the code that finally did the trick (I also had to get rid of carriage return characters, hence the “\n”):

$string = trim( $string, "\xC2\xA0\n" );