Advertisement
Guest User

Untitled

a guest
Mar 6th, 2017
92
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.46 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Web;
  6. using System.Net.Http;
  7. using System.Net.Http.Headers;
  8. using System.Security.Principal;
  9. using System.Threading;
  10. using System.DirectoryServices.AccountManagement;
  11.  
  12. namespace MyApp.Security
  13. {
  14. public class BasicAuthMessageHandler : DelegatingHandler
  15. {
  16.  
  17. protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  18. {
  19. AuthenticationHeaderValue authValue = request.Headers.Authorization;
  20. if (authValue != null && !String.IsNullOrWhiteSpace(authValue.Parameter))
  21. {
  22. string username = null;
  23. string password = null;
  24. string toSplit = Encoding.UTF8.GetString(Convert.FromBase64String(authValue.Parameter));
  25. int index;
  26. if ((index = toSplit.IndexOf(":")) != -1)//verifies that they included at least the : between the username and password
  27. {
  28. username = toSplit.Substring(0, index);
  29. password = toSplit.Substring(index + 1);
  30. }
  31. //makes sure that there IS some semblance of a username and password. Blanks aren't allowed.
  32. if ( !string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
  33. {
  34. IPrincipal currentPrincipal = CreatePrincipal(username, password);//Validates the credentials and creates a principal containing their domain groups
  35. Thread.CurrentPrincipal = currentPrincipal;//sets the principal for the thread
  36. HttpContext.Current.User = currentPrincipal;//sets the principal for the HttpContext
  37. }
  38. }
  39. return base.SendAsync(request, cancellationToken);
  40. }
  41. private IPrincipal CreatePrincipal(String username, String password)
  42. {
  43. PrincipalContext ctx = new PrincipalContext(ContextType.Domain);//Sets up a context for the domain the app is running in
  44. if (username.ToLower() == "token")//special case where the username is "token" means we're using token authentication instead of normal basic authentication
  45. {
  46. username = TokenCache.localTokenCache.validateToken(password);//checks for presence of the token in the cache and grabs the username associated with it if any.
  47. if (username == null)
  48. {
  49. return null;//No token? No login!
  50. }
  51. else
  52. {
  53. UserPrincipal user = UserPrincipal.FindByIdentity(ctx, username);//Grab the necessary information about the user from the domain.
  54. if (user.Enabled == true)//make sure their account isn't disabled.
  55. {
  56. //SIDs can be used to check NTFS file access permissions before doing an operation on behalf of the user.
  57. List<string> groups = new List<string>(user.GetAuthorizationGroups().Select(i => i.Sid.Value));//grab the SIDs from any groups they're in
  58. groups.Add(user.Sid.Value);//also add their own SID
  59. groups.Add("Auth:Token");//Lets me check later from APIs if they authenticated with a token or username and password.
  60. //Note that here I'm using the "Authentication Type" part of generic identity to store the token if I make one.
  61. //This is a convienient place to put a string that can be used by API methods later in the pipeline.
  62. return new GenericPrincipal(new GenericIdentity(user.SamAccountName, ""), groups.ToArray());//Creates and returns the Principal
  63. }
  64. else
  65. {
  66. return null;//disabled account? No login!
  67. }
  68. }
  69. }
  70. if (ctx.ValidateCredentials(username, password))//if they give an actual username and password, we authenticate against the domain!
  71. {
  72. UserPrincipal user = UserPrincipal.FindByIdentity(ctx, username);//Grab the necessary information about the user from the domain.
  73. string token = TokenCache.localTokenCache.generateNewToken(user.SamAccountName);//generate a new token for the user
  74. if (user.Enabled == true)//make sure their account isn't disabled.
  75. {
  76. //SIDs can be used to check NTFS file access permissions before doing an operation on behalf of the user.
  77. List<string> groups = new List<string>(user.GetAuthorizationGroups().Select(i => i.Sid.Value));//grab the SIDs from any groups they're in
  78. groups.Add(user.Sid.Value);//also add their own SID
  79. groups.Add("Yeti:Basic");//Lets me check later from APIs if they authenticated with a token or username and password.
  80. //Note that here I'm using the "Authentication Type" part of generic identity to store the token if I make one.
  81. //This is a convienient place to put a string that can be used by API methods later in the pipeline.
  82. return new GenericPrincipal(new GenericIdentity(user.SamAccountName, token), groups.ToArray());//Creates and returns the Principal
  83. }
  84. else
  85. {
  86. return null;//disabled account? No login!
  87. }
  88. }
  89. return null;//invalid username and password? No login!
  90. }
  91. }
  92. }
  93.  
  94. using System;
  95. using System.Security.Cryptography;
  96. using System.Collections.Generic;
  97.  
  98. namespace MyApp.Security
  99. {
  100. class TokenCache : IDisposable
  101. {
  102. public static readonly TokenCache localTokenCache = new TokenCache(new TimeSpan(20,0,0));//Singleton where tokens expire after 20 hours
  103. private Dictionary<string, string> userToToken = new Dictionary<string, string>();
  104. private Dictionary<string, string> tokenToUser = new Dictionary<string, string>();
  105. private Dictionary<string, DateTime> tokenToDate = new Dictionary<string, DateTime>();
  106.  
  107. private RNGCryptoServiceProvider numgen;
  108. private TimeSpan _maxValidity;
  109. private TokenCache(TimeSpan maxValidity)
  110. {
  111. _maxValidity = maxValidity;
  112. numgen = new RNGCryptoServiceProvider();
  113. }
  114. public string validateToken(string token)
  115. {
  116. if (tokenToUser.ContainsKey(token) && DateTime.Now - tokenToDate[token] < _maxValidity)//do we have the token, and is it unexpired?
  117. {
  118. return tokenToUser[token];//Yes? Return username.
  119. }else
  120. {
  121. return null;//No? Then return null;
  122. }
  123. }
  124. public string generateNewToken(string username)
  125. {
  126. byte[] tokenarr = new byte[33];
  127. numgen.GetBytes(tokenarr);
  128. string token = SimpleMethods.Base642URL(Convert.ToBase64String(tokenarr));//Makes a URL friendly token
  129. DateTime createdon = DateTime.Now;
  130. removePreviousTokens(username, service);
  131. tokenToUser.Add(token, username);
  132. userToToken.Add(username, token);
  133. tokenToDate.Add(token, createdon);
  134. return token;
  135. }
  136. private void removePreviousTokens(string username)
  137. {
  138. if(userToToken.ContainsKey(username))
  139. {
  140. string token = userToToken[username];
  141. tokenToUser.Remove(token);
  142. userToToken.Remove(username);
  143. tokenToDate.Remove(token);
  144. }
  145. }
  146.  
  147. #region IDisposable Support
  148. private bool disposedValue = false; // To detect redundant calls
  149.  
  150. protected virtual void Dispose(bool disposing)
  151. {
  152. if (!disposedValue)
  153. {
  154. if (disposing)
  155. {
  156. numgen.Dispose();
  157. }
  158. tokenToDate = null;
  159. tokenToUser = null;
  160. userToToken = null;
  161. disposedValue = true;
  162. }
  163. }
  164.  
  165. public void Dispose()
  166. {
  167. Dispose(true);
  168. }
  169. #endregion
  170. }
  171. }
  172.  
  173. public static string Base642URL(string base64String)
  174. {
  175. base64String = base64String.Replace("+", "-");
  176. base64String = base64String.Replace("/", "_");
  177. base64String = base64String.Replace("=", "");
  178. return base64String;
  179. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement