views:

297

answers:

3

I need a WCF service to sit on top of ASPNET membership and allow me to call all the methods of ASPNET membership such as validateUser, createUser etc.

I need this becasue I have out-of-firewall apps that need to access a single central ASPNET membership database. If they were all inside the firewall I'd just point the connectionstring to the database and be done.

Microsoft provides this service : WCF Authentication Service

When I saw this I thought 'great - thats just what I need'. Then I looked at the interface :

public interface AuthenticationService {

    bool ValidateUser(string username, string password, string customCredential);
    bool Login(string username, string password, string customCredential, bool isPersistent);
    bool IsLoggedIn();
    void Logout();
}

Uh oh! No 'CreateUser' etc.

I found a Codeproject which basically does what I want : Custom MembershipProvider and RoleProvider Implementations that use Web Services. The problem is it is ASMX and I need WFC. It is also a tremendous amount of code and I don't know how well it is tested.

I'm thinking I may have to just create my own service to do this - but I'm sure there must be an existing example of this out there. I just haven't found it yet. I'm slightly weary of writing it myself because there are always catches when it comes to security and I don't want to miss anything.

Fortunately I'm using the IMembershipService interface in ASP.NET MVC so I can pretty easily slot in whatever I want to. Is my own service the easiest way ?

+1  A: 

Most easy way is just routing the CreateUser function in your WCF service to your membership providers CreateUser function?

ResponseType CreateUser(UserContainer user);

implemented

public ResponseType CreateUser(UserContainer user)
{
    //map user -> a real user object
    RealUser realUser = Convert(user);
    new MyMembershipProvider().CreateUser(realUserObject);
    return true;
}
Jan Jongboom
im thinking i'll have to do that, but I'm sure theres a complete solution out there already. i'd want everything in one place and not use both my service and the MS one at the same time. oh and i think you need to 'return CreateUser(...)' instead of true
Simon_Weaver
The whole idea of some WCF service is that you explicitly need to tell your service which functions need to be exposed to your clients. Therefore you just write some wrapper service, which doesn't do anything than just throw functions around to your real business code (like here your membership code). And of course this is just some pseudo code :-)
Jan Jongboom
ya - lets face it i could probably have written the service by now in the time i spent researching the options. i'm just trying to learn the best approach - especially if one already exists.
Simon_Weaver
+2  A: 

Give this a shot. You MUST SECURE THIS ENDPOINT!

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Text.RegularExpressions;
using System.Web.Security;
namespace Salient.ServiceModel.AspNetProviders
{
    [ServiceContract(Namespace = "Salient.ServiceModel.AspNetProviders", Name = "MembershipProvider")]
    public interface IMembershipProvider
    {
        string ProviderName { get; set; }

