Write to Profile fields from module

Topics: Writing modules
Aug 17, 2011 at 7:39 AM

I'm just going to flat out ask if someone can help me write the code for this. I've spent countless hours on it and it's just beyond my ability at this point. Here's what I need to do...

I'm using the AlexZh.WindowsAuthentication module so I can run Orchard on our intranet. The module takes the current logged in Windows user and either creates a new Orchard account for them using their Windows login name as the username or authenticates them if they've previously logged in. Here is the entire service file that handles that

using System;
using System.DirectoryServices;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using Orchard;
using Orchard.ContentManagement;
using Orchard.Data;
using Orchard.Environment.Extensions;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Mvc;
using Orchard.Roles.Models;
using Orchard.Roles.Services;
using Orchard.Security;
using Orchard.Services;
using Orchard.UI.Notify;
using Orchard.Users.Events;
using Orchard.Users.Models;

namespace AlexZh.WindowsAuthentication.Services
{
	[OrchardSuppressDependency("Orchard.Security.Providers.FormsAuthenticationService")]
	public class CustomAuthenticationService : IAuthenticationService
	{
		private readonly FormsAuthenticationService systemAuthenticationService;
		private readonly IMembershipService membershipService;
		private readonly IHttpContextAccessor httpContextAccessor;
		private readonly IRoleService roleService;
		private readonly IRepository<UserRolesPartRecord> userRolesRepository;
		private readonly IWinAuthSettingsService winUserSettingsService;
		private readonly IOrchardServices orchardServices;
		private readonly IEnumerable<IUserEventHandler> userEventHandlers;

		private ILogger logger;

		public ILogger Logger
		{
			get { return logger; }
			set
			{
				logger = value;
				systemAuthenticationService.Logger = logger;
			}
		}

		public Localizer T { get; set; }

		public CustomAuthenticationService(
			IClock clock,
			IContentManager contentManager,
			IMembershipService membershipService,
			IRoleService roleService,
			IHttpContextAccessor httpContextAccessor,
			IRepository<UserRolesPartRecord> userRolesRepository,
			IWinAuthSettingsService winUserSettingsService,
			IOrchardServices orchardServices,
			IEnumerable<IUserEventHandler> userEventHandlers)
		{
			this.membershipService = membershipService;
			this.roleService = roleService;
			this.httpContextAccessor = httpContextAccessor;
			this.userRolesRepository = userRolesRepository;
			this.winUserSettingsService = winUserSettingsService;
			this.orchardServices = orchardServices;
			this.userEventHandlers = userEventHandlers;
			systemAuthenticationService = new FormsAuthenticationService(clock, contentManager, httpContextAccessor);

			Logger = NullLogger.Instance;
			T = NullLocalizer.Instance;
		}

		#region UserName from WindowsIdentity

		private static string GetUserName(IIdentity identity)
		{
			return identity == null ? null : GetUserName(identity.Name);
		}

		private static string GetUserName(string identityName)
		{
			if (string.IsNullOrEmpty(identityName))
				return identityName;
			int index = identityName.IndexOf('\\');
			if (index > 0 && index < (identityName.Length - 1))
			{
				identityName = identityName.Substring(index + 1);
			}
			index = identityName.IndexOf('@');
			if (index > 0 && index < (identityName.Length - 1))
			{
				identityName = identityName.Remove(index);
			}

			return identityName;
		}

		#endregion

		#region Implementation of IAuthenticationService

		public void SignIn(IUser user, bool createPersistentCookie)
		{
			systemAuthenticationService.SignIn(user, createPersistentCookie);
		}

		public void SignOut()
		{
			systemAuthenticationService.SignOut();
		}

		public void SetAuthenticatedUserForRequest(IUser user)
		{
			systemAuthenticationService.SetAuthenticatedUserForRequest(user);
		}

