summaryrefslogtreecommitdiffstats
path: root/src/OpenID/OpenIdProviderMvc/Code
diff options
context:
space:
mode:
Diffstat (limited to 'src/OpenID/OpenIdProviderMvc/Code')
-rw-r--r--src/OpenID/OpenIdProviderMvc/Code/AccountMembershipService.cs40
-rw-r--r--src/OpenID/OpenIdProviderMvc/Code/AnonymousIdentifierProvider.cs42
-rw-r--r--src/OpenID/OpenIdProviderMvc/Code/FormsAuthenticationService.cs21
-rw-r--r--src/OpenID/OpenIdProviderMvc/Code/IFormsAuthentication.cs24
-rw-r--r--src/OpenID/OpenIdProviderMvc/Code/IMembershipService.cs40
-rw-r--r--src/OpenID/OpenIdProviderMvc/Code/ReadOnlyXmlMembershipProvider.cs279
-rw-r--r--src/OpenID/OpenIdProviderMvc/Code/Util.cs17
7 files changed, 463 insertions, 0 deletions
diff --git a/src/OpenID/OpenIdProviderMvc/Code/AccountMembershipService.cs b/src/OpenID/OpenIdProviderMvc/Code/AccountMembershipService.cs
new file mode 100644
index 0000000..1a5dfd4
--- /dev/null
+++ b/src/OpenID/OpenIdProviderMvc/Code/AccountMembershipService.cs
@@ -0,0 +1,40 @@
+namespace OpenIdProviderMvc.Code {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Web;
+ using System.Web.Security;
+
+ public class AccountMembershipService : IMembershipService {
+ private MembershipProvider provider;
+
+ public AccountMembershipService()
+ : this(null) {
+ }
+
+ public AccountMembershipService(MembershipProvider provider) {
+ this.provider = provider ?? Membership.Provider;
+ }
+
+ public int MinPasswordLength {
+ get {
+ return this.provider.MinRequiredPasswordLength;
+ }
+ }
+
+ public bool ValidateUser(string userName, string password) {
+ return this.provider.ValidateUser(userName, password);
+ }
+
+ public MembershipCreateStatus CreateUser(string userName, string password, string email) {
+ MembershipCreateStatus status;
+ this.provider.CreateUser(userName, password, email, null, null, true, null, out status);
+ return status;
+ }
+
+ public bool ChangePassword(string userName, string oldPassword, string newPassword) {
+ MembershipUser currentUser = this.provider.GetUser(userName, true /* userIsOnline */);
+ return currentUser.ChangePassword(oldPassword, newPassword);
+ }
+ }
+}
diff --git a/src/OpenID/OpenIdProviderMvc/Code/AnonymousIdentifierProvider.cs b/src/OpenID/OpenIdProviderMvc/Code/AnonymousIdentifierProvider.cs
new file mode 100644
index 0000000..6dc210d
--- /dev/null
+++ b/src/OpenID/OpenIdProviderMvc/Code/AnonymousIdentifierProvider.cs
@@ -0,0 +1,42 @@
+namespace OpenIdProviderMvc.Code {
+ using System;
+ using System.Web.Security;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Provider;
+ using OpenIdProviderMvc.Models;
+
+ internal class AnonymousIdentifierProvider : PrivatePersonalIdentifierProviderBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AnonymousIdentifierProvider"/> class.
+ /// </summary>
+ internal AnonymousIdentifierProvider()
+ : base(Util.GetAppPathRootedUri("anon?id=")) {
+ }
+
+ /// <summary>
+ /// Gets the salt to use for generating an anonymous identifier for a given OP local identifier.
+ /// </summary>
+ /// <param name="localIdentifier">The OP local identifier.</param>
+ /// <returns>The salt to use in the hash.</returns>
+ /// <remarks>
+ /// It is important that this method always return the same value for a given
+ /// <paramref name="localIdentifier"/>.
+ /// New salts can be generated for local identifiers without previously assigned salt
+ /// values by calling <see cref="CreateSalt"/> or by a custom method.
+ /// </remarks>
+ protected override byte[] GetHashSaltForLocalIdentifier(Identifier localIdentifier) {
+ // This is just a sample with no database... a real web app MUST return
+ // a reasonable salt here and have that salt be persistent for each user.
+ var membership = (ReadOnlyXmlMembershipProvider)Membership.Provider;
+ string username = User.GetUserFromClaimedIdentifier(new Uri(localIdentifier));
+ string salt = membership.GetSalt(username);
+ return Convert.FromBase64String(salt);
+
+ // If users were encountered without a salt, one could be generated like this,
+ // and would also need to be saved to the user's account.
+ //// var newSalt = AnonymousIdentifierProviderBase.GetNewSalt(5);
+ //// user.Salt = newSalt;
+ //// return newSalt;
+ }
+ }
+}
diff --git a/src/OpenID/OpenIdProviderMvc/Code/FormsAuthenticationService.cs b/src/OpenID/OpenIdProviderMvc/Code/FormsAuthenticationService.cs
new file mode 100644
index 0000000..22db860
--- /dev/null
+++ b/src/OpenID/OpenIdProviderMvc/Code/FormsAuthenticationService.cs
@@ -0,0 +1,21 @@
+namespace OpenIdProviderMvc.Code {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Web;
+ using System.Web.Security;
+
+ public class FormsAuthenticationService : IFormsAuthentication {
+ public string SignedInUsername {
+ get { return HttpContext.Current.User.Identity.Name; }
+ }
+
+ public void SignIn(string userName, bool createPersistentCookie) {
+ FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
+ }
+
+ public void SignOut() {
+ FormsAuthentication.SignOut();
+ }
+ }
+}
diff --git a/src/OpenID/OpenIdProviderMvc/Code/IFormsAuthentication.cs b/src/OpenID/OpenIdProviderMvc/Code/IFormsAuthentication.cs
new file mode 100644
index 0000000..d4c8a01
--- /dev/null
+++ b/src/OpenID/OpenIdProviderMvc/Code/IFormsAuthentication.cs
@@ -0,0 +1,24 @@
+namespace OpenIdProviderMvc.Code {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Web;
+using System.Web.Security;
+
+ /// <summary>
+ /// An interface that wraps the <see cref="FormsAuthentication"/> type.
+ /// </summary>
+ /// <remarks>
+ /// The FormsAuthentication type is sealed and contains static members, so it is difficult to
+ /// unit test code that calls its members. The interface and helper class below demonstrate
+ /// how to create an abstract wrapper around such a type in order to make the AccountController
+ /// code unit testable.
+ /// </remarks>
+ public interface IFormsAuthentication {
+ string SignedInUsername { get; }
+
+ void SignIn(string userName, bool createPersistentCookie);
+
+ void SignOut();
+ }
+}
diff --git a/src/OpenID/OpenIdProviderMvc/Code/IMembershipService.cs b/src/OpenID/OpenIdProviderMvc/Code/IMembershipService.cs
new file mode 100644
index 0000000..94459f8
--- /dev/null
+++ b/src/OpenID/OpenIdProviderMvc/Code/IMembershipService.cs
@@ -0,0 +1,40 @@
+namespace OpenIdProviderMvc.Code {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Web;
+ using System.Web.Security;
+
+ public interface IMembershipService {
+ /// <summary>
+ /// Gets the length of the min password.
+ /// </summary>
+ int MinPasswordLength { get; }
+
+ /// <summary>
+ /// Validates the user.
+ /// </summary>
+ /// <param name="userName">Name of the user.</param>
+ /// <param name="password">The password.</param>
+ /// <returns>Whether the given username and password is correct.</returns>
+ bool ValidateUser(string userName, string password);
+
+ /// <summary>
+ /// Creates a new user account.
+ /// </summary>
+ /// <param name="userName">Name of the user.</param>
+ /// <param name="password">The password.</param>
+ /// <param name="email">The email.</param>
+ /// <returns>The success or reason for failure of account creation.</returns>
+ MembershipCreateStatus CreateUser(string userName, string password, string email);
+
+ /// <summary>
+ /// Changes the password for a user.
+ /// </summary>
+ /// <param name="userName">Name of the user.</param>
+ /// <param name="oldPassword">The old password.</param>
+ /// <param name="newPassword">The new password.</param>
+ /// <returns>A value indicating whether the password change was successful.</returns>
+ bool ChangePassword(string userName, string oldPassword, string newPassword);
+ }
+}
diff --git a/src/OpenID/OpenIdProviderMvc/Code/ReadOnlyXmlMembershipProvider.cs b/src/OpenID/OpenIdProviderMvc/Code/ReadOnlyXmlMembershipProvider.cs
new file mode 100644
index 0000000..d66573f
--- /dev/null
+++ b/src/OpenID/OpenIdProviderMvc/Code/ReadOnlyXmlMembershipProvider.cs
@@ -0,0 +1,279 @@
+namespace OpenIdProviderMvc.Code {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.Configuration.Provider;
+ using System.Security.Permissions;
+ using System.Web;
+ using System.Web.Hosting;
+ using System.Web.Security;
+ using System.Xml;
+
+ public class ReadOnlyXmlMembershipProvider : MembershipProvider {
+ private Dictionary<string, MembershipUser> users;
+ private string xmlFileName;
+
+ // MembershipProvider Properties
+ public override string ApplicationName {
+ get { throw new NotSupportedException(); }
+ set { throw new NotSupportedException(); }
+ }
+
+ public override bool EnablePasswordRetrieval {
+ get { return false; }
+ }
+
+ public override bool EnablePasswordReset {
+ get { return false; }
+ }
+
+ public override int MaxInvalidPasswordAttempts {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override int MinRequiredNonAlphanumericCharacters {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override int MinRequiredPasswordLength {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override int PasswordAttemptWindow {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override MembershipPasswordFormat PasswordFormat {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override string PasswordStrengthRegularExpression {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override bool RequiresQuestionAndAnswer {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override bool RequiresUniqueEmail {
+ get { throw new NotSupportedException(); }
+ }
+
+ // MembershipProvider Methods
+ public override void Initialize(string name, NameValueCollection config) {
+ // Verify that config isn't null
+ if (config == null) {
+ throw new ArgumentNullException("config");
+ }
+
+ // Assign the provider a default name if it doesn't have one
+ if (string.IsNullOrEmpty(name)) {
+ name = "ReadOnlyXmlMembershipProvider";
+ }
+
+ // Add a default "description" attribute to config if the
+ // attribute doesn't exist or is empty
+ if (string.IsNullOrEmpty(config["description"])) {
+ config.Remove("description");
+ config.Add("description", "Read-only XML membership provider");
+ }
+
+ // Call the base class's Initialize method
+ base.Initialize(name, config);
+
+ // Initialize _XmlFileName and make sure the path
+ // is app-relative
+ string path = config["xmlFileName"];
+
+ if (string.IsNullOrEmpty(path)) {
+ path = "~/App_Data/Users.xml";
+ }
+
+ if (!VirtualPathUtility.IsAppRelative(path)) {
+ throw new ArgumentException("xmlFileName must be app-relative");
+ }
+
+ string fullyQualifiedPath = VirtualPathUtility.Combine(
+ VirtualPathUtility.AppendTrailingSlash(HttpRuntime.AppDomainAppVirtualPath),
+ path);
+
+ this.xmlFileName = HostingEnvironment.MapPath(fullyQualifiedPath);
+ config.Remove("xmlFileName");
+
+ // Make sure we have permission to read the XML data source and
+ // throw an exception if we don't
+ FileIOPermission permission = new FileIOPermission(FileIOPermissionAccess.Read, this.xmlFileName);
+ permission.Demand();
+
+ // Throw an exception if unrecognized attributes remain
+ if (config.Count > 0) {
+ string attr = config.GetKey(0);
+ if (!string.IsNullOrEmpty(attr)) {
+ throw new ProviderException("Unrecognized attribute: " + attr);
+ }
+ }
+ }
+
+ public override bool ValidateUser(string username, string password) {
+ // Validate input parameters
+ if (string.IsNullOrEmpty(username) ||
+ string.IsNullOrEmpty(password)) {
+ return false;
+ }
+
+ try {
+ // Make sure the data source has been loaded
+ this.ReadMembershipDataStore();
+
+ // Validate the user name and password
+ MembershipUser user;
+ if (this.users.TryGetValue(username, out user)) {
+ if (user.Comment == password) { // Case-sensitive
+ // NOTE: A read/write membership provider
+ // would update the user's LastLoginDate here.
+ // A fully featured provider would also fire
+ // an AuditMembershipAuthenticationSuccess
+ // Web event
+ return true;
+ }
+ }
+
+ // NOTE: A fully featured membership provider would
+ // fire an AuditMembershipAuthenticationFailure
+ // Web event here
+ return false;
+ } catch (Exception) {
+ return false;
+ }
+ }
+
+ public override MembershipUser GetUser(string username, bool userIsOnline) {
+ // Note: This implementation ignores userIsOnline
+
+ // Validate input parameters
+ if (string.IsNullOrEmpty(username)) {
+ return null;
+ }
+
+ // Make sure the data source has been loaded
+ this.ReadMembershipDataStore();
+
+ // Retrieve the user from the data source
+ MembershipUser user;
+ if (this.users.TryGetValue(username, out user)) {
+ return user;
+ }
+
+ return null;
+ }
+
+ public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords) {
+ // Note: This implementation ignores pageIndex and pageSize,
+ // and it doesn't sort the MembershipUser objects returned
+
+ // Make sure the data source has been loaded
+ this.ReadMembershipDataStore();
+
+ MembershipUserCollection users = new MembershipUserCollection();
+
+ foreach (KeyValuePair<string, MembershipUser> pair in this.users) {
+ users.Add(pair.Value);
+ }
+
+ totalRecords = users.Count;
+ return users;
+ }
+
+ public override int GetNumberOfUsersOnline() {
+ throw new NotSupportedException();
+ }
+
+ public override bool ChangePassword(string username, string oldPassword, string newPassword) {
+ throw new NotSupportedException();
+ }
+
+ public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) {
+ throw new NotSupportedException();
+ }
+
+ public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) {
+ throw new NotSupportedException();
+ }
+
+ public override bool DeleteUser(string username, bool deleteAllRelatedData) {
+ throw new NotSupportedException();
+ }
+
+ public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) {
+ throw new NotSupportedException();
+ }
+
+ public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) {
+ throw new NotSupportedException();
+ }
+
+ public override string GetPassword(string username, string answer) {
+ throw new NotSupportedException();
+ }
+
+ public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) {
+ throw new NotSupportedException();
+ }
+
+ public override string GetUserNameByEmail(string email) {
+ throw new NotSupportedException();
+ }
+
+ public override string ResetPassword(string username, string answer) {
+ throw new NotSupportedException();
+ }
+
+ public override bool UnlockUser(string userName) {
+ throw new NotSupportedException();
+ }
+
+ public override void UpdateUser(MembershipUser user) {
+ throw new NotSupportedException();
+ }
+
+ internal string GetSalt(string userName) {
+ // This is just a sample with no database... a real web app MUST return
+ // a reasonable salt here and have that salt be persistent for each user.
+ this.ReadMembershipDataStore();
+ return this.users[userName].Email;
+ }
+
+ // Helper method
+ private void ReadMembershipDataStore() {
+ lock (this) {
+ if (this.users == null) {
+ this.users = new Dictionary<string, MembershipUser>(16, StringComparer.InvariantCultureIgnoreCase);
+ XmlDocument doc = new XmlDocument();
+ doc.Load(this.xmlFileName);
+ XmlNodeList nodes = doc.GetElementsByTagName("User");
+
+ foreach (XmlNode node in nodes) {
+ // Yes, we're misusing some of these fields. A real app would
+ // have the right fields from a database to use.
+ MembershipUser user = new MembershipUser(
+ Name, // Provider name
+ node["UserName"].InnerText, // Username
+ null, // providerUserKey
+ node["Salt"].InnerText, // Email
+ string.Empty, // passwordQuestion
+ node["Password"].InnerText, // Comment
+ true, // isApproved
+ false, // isLockedOut
+ DateTime.Now, // creationDate
+ DateTime.Now, // lastLoginDate
+ DateTime.Now, // lastActivityDate
+ DateTime.Now, // lastPasswordChangedDate
+ new DateTime(1980, 1, 1)); // lastLockoutDate
+
+ this.users.Add(user.UserName, user);
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/OpenID/OpenIdProviderMvc/Code/Util.cs b/src/OpenID/OpenIdProviderMvc/Code/Util.cs
new file mode 100644
index 0000000..6623952
--- /dev/null
+++ b/src/OpenID/OpenIdProviderMvc/Code/Util.cs
@@ -0,0 +1,17 @@
+namespace OpenIdProviderMvc.Code {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Web;
+
+ internal static class Util {
+ internal static Uri GetAppPathRootedUri(string value) {
+ string appPath = HttpContext.Current.Request.ApplicationPath.ToLowerInvariant();
+ if (!appPath.EndsWith("/")) {
+ appPath += "/";
+ }
+
+ return new Uri(HttpContext.Current.Request.Url, appPath + value);
+ }
+ }
+}