        [OperationContract]
        ProviderProperties GetProviderProperties();
        [OperationContract]
        MembershipUser GetUserByKey(object providerUserKey, bool userIsOnline);
        [OperationContract]
        MembershipCreateResult CreateUser(string username, string password,
                                          string email, string passwordQuestion,
                                          string passwordAnswer, bool isApproved,
                                          object providerUserKey);
        [OperationContract]
        MembershipCreateResult AddUser(MembershipUser user);
        [OperationContract]
        bool ChangePasswordQuestionAndAnswer(string username, string password,
                                             string newPasswordQuestion, string newPasswordAnswer);
        [OperationContract]
        string GetPassword(string username, string answer);
        [OperationContract]
        bool ChangePassword(string username, string oldPassword, string newPassword);
        [OperationContract]
        string ResetPassword(string username, string answer);
        [OperationContract]
        void UpdateUser(MembershipUser user);
        [OperationContract]
        bool ValidateUser(string username, string password);
        [OperationContract]
        bool UnlockUser(string userName);
        [OperationContract]
        MembershipUser GetUserByName(string username, bool userIsOnline);
        [OperationContract]
        string GetUserNameByEmail(string email);
        [OperationContract]
        bool DeleteUser(string username, bool deleteAllRelatedData);
        [OperationContract]
        MembershipFindResult GetAllUsers(int pageIndex, int pageSize);
        [OperationContract]
        int GetNumberOfUsersOnline();
        [OperationContract]
        MembershipFindResult FindUsersByName(string usernameToMatch, int pageIndex,
                                             int pageSize);
        [OperationContract]
        MembershipFindResult FindUsersByEmail(string emailToMatch, int pageIndex,
                                              int pageSize);
    }
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class MembershipProvider : IMembershipProvider
    {
        private System.Web.Security.MembershipProvider Provider
        {
            get
            {
                if (string.IsNullOrEmpty(ProviderName))
                {
                    return Membership.Provider;
                }
                return Membership.Providers[ProviderName];
            }
        }
        #region IMembershipProvider Members
        public DateTime TestInput(DateTime date)
        {
            return date;
        }
        public string ProviderName { get; set; }
        public ProviderProperties GetProviderProperties()
        {
            var returnValue = new ProviderProperties
                                  {
                                      ApplicationName = Provider.ApplicationName,
                                      EnablePasswordReset = Provider.EnablePasswordReset,
                                      EnablePasswordRetrieval = Provider.EnablePasswordRetrieval,
                                      MaxInvalidPasswordAttempts = Provider.MaxInvalidPasswordAttempts,
                                      MinRequiredNonAlphanumericCharacters =
                                          Provider.MinRequiredNonAlphanumericCharacters,
                                      MinRequiredPasswordLength = Provider.MinRequiredPasswordLength,
                                      PasswordAttemptWindow = Provider.PasswordAttemptWindow,
                                      PasswordFormat = Provider.PasswordFormat,
                                      PasswordStrengthRegularExpression = Provider.PasswordStrengthRegularExpression,
                                      RequiresQuestionAndAnswer = Provider.RequiresQuestionAndAnswer,
                                      RequiresUniqueEmail = Provider.RequiresUniqueEmail
                                  };
            return returnValue;
        }
        public MembershipUser GetUserByKey(object providerUserKey, bool userIsOnline)
        {
            return Provider.GetUser(providerUserKey, userIsOnline);
        }
        public MembershipCreateResult CreateUser(string username, string password,
                                                 string email, string passwordQuestion,
                                                 string passwordAnswer, bool isApproved,
                                                 object providerUserKey)
        {
            MembershipCreateStatus status;
            MembershipUser user = Provider.CreateUser(username, password, email, passwordQuestion, passwordAnswer,
                                                      isApproved,
                                                      providerUserKey, out status);
            return new MembershipCreateResult(user, status);
        }
        public MembershipCreateResult AddUser(MembershipUser user)
        {
            return new MembershipCreateResult(user, MembershipCreateStatus.DuplicateEmail);
        }
        public bool ChangePasswordQuestionAndAnswer(string username, string password,
                                                    string newPasswordQuestion, string newPasswordAnswer)
        {
            return Provider.ChangePasswordQuestionAndAnswer(username, password, newPasswordQuestion, newPasswordAnswer);
        }
        public string GetPassword(string username, string answer)
        {
            return Provider.GetPassword(username, answer);
        }
        public bool ChangePassword(string username, string oldPassword, string newPassword)
        {
            return Provider.ChangePassword(username, oldPassword, newPassword);
        }
        public string ResetPassword(string username, string answer)
        {
            return Provider.ResetPassword(username, answer);
        }
        public void UpdateUser(MembershipUser user)
        {
            Provider.UpdateUser(user);
        }
        public bool ValidateUser(string username, string password)
        {
            return Provider.ValidateUser(username, password);
        }
        public bool UnlockUser(string userName)
        {
            return Provider.UnlockUser(userName);
        }
        public MembershipUser GetUserByName(string username, bool userIsOnline)
        {
            return Provider.GetUser(username, userIsOnline);
        }
        public string GetUserNameByEmail(string email)
        {
            return Provider.GetUserNameByEmail(email);
        }
        public bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            return Provider.DeleteUser(username, deleteAllRelatedData);
        }
        public MembershipFindResult GetAllUsers(int pageIndex, int pageSize)
        {
            int totalRecords;
            MembershipUserCollection users = Provider.GetAllUsers(pageIndex, pageSize, out totalRecords);
            var list = new List<MembershipUser>();
            foreach (MembershipUser user in users)
            {
                list.Add(user);
            }
            return new MembershipFindResult(list, totalRecords);
        }
        public int GetNumberOfUsersOnline()
        {
            return Provider.GetNumberOfUsersOnline();
        }
        public MembershipFindResult FindUsersByName(string usernameToMatch, int pageIndex,
                                                    int pageSize)
        {
            int totalRecords;
            MembershipUserCollection users = Provider.FindUsersByName(usernameToMatch, pageIndex, pageSize,
                                                                      out totalRecords);
            var list = new List<MembershipUser>();
            foreach (MembershipUser user in users)
            {
                list.Add(user);
            }
            return new MembershipFindResult(list, totalRecords);
        }
        public MembershipFindResult FindUsersByEmail(string emailToMatch, int pageIndex,
                                                     int pageSize)
        {
            int totalRecords;
            MembershipUserCollection users = Provider.FindUsersByEmail(emailToMatch, pageIndex, pageSize,
                                                                       out totalRecords);
            var list = new List<MembershipUser>();
            foreach (MembershipUser user in users)
            {
                list.Add(user);
            }
            return new MembershipFindResult(list, totalRecords);
        }
        #endregion
    }
    [DataContract]
    public class MembershipFindResult
    {
        [DataMember]
        public int RecordCount;
        [DataMember]
        public IEnumerable<MembershipUser> Users;
        public MembershipFindResult()
        {
        }
        public MembershipFindResult(IEnumerable<MembershipUser> users, int recordCount)
        {
            Users = users;
            RecordCount = recordCount;
        }
    }
    [DataContract]
    public class MembershipCreateResult
    {
        [DataMember]
        public MembershipCreateStatus CreateStatus;
        [DataMember]
        public MembershipUser User;
        public MembershipCreateResult()
        {
        }
        public MembershipCreateResult(MembershipUser user, MembershipCreateStatus createStatus)
        {
            User = user;
            CreateStatus = createStatus;
        }
    }
    [DataContract]
    public class ProviderProperties
    {
        public ProviderProperties()
        {
        }
        public ProviderProperties(bool enablePasswordRetrieval, bool enablePasswordReset, bool requiresQuestionAndAnswer,
                                  int maxInvalidPasswordAttempts,
                                  int passwordAttemptWindow, bool requiresUniqueEmail,
                                  MembershipPasswordFormat passwordFormat, int minRequiredPasswordLength,
                                  int minRequiredNonAlphanumericCharacters,
                                  string passwordStrengthRegularExpression, string applicationName)
        {
            EnablePasswordRetrieval = enablePasswordRetrieval;
            EnablePasswordReset = enablePasswordReset;
            RequiresQuestionAndAnswer = requiresQuestionAndAnswer;
            MaxInvalidPasswordAttempts = maxInvalidPasswordAttempts;
            PasswordAttemptWindow = passwordAttemptWindow;
            RequiresUniqueEmail = requiresUniqueEmail;
            PasswordFormat = passwordFormat;
            MinRequiredPasswordLength = minRequiredPasswordLength;
            MinRequiredNonAlphanumericCharacters = minRequiredNonAlphanumericCharacters;
            PasswordStrengthRegularExpression = passwordStrengthRegularExpression;
            ApplicationName = applicationName;
        }
        [DataMember]
        public bool EnablePasswordRetrieval { get;  set; }
        [DataMember]
        public bool EnablePasswordReset { get;  set; }
        [DataMember]
        public bool RequiresQuestionAndAnswer { get;  set; }
        [DataMember]
        public int MaxInvalidPasswordAttempts { get;  set; }
        [DataMember]
        public int PasswordAttemptWindow { get;  set; }
        [DataMember]
        public bool RequiresUniqueEmail { get;  set; }
        [DataMember]
        public MembershipPasswordFormat PasswordFormat { get;  set; }
        [DataMember]
        public int MinRequiredPasswordLength { get;  set; }
        [DataMember]
        public int MinRequiredNonAlphanumericCharacters { get;  set; }
        [DataMember]
        public string PasswordStrengthRegularExpression { get;  set; }
        [DataMember]
        public string ApplicationName { get;  set; }
    }
}

did I mention that you MUST SECURE THIS ENDPOINT???

Sky Sanders
great. thanks!!(ps. i had to put two exclamations to make this comment long enough to be accepted - but it probably deserves it anyway)
Simon_Weaver
I knocked that out a while back and have only tested a handful of the operations but previous incarnations of that class functioned well. Will get you started anyway.... glad I could help.
Sky Sanders
+1 you've just saved me a bunch of time - thank you.
Tr1stan
A: 

This is exactly what I have been looking for. Very nice. Thanks.

Jason