Archive | Programming

WordPress: wp_signon() – $current_user is not populated

Happy Christmas and holidays!

I would be out in the sun if there was any! But since it’s raining I’m doing a bit of WordPress development for a project I’m working on.

The wp_signon() function logs a user in but for some reason after doing so the global user variables such as $current_user and $user_ID are not populated until the page is refreshed and calling get_currentuserinfo() doesn’t populate them. The is_user_logged_in() function also returns false.  I think the problem has something to do with the cookie authentication process.

There is however a solution to the problem and it doesn’t involve refreshing the page.  When you call wp_signon() it returns a user object if the sign-on was successful, so we do have all the user info we need, it’s just not available in the standard global way.  To fix this we just need to call the wp_set_current_user() specifying the id of user object which wp_signon() returned.

To demonstrate, here’s an example of creating a user, logging them in and submitting a post all in one page refresh.

<?php
_my_newuser('joe','itsasecret','joe@joe.com','Joe');
 
_my_user_login('joe','itsasecret');
 
_my_commit_post($newpost);
 
_my_current_user();
 
//Register a new user
function _my_newuser($username,$password,$email,$nickname)
{
	global $wpdb;
	$user_login = $wpdb->escape( $username );
	$user_pass = $password;
	$nickname = $nickname;
	$user_email = $username;
	$userdata = compact('user_login', 'user_email', 'user_pass', 'first_name','nickname' );
	return wp_insert_user($userdata);
}
 
//Log a user in and set them as current user
function _my_user_login($username,$password)
{
	$creds = array();
	$creds['user_login'] = $username;
	$creds['user_password'] = $password;
	$creds['remember'] = true;
	$user = wp_signon( $creds, false );
	wp_set_current_user($user->ID); //Here is where we update the global user variables
	return $user;
}
 
//Commits a new post to the DB
function _my_commit_post($postdata)
{
	global $user_ID;
	$new_post = array(
		'post_title' => $postdata->title,
		'post_content' => $postdata->description,
		'post_status' => 'publish',
		'post_date' => date('Y-m-d H:i:s'),
		'post_author' => $user_ID,
		'post_type' => 'post',
		'post_category' => array(0),
		'tags_input' => $postdata->tags
	);
	$post_id = wp_insert_post($new_post,true);
}
 
//Print info about the user who is now logged in
function _my_current_user()
{
	global $current_user,$user_ID;
 
	if(is_user_logged_in())
	{
		echo '<br />User Logged in ok<br />';
		echo 'User ID is: '.$user_ID.'<br />';
		echo 'User login is: '.$current_user->user_login.'<br />';
	}
	else
		echo 'No user is logged in< br/>';
}
 
?>

Of Course you might want some error handling in there! 😉 And did you notice the new syntax highlighting plug-in? Neat ay?

10

SetPassword over domain trust fails – COMException 0x8007202

After breaking my head over this for about 3 days and finally coming up with a solution I thought Id share.