		public IUser GetAuthenticatedUser()
		{
			// Try to use default authentication seervice
			var user = systemAuthenticationService.GetAuthenticatedUser();
			if (user == null)
			{
				var httpContext = httpContextAccessor.Current();

				if (httpContext != null && httpContext.Request.IsAuthenticated && (httpContext.User.Identity is WindowsIdentity))
				{
					// Windows authentication
					var settings = winUserSettingsService.Retrieve();
					if (!settings.EnableWindowsAuthentication)
					{
						orchardServices.Notifier.Add(NotifyType.Error, T("Windows authentication is disabled. Please contact administrator."));
						return null;
					}

					var windowsIdentity = (WindowsIdentity) httpContext.User.Identity;
					string userName = GetUserName(windowsIdentity);
					user = membershipService.GetUser(userName);

					// Creating new user if not exists
					if (user == null)
					{
						string email = settings.EmailDomain;
						if (string.IsNullOrEmpty(email))
							email = null;
						else
							email = userName + "@" + email;

						user = membershipService.CreateUser(new CreateUserParams(
						                                    	userName,
						                                    	Guid.NewGuid().ToString(),
						                                    	email,
						                                    	Guid.NewGuid().ToString(),
						                                    	Guid.NewGuid().ToString(),
						                                    	true));
						// Adding default roles to user
						if (user != null)
						{
							var roles = settings.DefaultRoles.ToArray();

							foreach (var role in roles)
							{
								var roleRecord = roleService.GetRoleByName(role);
								if (roleRecord != null)
									userRolesRepository.Create(new UserRolesPartRecord {Role = roleRecord, UserId = user.Id});
							}
						}
					}
					// Signing In Windows User
					if (user != null)
					{
						SetAuthenticatedUserForRequest(user);
						SignIn(user, false);
						foreach (var userEventHandler in userEventHandlers)
						{
							userEventHandler.LoggedIn(user);
						}
					}
				}
			}

			return user;
		}

		#endregion
	}
}

if you look for the comment

// Creating new user if not exists

this is where the new user is created.

I also have Contrib.Profile installed and I have 2 fields added to the User Profile, FirstName and LastName. What I need to do is update the FirstName and LastName Profile values for the newly created user. I'll be pulling those values from Active Directory, so I'll handle that code. For the purposes of this question, just assume that the values being added to the Profile are sting variables sFirstName and sLastName.

Is there anyone who is willing and able to help me put the code together to update those Profile values for a newly created user in this module? Your assistance will be greatly appreciated...this is basically the only thing that's keeping me from going live on this intranet site. Thanks so much!

Aug 17, 2011 at 3:33 PM

So I assume you've attached the ProfilePart to the User content type. If so, then I believe this code will do what you want to do:

// Note, normally you would want to reference the Contrib.Profile module and simply use: user.ContentItem.As<ProfilePart>()
// However the Profile module does not define a model class for the ProfilePart, it's simply created as metadata by the migration
// So this is another way to get a content part from a content item
var profile = user.ContentItem.Parts.Single(p => p.PartDefinition.Name == "ProfilePart");

// The api for setting field values is a little wonky, but this is how you do it :-)
profile.Fields.Single(f => f.Name == "FirstName").Storage.Set(null, firstName);
profile.Fields.Single(f => f.Name == "LastName").Storage.Set(null, lastName);

 

I hope this helps. Let me know if that doesn't work, or if I misunderstood your question.

Kevin

 

Aug 17, 2011 at 5:30 PM

**bows down before Kevin

You are my savior...works PERFECTLY. I can't thank you enough for that code. I didn't think it would be that simple, but I was no where even close. Wonky works for me.

Thanks again! If you message me your email address, I'd like to send you something as a thank you.

Coordinator
Aug 17, 2011 at 7:29 PM

Even simpler:

dynamic contentItem = user.ContentItem; 
 
contentItem.ProfilePart.FirstName = firstName;

Or at least it’s something very close to that one …

Aug 17, 2011 at 10:02 PM

Ah, thanks Sebastien. I remember you telling me one time that you guys were going to add dynamic access to the Fields, but I didn't realize you had done that. That's definitely better! :-)

@psenechal, no need to send me anything. I'm just glad my code worked for you. :-)  Looks like you can make it even better with Sebastien's suggestion.

Aug 17, 2011 at 10:58 PM

ahhh...it's actually

contentItem.ProfilePart.FirstName.Value = firstName;
works perfect again. You guys rock!

Nov 23, 2011 at 9:25 PM
psenechal wrote:

ahhh...it's actually

 

contentItem.ProfilePart.FirstName.Value = firstName;
works perfect again. You guys rock!

 

This is great.

Can we add roles to the user in a similar way? userRolesRepository is not in scope where I need it.

Thank you

Coordinator
Nov 23, 2011 at 9:27 PM

Well, you don't want to do that in the view do you?

Nov 23, 2011 at 9:34 PM

No, I need it in my custom MembershipService.

Coordinator
Nov 23, 2011 at 9:43 PM

What happens when you try to inject a IRepository<UserRolesPartRecord>?

Nov 23, 2011 at 10:24 PM

It works perfectly, but I wanted a solution that was a bit more challenging... :o)

Coordinator
Nov 23, 2011 at 10:36 PM

:)