Here’s the situation:

  • 2 Forests with a one way external trust between them.
    • Domain B in Forest B trusts Domain A in forest A.
  • An ASP.NET Application running in Domain A
    • Configured to impersonate.
    • Application Pool running under a service account from domain A which is trusted for delegation
    • Application URL (DNS name) is registered as an SPN against the above service account used to the application pool.
  • A user (Bob) from Domain A has permission to modify properties and reset passwords of users in Domain B
  • When using the Active Directory users and computers MMC Bob is able to reset passwords and modify properties of users in the trusting domain(Domain B) I.e. Permissions are ok!
  • When Bob uses the ASP.NET application (System.DirectoryServices.DirectoryEntry) Bob is able to modify properties of users in the trusting domain. I.e. Bob’s creditneials are sucessfuly delegated to the domain controller in the trusitng domain. I.e. Delegation is working!
  • When the ASP.NET application specifies Bob’s username/password in the DirectoryEntry object e.g. DirectoryEnty de = new DirectoryEntry(“LDAP://domainB.com/CN=Steve,ou=test,dc=domainb,dc=com”,”bob”,”password1″) then invokes the setpassword method, the password is successfully set. I.e. SSL is working correctly. Necessary firewall ports are open etc
  • When Bob uses the ASP.NET application and invokes the setpassword method now using delegation a COMException 0x80072020 is thrown.

I’ve spent some hours trying to work this out, network dumps with WireShark (no good for LDAPS), Kerberos logging on DCs and webserver, etc etc. I have also opened up the firewall to allow DirectoryEntry.Invoke(“setpassword”) to try his other methods. I just can’t get this to work in a domain trust environment with delegation. I have however found that using System.DirectoryServices.Protocols to reset the password in a trusting domain with a delegated credential does work! For this I’m very relieved; I was almost at the point where I was going to go back to our dev guys and tell them to switch back to using a superuser account instead of the delegation method which I had been pushing on them for weeks!

Frustrating though that I still don’t know why this method works where the standard System.DirectoryServices method doesn’t!

This example was taken from chapter 10.16 of The .NET Developer’s Guide to Directory Services Programming by Joe Kaplan and Ryan Dunn. I strongly recommend you get this book.  It’s not the newest book out but it’s still packed with really relevant stuff and lots of “gold nuggets” from 2 guys who really know their stuff.

using System;
using System.DirectoryServices.Protocols;
using System.Net;
using System.Text;

public class PasswordModifier
{
    public static void Main()
    {
        NetworkCredential credential = new NetworkCredential(
            "someuser",
            "Password1",
            "domain"
            );
        DirectoryConnection connection;

        try
        {

            //change these options to use Kerberos encryption
            connection = GetConnection(
                "domain.com:636",
                credential,
                true
                );

            ChangePassword(
                connection,
                "CN=someuser,CN=users,DC=domain,DC=com",
                "Password1",
                "Password2"
                );

            Console.WriteLine("Password modified!");
            IDisposable disposable = connection as IDisposable;

                if (disposable != null)
                    disposable.Dispose();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        private static DirectoryConnection GetConnection(
            string server,
            NetworkCredential credential,
            bool useSsl
            )
        {
            LdapConnection connection =
               new LdapConnection(server);

            if (useSsl)
            {
                connection.SessionOptions.SecureSocketLayer = true;
            }
            else
            {
               connection.SessionOptions.Sealing = true;
            }

            connection.Bind(credential);
            return connection;
        }

        private static void ChangePassword(
            DirectoryConnection connection,
            string userDN,
            string oldPassword,
            string newPassword
            )
        {
            DirectoryAttributeModification deleteMod =
                new DirectoryAttributeModification();
            deleteMod.Name = "unicodePwd";
            deleteMod.Add(GetPasswordData(oldPassword));
            deleteMod.Operation= DirectoryAttributeOperation.Delete;

            DirectoryAttributeModification addMod =
                new DirectoryAttributeModification();
            addMod.Name = "unicodePwd";
            addMod.Add(GetPasswordData(newPassword));
            addMod.Operation = DirectoryAttributeOperation.Add;

        ModifyRequest request = new ModifyRequest(
            userDN,
            deleteMod,
            addMod
            );

        DirectoryResponse response =
            connection.SendRequest(request);
    }

    private static void SetPassword(
        DirectoryConnection connection,
        string userDN,
        string password
        )
    {

        DirectoryAttributeModification pwdMod =
            new DirectoryAttributeModification();
        pwdMod.Name = "unicodePwd";
        pwdMod.Add(GetPasswordData(password));
        pwdMod.Operation = DirectoryAttributeOperation.Replace;

        ModifyRequest request = new ModifyRequest(
            userDN,
            pwdMod
            );

        DirectoryResponse response =
            connection.SendRequest(request);

    }

    private static byte[] GetPasswordData(string password)
    {
        string formattedPassword;
        formattedPassword = String.Format("\"{0}\"", password);
        return (Encoding.Unicode.GetBytes(formattedPassword));
    }
}

In my case this is somehow related to the domain trust but I guess any time 0x8007202 appears during setpassword and you just can’t get it to go away I’d suggest giving this method a go!

0