Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using Elmah;
- using Microsoft.AspNet.Identity;
- using Microsoft.AspNet.Identity.Owin;
- using Microsoft.Owin;
- using Microsoft.Owin.Security;
- using Newtonsoft.Json;
- using PortalApi.Helpers;
- using PortalApi2.Models.Student;
- using PortalModules;
- using PortalModules.Auth;
- using PortalWeb.Helpers;
- using System;
- using System.Collections.Generic;
- using System.Configuration;
- using System.Data.Entity.Validation;
- using System.Linq;
- using System.Net;
- using System.Net.Http;
- using System.Net.Http.Headers;
- using System.Security.Claims;
- using System.Security.Principal;
- using System.Text.RegularExpressions;
- using System.Threading.Tasks;
- using System.Web;
- using System.Web.Mvc;
- using System.Web.Security;
- using System.Xml;
- namespace PortalWeb.Controllers
- {
- public class AccountController : Controller
- {
- public async Task<ActionResult> AddGradStudents()
- {
- var apictrl = new ApiController();
- var ids = new List<string> { };
- var path = string.Format("{0}\\grads.txt", Server.MapPath("~/App_Data"));
- var file = new System.IO.StreamReader(path);
- do
- {
- ids.Add(file.ReadLine());
- } while (file.Peek() != -1);
- file.Close();
- var ret = new List<string>();
- var failCreate = new List<string>();
- var owinContext = Request.GetOwinContext();
- var userManager = owinContext.GetUserManager<ApplicationUserManager>();
- foreach (var id in ids)
- {
- var result = ((await apictrl.ProxyAdmin("student/terms2", id)) as ContentResult).Content;
- if (result != null)
- {
- var apiResult = JsonConvert.DeserializeObject<ApiCore.Attributes.ApiResult>(result);
- var termData = JsonConvert.DeserializeObject<AcadTermData>(apiResult.data.ToString());
- if (termData.academicTerms != null)
- {
- if (!termData.academicTerms.Any(t => t.termCode == "1179"))
- ret.Add(termData.username);
- }
- else
- {
- var createResult = await createUserIfNotExists(owinContext, userManager, termData.username);
- if(createResult.HasValue && !createResult.Value)
- failCreate.Add(termData.username);
- else
- {
- var user = await userManager.FindByNameAsync(termData.username);
- if(!user.Claims.Any(c => c.ClaimType == "FutureCareer" && c.ClaimValue == "GRD"))
- owinContext.SafeAddClaim(user.Id, new Claim("FutureCareer", "GRD"));
- }
- }
- }
- }
- return Json(new { didntTouch = ret, failedCreate = failCreate }, JsonRequestBehavior.AllowGet);
- }
- public async Task<ActionResult> FindStudentIds(string usernames)
- {
- var listUsers = usernames.Split(',');
- var apictrl = new ApiController();
- var ret = new List<string>();
- var fail = new List<string>();
- foreach (var username in listUsers)
- {
- var result = ((await apictrl.ProxyAdmin("student/bio2", username)) as ContentResult).Content;
- if (result != null)
- {
- var apiResult = JsonConvert.DeserializeObject<ApiCore.Attributes.ApiResult>(result);
- if (apiResult.data != null)
- {
- var bioData = JsonConvert.DeserializeObject<StudentBio>(apiResult.data.ToString());
- ret.Add(string.Format("{0}:{1}", username, bioData.studentId));
- }
- else
- fail.Add(username);
- }
- }
- return Json(new { didntTouch = ret, fail = fail }, JsonRequestBehavior.AllowGet);
- }
- #region Login logic
- #if DEBUG
- /// <summary>
- /// Endpoint to work with ionic serve
- /// </summary>
- /// <param name="username"></param>
- /// <returns></returns>
- public async Task<string> FakeCas(string username)
- {
- // "go to cas, return back to here, username checks out"
- return await LoginMobileUser(username,"nana");
- }
- #endif
- /// <summary>
- /// Returns to here from CAS after use logged in from the app
- /// </summary>
- /// <param name="returnUrl">URL that CAS returns users to. </param>
- /// <param name="ticket">CAS service ticket</param>
- /// <returns></returns>
- [AllowAnonymous]
- public async Task<ActionResult> LoginMobile(string returnUrl, string ticket)
- {
- string deviceInfo = null;
- if (returnUrl != "/Index")
- deviceInfo = returnUrl.Substring(8);
- return await DoLogin(true, returnUrl, ticket, deviceInfo);
- }
- /// <summary>
- /// Return to here from CAS after user logged in from the web
- /// </summary>
- /// <param name="returnUrl">URL that CAS returns users to.</param>
- /// <param name="ticket">CAS service ticket</param>
- /// <returns></returns>
- [AllowAnonymous]
- public async Task<ActionResult> Login(string returnUrl, string ticket)
- {
- return await DoLogin(false, returnUrl, ticket);
- }
- /// <summary>
- /// Login endpoint for anonymous users.
- /// </summary>
- /// <param name="returnUrl"></param>
- /// <returns></returns>
- [AllowAnonymous]
- public async Task<ActionResult> LoginAnon(string returnUrl)
- {
- var owinContext = Request.GetOwinContext();
- var userManager = owinContext.GetUserManager<ApplicationUserManager>();
- var user = await userManager.FindByNameAsync("anonymous2");
- ClaimsIdentity cookiesIdentity = await userManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
- var authenticationManager = Request.GetOwinContext().Authentication;
- var authProperties = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = new DateTimeOffset(DateTime.Now.AddHours(8)) };
- authenticationManager.SignIn(authProperties, cookiesIdentity);
- var xsrfCookie = getXsrfCookie("anonymous2");
- if (xsrfCookie == null)
- throw new Exception("Couldn't generate XSRF token. Log out and back in again.");
- HttpContext.Response.Cookies.Add(xsrfCookie);
- var domain = ConfigurationManager.AppSettings["PortalUrl"];
- if (Request.Url.Authority == "localhost:4455")
- domain = "http://localhost:4455";
- else if (Request.Url.Authority == "portal.uwaterloo.ca")
- domain = "https://portal.uwaterloo.ca";
- return Redirect(domain + returnUrl);
- }
- [AllowAnonymous]
- [HttpPost]
- public async Task<ActionResult> LoginAnonApp(string userName)
- {
- if (string.IsNullOrEmpty(userName))
- userName = "anonymous";
- var anonUsers = new string[] { "anonymous", "anonymous2" };
- if (!anonUsers.Contains(userName))
- return null;
- var owinContext = Request.GetOwinContext();
- var userManager = owinContext.GetUserManager<ApplicationUserManager>();
- //var createResult = (await createUserIfNotExists(owinContext, userManager, "anonymous"));
- //if (createResult.HasValue && !createResult.Value)
- // throw new Exception("User create failed");
- //var updateResult = await UpdateClaims("anonymous", owinContext);
- //if (!updateResult)
- // throw new Exception("Update claims has failed.");
- return Content(await LoginMobileUser(userName, null));
- }
- private async Task<string> LoginMobileUser(string username, string deviceInfo)
- {
- var password = getUserPassword(username);
- using (var client = new HttpClient())
- {
- var portalUrl = "https://portal.uwaterloo.ca";
- #if DEBUG
- portalUrl = "http://localhost:4455";
- #endif
- #if TEST
- portalUrl = "https://portalbeta.uwaterloo.ca";
- #endif
- HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, portalUrl + "/oauth2/token");
- var values = new Dictionary<string, string>
- {
- { "username", username },
- { "password", password },
- {"client_id", "portalApp" },
- {"grant_type","password" },
- {"device_info",deviceInfo }
- };
- req.Content = new FormUrlEncodedContent(values);
- req.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
- var response = await client.SendAsync(req);
- var responseString = await response.Content.ReadAsStringAsync();
- Response.ContentType = "application/json";
- return responseString;
- }
- }
- #endregion
- #region Anonymous User Stuff
- /// <summary>
- /// Checks if there are new or deleted personas compared to those that user already has.
- /// Quicker than re-fetching everything
- /// </summary>
- /// <param name="personas"></param>
- /// <returns></returns>
- [AllowAnonUser]
- public async Task<ActionResult> PersonasConfigurationChanged(string personas) {
- var personasArray = personas.Split(',');
- var anonUsers = GetAnonymousUsers();
- var newAnonUsers = anonUsers.Where(k => !personasArray.Contains(k.userName)).ToList();
- var anonUsersUids = anonUsers.Select(k => k.userName);
- var deletedUsers = personasArray.Where(k => !anonUsersUids.Contains(k)).ToList();
- return Json(newAnonUsers.Count > 0 || deletedUsers.Count>0, JsonRequestBehavior.AllowGet);
- }
- public static List<AnonymousUser> GetAnonymousUsers() {
- using (var db = new UWPortalEntities()) {
- return db.AnonymousUsers.ToList();
- }
- }
- /// <summary>
- /// gets the list of identities for anonymous users
- /// first thing that gets called when app starts
- /// </summary>
- /// <param name="appVersion"></param>
- /// <returns></returns>
- [AllowAnonymous]
- [HttpPost]
- public async Task<ActionResult> GetAnonTokensAndHomeSettings_7(int appVersion)
- {
- // if this is an old version that is no longer supported, respond with a special code that
- // will lock the user out and display an approriate message
- var versionInfo = ConfigHelper.GetAppVersionInfo();
- // this block will handle apps after version 7, as prior version won't submit appVersion argument
- if (versionInfo.version > appVersion && versionInfo.phase == 2)
- {
- return Json(new { appExpired = true });
- }
- var tokens = new List<dynamic>();
- var baseDats = new List<dynamic>();
- var owinContext = Request.GetOwinContext();
- var userManager = owinContext.GetUserManager<ApplicationUserManager>();
- List<Task<dynamic>> tasks = new List<Task<dynamic>>();
- var anonUsers = GetAnonymousUsers();
- foreach (var anonUser in anonUsers)
- {
- // get jwt token
- //var token = await LoginMobileUser(anonUser, null);
- //tokens.Add(new { userName = anonUser, token = token });
- tasks.Add(getJwtToken(anonUser.userName));
- // get base data
- //var user = await userManager.FindByNameAsync(anonUser);
- //var principal = new GenericPrincipal(userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie), null);
- //var baseData = await HomeController.getBaseDataForUser(principal, false, true);
- //baseDats.Add(new { userName = anonUser, baseData = baseData });
- var user = await userManager.FindByNameAsync(anonUser.userName);
- var principal = new GenericPrincipal(userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie), null);
- tasks.Add(getBaseData_7(principal, anonUser.userName, userManager));
- }
- await Task.WhenAll(tasks);
- for (var i = 0; i < tasks.Count; i++)
- {
- if (i % 2 == 1)
- {
- // it's base data
- baseDats.Add(tasks[i].Result);
- }
- else
- {
- // it's a token
- tokens.Add(tasks[i].Result);
- }
- }
- var ret = new { tokens = tokens, baseDats = baseDats, anonUsers = anonUsers };
- return Json(ret);
- }
- /// <summary>
- /// gets the list of identities for anonymous users
- /// first thing that gets called when app starts
- /// </summary>
- /// <param name="appVersion"></param>
- /// <returns></returns>
- [AllowAnonymous]
- [HttpPost]
- public async Task<ActionResult> GetAnonTokensAndHomeSettings()
- {
- var tokens = new List<dynamic>();
- var baseDats = new List<dynamic>();
- var owinContext = Request.GetOwinContext();
- var userManager = owinContext.GetUserManager<ApplicationUserManager>();
- List<Task<dynamic>> tasks = new List<Task<dynamic>>();
- var anonUsers = GetAnonymousUsers();
- foreach (var anonUser in anonUsers)
- {
- // get jwt token
- //var token = await LoginMobileUser(anonUser, null);
- //tokens.Add(new { userName = anonUser, token = token });
- tasks.Add(getJwtToken(anonUser.userName));
- // get base data
- //var user = await userManager.FindByNameAsync(anonUser);
- //var principal = new GenericPrincipal(userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie), null);
- //var baseData = await HomeController.getBaseDataForUser(principal, false, true);
- //baseDats.Add(new { userName = anonUser, baseData = baseData });
- var user = await userManager.FindByNameAsync(anonUser.userName);
- var principal = new GenericPrincipal(userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie), null);
- tasks.Add(getBaseData(principal, anonUser.userName, userManager));
- }
- await Task.WhenAll(tasks);
- for (var i = 0; i < tasks.Count; i++)
- {
- if (i % 2 == 1)
- {
- // it's base data
- baseDats.Add(tasks[i].Result);
- }
- else
- {
- // it's a token
- tokens.Add(tasks[i].Result);
- }
- }
- var ret = new { tokens = tokens, baseDats = baseDats, anonUsers = anonUsers };
- return Json(ret);
- }
- /// <summary>
- /// Gets JWT token for the specified user (PRIVATE)
- /// Needs to be in a spearate function to make it into a threaded approach
- /// for multiple anon users
- /// </summary>
- /// <param name="username"></param>
- /// <returns></returns>
- private async Task<dynamic> getJwtToken(string username)
- {
- return await LoginMobileUser(username, null);
- }
- private async Task<dynamic> getBaseData(GenericPrincipal principal, string userName, ApplicationUserManager userManager)
- {
- var baseData = await HomeController.getBaseDataForUser(principal, false, true);
- return new { userName = userName, baseData = baseData };
- }
- private async Task<dynamic> getBaseData_7(GenericPrincipal principal, string userName, ApplicationUserManager userManager)
- {
- var baseData = await HomeController.getBaseDataForUser_7(principal, false, true);
- return new { userName = userName, baseData = baseData };
- }
- #endregion
- public static async Task<bool> UpdateClaims(string username, IOwinContext owinContext)
- {
- var progress = "";
- try
- {
- var userManager = owinContext.GetUserManager<ApplicationUserManager>();
- ApplicationUser user = await userManager.FindByNameAsync(username);
- // No need to update any claims for anonymous user
- if (user.Claims.Any(c => c.ClaimType == "role" && c.ClaimValue == "anonymous"))
- return true;
- // Create a principal and send it to the GetSetConfig function so that it can setup a Config record for new users.
- // IMPORTANT SO THAT THEY HAVE AN API KEY TO USE FOR RETRIEVING DATA
- var principal = new GenericPrincipal(userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie), null);
- var userConfig = ConfigHelper.GetSetConfig(principal);
- //--------------Audience Types--------------------
- var NexusAudienceClaim = new Claim("Audience", "NexusUser");
- var StudentAudienceClaim = new Claim("Audience", "Student");
- var AnonRole = new Claim("role", "anonymous");
- var AnonRemovedRole = new Claim("role", "anonymousRoleRemoved");
- var allClaims = userManager.GetClaims(user.Id).ToList();
- //--------------Has logged in, so is a Nexus User--------------
- var nexusClaims = allClaims.Where(c => c.Is(NexusAudienceClaim)).ToList();
- // If they don't have one, add it
- if (nexusClaims.Count == 0)
- owinContext.SafeAddClaim(user.Id, NexusAudienceClaim);
- else if (nexusClaims.Count > 1)
- {
- // If they have many copies, make sure they only end up with one
- for (var i = 0; i < nexusClaims.Count - 1; i++)
- owinContext.SafeRemoveClaim(user.Id, NexusAudienceClaim);
- }
- progress += "nexus user set; ";
- //--------------Reset staff claim--------------
- //if (user.Claims.HasClaim("Audience", "Staff"))
- // owinContext.SafeRemoveClaim(user.Id, new Claim("Audience", "Staff"));
- progress += "staff reset; ";
- //--------------Check if student--------------
- var studentAudienceClaims = userManager.GetClaims(user.Id).Where(c => c.Is(StudentAudienceClaim)).ToList();
- var student = await getStudent2(username);
- if (student.isStudent)
- {
- // Clean up student audience claim
- if (studentAudienceClaims.Count == 0)
- owinContext.SafeAddClaim(user.Id, StudentAudienceClaim);
- else if (studentAudienceClaims.Count > 1)
- {
- // If they have many copies, make sure they only end up with one
- for (var i = 0; i < studentAudienceClaims.Count - 1; i++)
- owinContext.SafeRemoveClaim(user.Id, StudentAudienceClaim);
- }
- progress += "student reset; ";
- // Make all student claims current
- var studentClaimTypes = new[] { "Career", "FutureCareer", "Faculty", "Department", "Plan Title", "Level", "Form of Study", "CollegeAffiliation", "Student Number" };
- // Add/modify any that need it
- foreach (var studentClaimType in studentClaimTypes)
- {
- progress += "removing - " + studentClaimType + "; ";
- var studentClaims = user.Claims.Where(c => c.ClaimType == studentClaimType).ToList();
- // Remove all
- foreach (var studentClaim in studentClaims)
- owinContext.SafeRemoveClaim(user.Id, new Claim(studentClaim.ClaimType, studentClaim.ClaimValue));
- }
- progress += "student claims reset-removed; ";
- // Add all new student claims
- foreach (var claim in student.claims)
- {
- progress += "adding - " + claim.Value + "; ";
- owinContext.SafeAddClaim(user.Id, claim);
- }
- progress += "student claims reset-added; ";
- }
- else
- {
- foreach (var studentAudClaim in studentAudienceClaims)
- owinContext.SafeRemoveClaim(user.Id, studentAudClaim);
- // Add the student number claim even if there is no current term card.
- foreach (var claim in student.claims)
- {
- if (claim.Type== "Student Number")
- owinContext.SafeAddClaim(user.Id, claim);
- }
- }
- #region Rez data
- //--------------Check for rez data--------------
- try
- {
- progress += "get rez; ";
- var rezInfo = await getResidenceInfo(username);
- if (rezInfo.isInRez)
- {
- progress += "is in rez; ";
- var claims = userManager.GetClaims(user.Id).ToList();
- // Reset ResidenceCommunity
- var communities = claims.Where(c => c.Type == "ResidenceCommunity").ToList();
- if (communities.Count > 0)
- {
- foreach (var community in communities)
- {
- owinContext.SafeRemoveClaim(user.Id, community);
- }
- }
- if (!string.IsNullOrWhiteSpace(rezInfo.communityName))
- owinContext.SafeAddClaim(user.Id, new Claim("ResidenceCommunity", rezInfo.communityName));
- progress += "community reset; ";
- // Reset ResidenceRoom
- var rooms = claims.Where(c => c.Type == "ResidenceRoom").ToList();
- if (rooms.Count > 0)
- {
- foreach (var room in rooms)
- {
- owinContext.SafeRemoveClaim(user.Id, room);
- }
- }
- if (!string.IsNullOrWhiteSpace(rezInfo.roomCode))
- owinContext.SafeAddClaim(user.Id, new Claim("ResidenceRoom", rezInfo.roomCode));
- progress += "room reset; ";
- // Reset LivingLearningCode
- var llCodes = claims.Where(c => c.Type == "LivingLearningCode").ToList();
- if (communities.Count > 0)
- {
- foreach (var llCode in llCodes)
- {
- owinContext.SafeRemoveClaim(user.Id, llCode);
- }
- }
- if (!string.IsNullOrWhiteSpace(rezInfo.llCode))
- owinContext.SafeAddClaim(user.Id, new Claim("LivingLearningCode", rezInfo.llCode));
- progress += "llCode reset; ";
- // Reset LivingLearningName
- var llNames = claims.Where(c => c.Type == "LivingLearningName").ToList();
- if (communities.Count > 0)
- {
- foreach (var llName in llNames)
- {
- owinContext.SafeRemoveClaim(user.Id, llName);
- }
- }
- if (!string.IsNullOrWhiteSpace(rezInfo.llName))
- owinContext.SafeAddClaim(user.Id, new Claim("LivingLearningName", rezInfo.llName));
- progress += "llName reset; ";
- }
- }
- catch (Exception e)
- {
- Elmah.ErrorLog.GetDefault(null).Log(new Error(e));
- progress += "rez err: " + e.Message + "; ";
- }
- #endregion
- progress += "rez done; ";
- //--------------Check if faculty--------------
- //...
- //If a user doesn't have any claim to access portal and has any of the above audiences then they will gain a claim to access the portal
- //If a user does have a claim defined to access portal, dont' overwrite it because access may have been revoked (CanAccessPortal: false)
- if (!user.Claims.Any(c => c.ClaimType == "CanAccessPortal") && user.Claims.Any(c => c.ClaimType == "Audience"))
- owinContext.SafeAddClaim(user.Id, new Claim("CanAccessPortal", "true"));
- // If user's config was setup for the first time last time it ran, then correctly setup the user's theme for first time
- if (userConfig.IsFirstLogin && student.isStudent && student.claims.Count > 0)
- {
- var studentObj = new PortalWeb.Controllers.Student();
- var groupCode = student.claims.FirstOrDefault(c => c.Type == "Faculty");
- if (groupCode != null)
- studentObj.Faculty = groupCode.Value;
- var careerCode = student.claims.FirstOrDefault(c => c.Type == "Career");
- if (careerCode != null)
- studentObj.Career = careerCode.Value;
- var formOfStudy = student.claims.FirstOrDefault(c => c.Type == "Form of Study");
- if (formOfStudy != null)
- studentObj.FormOfStudy = formOfStudy.Value;
- var planTitles = student.claims.Where(c => c.Type == "Plan Title").Select(c => c.Value).ToArray();
- if (planTitles.Length > 0)
- studentObj.PlanTitles = planTitles;
- var unitCodes = student.claims.Where(c => c.Type == "Department").Select(c => c.Value).ToArray();
- if (unitCodes.Length > 0)
- studentObj.Departments = unitCodes;
- var level = student.claims.FirstOrDefault(c => c.Type == "Level");
- if (level != null)
- studentObj.Level = level.Value;
- var stdNum = student.claims.FirstOrDefault(c => c.Type == "Student Number");
- if (stdNum != null)
- studentObj.StudentNumber = stdNum.Value;
- var themeId = ConfigHelper.GetThemeIdFromFaculty(studentObj.Faculty, studentObj.Departments);
- ConfigHelper.GetSetConfig(principal, userInfo: studentObj, themeId: themeId, resetSocial: true);
- }
- progress += "can access done; ";
- // If not a student, check if fake data is enabled. If it is not, then remove the student related claims from this session
- if (!student.isStudent)
- {
- progress += "not student; ";
- using (var db = new UWPortalEntities())
- {
- var config = db.Configs.SingleOrDefault(c => c.Username == username);
- if (config != null && !config.FakeDataOn)
- {
- progress += "fake data disabled; ";
- var studentClaimTypes = new[] { "Career", "Faculty", "Department", "Plan Title", "Level", "Form of Study", "CollegeAffiliation", "Student Number" };
- foreach (var studentClaimType in studentClaimTypes)
- {
- var studentClaims = user.Claims.Where(c => c.ClaimType == studentClaimType).ToList();
- foreach (var claim in studentClaims)
- user.Claims.Remove(claim);
- }
- }
- }
- }
- else
- {
- // Make sure that if they're a student, their fake data isn't turned on.
- using (var db = new UWPortalEntities())
- {
- var config = db.Configs.SingleOrDefault(c => c.Username == username);
- if (config != null && config.FakeDataOn)
- {
- config.FakeDataOn = false;
- db.SaveChanges();
- }
- }
- }
- progress += "past fake data; ";
- return true;
- }
- catch (Exception e)
- {
- Elmah.ErrorLog.GetDefault(null).Log(new Error(e));
- processLoginException(e, progress, username);
- return false;
- }
- }
- /// <summary>
- /// Gets the couch password for the current user.
- /// </summary>
- /// <param name="sdk"></param>
- /// <returns></returns>
- [AllowAnonUser]
- [HttpPost]
- public async Task<ActionResult> GetCouchPassword(bool sdk = false)
- {
- var couchResponse = await CouchDBService.initUser(User.Identity.Name, sdk, User.IsInRole("anonymous"));
- return Json(new { user = User.Identity.Name, password = CouchDBService.genPassForCurrentUser(User.Identity.Name, sdk) }, JsonRequestBehavior.AllowGet);
- }
- private string doubleUrlEncode(string url)
- {
- return Helpers.Utils.UrlEncodeUpper(Helpers.Utils.UrlEncodeUpper(url));
- }
- /// <summary>
- /// Logs user into Portal.
- /// </summary>
- /// <param name="mobile"></param>
- /// <param name="returnUrl"></param>
- /// <param name="ticket"></param>
- /// <param name="deviceInfo"></param>
- /// <returns></returns>
- private async Task<ActionResult> DoLogin(bool mobile, string returnUrl, string ticket, string deviceInfo = null)
- {
- if (string.IsNullOrEmpty(returnUrl))
- returnUrl = "/";
- if (returnUrl.Equals("/Home/SignOut"))
- return Redirect("/");
- if (returnUrl.Equals("/elmah"))
- returnUrl = "/";
- var username = string.Empty;
- var progress = string.Empty;
- try
- {
- var casUrl = "https://cas.uwaterloo.ca/cas";
- var domain = ConfigurationManager.AppSettings["PortalUrl"];
- if (Request.Url.Authority == "localhost:4455")
- domain = "http://localhost:4455";
- else if (Request.Url.Authority == "portal.uwaterloo.ca")
- domain = "https://portal.uwaterloo.ca";
- var serviceUrl = domain + "/Account/Login" + (mobile ? "Mobile" : "") + "?returnUrl=" + doubleUrlEncode(returnUrl);
- var resultUrl = string.Format("{0}/login?service={1}", casUrl, serviceUrl);
- // Returning from CAS: validate ticket
- if (!string.IsNullOrWhiteSpace(ticket))
- {
- var client = new WebClient();
- var result = client.DownloadString(string.Format("{0}/serviceValidate?ticket={1}&service={2}", casUrl, ticket, serviceUrl));
- var xmlDoc = new XmlDocument();
- xmlDoc.LoadXml(result);
- var responseNode = xmlDoc.ChildNodes[0].ChildNodes[0];
- if (responseNode.LocalName == "authenticationSuccess")
- {
- var userNode = responseNode.ChildNodes[0].ChildNodes[0];
- username = userNode.Value.ToLower();
- progress += "set value; ";
- }
- }
- // CAS ticket valid
- if (!string.IsNullOrWhiteSpace(username))
- {
- progress += "start; ";
- resultUrl = domain + returnUrl;
- // Try to find user in ASP.NET identity db
- var owinContext = Request.GetOwinContext();
- var userManager = owinContext.GetUserManager<ApplicationUserManager>();
- // createResult
- // true: user exists
- // false: creation failed
- // null: new user created
- var createResult = (await createUserIfNotExists(owinContext, userManager, username));
- if (createResult.HasValue && !createResult.Value)
- throw new Exception("User create failed");
- progress += "user set; ";
- // don't do update claims here if this is a desktop login
- // we're gonnna do it in a separate request via GetUpdatedClaimsIdentity to expediate first render
- if (mobile)
- {
- var updateResult = await UpdateClaims(username, owinContext);
- if (!updateResult)
- throw new Exception("Update claims has failed.");
- }
- // If this is a web request (not mobile), set cookies for authentication
- if (!mobile)
- {
- //if this is first time for user logging in, need to run updateClaims here
- if (!createResult.HasValue)
- {
- var updateResult = await UpdateClaims(username, owinContext);
- if (!updateResult)
- throw new Exception("Update claims has failed.");
- }
- else
- {
- // Check to make sure they have a CanAccessPortal claim
- ApplicationUser user = await userManager.FindByNameAsync(username);
- if (!user.Claims.Any(c => c.ClaimType == "CanAccessPortal"))
- owinContext.SafeAddClaim(user.Id, new Claim("CanAccessPortal", "true"));
- }
- var signedIn = await signInAsUser(userManager, username);
- progress += "cookie set; ";
- setXsrf(username);
- progress += "xsrf set; ";
- }
- // if request is coming from a mobile device we need to return jwt token + refresh token + set couchdb cookie
- // perform a request to the oauth server with username and password and return response with the token
- if (mobile)
- {
- return Content(await LoginMobileUser(username, deviceInfo));
- }
- }
- return Redirect(resultUrl);
- }
- catch (Exception e)
- {
- Elmah.ErrorLog.GetDefault(null).Log(new Error(e));
- processLoginException(e, progress, username);
- return Content(e.Message);
- }
- }
- /// <summary>
- /// DO NOT USE "username" IT'S FOR HONEYPOT PURPOSES ONLY
- /// USE "User.Identity.Name"
- /// </summary>
- /// <param name="username"></param>
- /// <returns></returns>
- [HttpPost]
- [AllowAnonUser]
- public async Task<ActionResult> GetUpdatedClaimsIdentity(string username)
- {
- // DO NOT USE "username"
- if (username != User.Identity.Name)
- {
- HoneyPotsController.AddHoneypotHit("GetUpdatedClaimsIdentity", HttpContext.Request.UserHostAddress, User.Identity.Name, username);
- }
- // DO NOT USE "username"
- var owinContext = Request.GetOwinContext();
- var userManager = owinContext.GetUserManager<ApplicationUserManager>();
- var updateResult = await UpdateClaims(User.Identity.Name, owinContext);
- // DO NOT USE "username"
- if (!updateResult)
- throw new Exception("Update claims has failed.");
- await signInAsUser(userManager, User.Identity.Name);
- // DO NOT USE "username"
- var user = await userManager.FindByNameAsync(User.Identity.Name);
- ClaimsIdentity cookiesIdentity = await userManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
- // DO NOT USE "username"
- var userInfo = HomeController.getClaimsAndRoles(new ClaimsPrincipal(cookiesIdentity), User.IsInRole("anonymous"));
- return Json(userInfo);
- // DO NOT USE "username"
- }
- private void setXsrf(string username)
- {
- var xsrfCookie = getXsrfCookie(username);
- if (xsrfCookie == null)
- throw new Exception("Couldn't generate XSRF token. Log out and back in again.");
- HttpContext.Response.Cookies.Add(xsrfCookie);
- }
- private async Task<bool> signInAsUser(ApplicationUserManager userManager, string username, bool removeAnonClaim = false)
- {
- var user = await userManager.FindByNameAsync(username);
- ClaimsIdentity cookiesIdentity = await userManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
- if (removeAnonClaim)
- {
- foreach (var claim in cookiesIdentity.Claims)
- {
- if (claim.Type == "role" && claim.Value == "anonymous")
- {
- cookiesIdentity.RemoveClaim(claim);
- break;
- }
- }
- cookiesIdentity.AddClaim(new Claim("role", "anonymousRoleRemoved"));
- cookiesIdentity.AddClaim(new Claim("Audience", "NexusUser"));
- }
- var authenticationManager = Request.GetOwinContext().Authentication;
- var authProperties = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = new DateTimeOffset(DateTime.Now.AddHours(8)) };
- authenticationManager.SignIn(authProperties, cookiesIdentity);
- return true;
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="owinContext"></param>
- /// <param name="userManager"></param>
- /// <param name="username"></param>
- /// <returns>true if user already exists, false if user does not exists and it failed, null if user did not exist and was just created</returns>
- private async Task<bool?> createUserIfNotExists(IOwinContext owinContext, ApplicationUserManager userManager, string username)
- {
- ApplicationUser user = await userManager.FindByNameAsync(username);
- // User does not exist: create user
- if (user == null)
- {
- var password = getUserPassword(username);
- var newUser = new ApplicationUser { UserName = username, Email = username + "@uwaterloo.ca" };
- var result = await userManager.CreateAsync(newUser, password);
- if (!result.Succeeded)
- return false;
- else
- return null;
- }
- return true;
- }
- /// <summary>
- /// Gets a hashed password for the user.
- /// </summary>
- /// <param name="username"></param>
- /// <returns></returns>
- private static string getUserPassword(string username)
- {
- var concat = string.Concat(username, "fJIdwFQ5eIp7zOPa");
- string password = FormsAuthentication.HashPasswordForStoringInConfigFile(concat, "sha1") + ";a";
- return password;
- }
- private static void processLoginException(Exception e, string progress, string username)
- {
- var dbErr = e as DbEntityValidationException;
- if (dbErr != null)
- {
- foreach (var eve in dbErr.EntityValidationErrors)
- {
- e.LogError(string.Format("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:", eve.Entry.Entity.GetType().Name, eve.Entry.State));
- foreach (var ve in eve.ValidationErrors)
- e.LogError(string.Format("- Property: \"{0}\", Error: \"{1}\"", ve.PropertyName, ve.ErrorMessage));
- }
- }
- e.LogError("Account login error. Username: " + username + " \nProgress: " + progress);
- }
- #region Admin Stuff
- [Authorize(Roles = "Master Admin")]
- public async Task<ActionResult> showUserPassword(string userName)
- {
- var password = getUserPassword(userName);
- return Content(password);
- }
- [Authorize(Roles = "Master Admin")]
- [HttpPost]
- public async Task<ActionResult> ImpersonateApp(string id)
- {
- var allowed = new[] { "pchvala", "ostruk", "jradman", "y339zhao" };
- if (!allowed.Contains(User.Identity.Name))
- return Content("No");
- var owinContext = Request.GetOwinContext();
- var userManager = owinContext.GetUserManager<ApplicationUserManager>();
- var createResult = (await createUserIfNotExists(owinContext, userManager, id));
- if (createResult.HasValue && !createResult.Value)
- throw new Exception("User create failed");
- var updateResult = await UpdateClaims(id, owinContext);
- if (!updateResult)
- throw new Exception("Update claims has failed.");
- return Content(await LoginMobileUser(id, null));
- }
- [Authorize(Roles = "Master Admin")]
- [HttpGet]
- public async Task<ActionResult> EditAnonAccount(string id)
- {
- var allowed = new[] { "pchvala", "ostruk", "jradman", "y339zhao" };
- if (!allowed.Contains(User.Identity.Name))
- return Content("No");
- var owinContext = Request.GetOwinContext();
- var userManager = owinContext.GetUserManager<ApplicationUserManager>();
- var createResult = (await createUserIfNotExists(owinContext, userManager, id));
- if (createResult.HasValue && !createResult.Value)
- return Content("Could not create user");
- var updateResult = await UpdateClaims(id, owinContext);
- if (!updateResult)
- return Content("Update claims has failed.");
- await signInAsUser(userManager, id, true);
- setXsrf(id);
- return Redirect("/Index");
- }
- /// <summary>
- /// Impersonate access point for debug purpose.
- /// </summary>
- /// <param name="id"></param>
- /// <returns></returns>
- [Authorize(Roles = "Master Admin")]
- [HttpGet]
- public async Task<ActionResult> Impersonate(string id)
- {
- var allowed = new[] { "pchvala", "ostruk", "jradman", "y339zhao" };
- if (!allowed.Contains(User.Identity.Name))
- return Content("No");
- var owinContext = Request.GetOwinContext();
- var userManager = owinContext.GetUserManager<ApplicationUserManager>();
- var createResult = (await createUserIfNotExists(owinContext, userManager, id));
- if (createResult.HasValue && !createResult.Value)
- return Content("Could not create user");
- var updateResult = await UpdateClaims(id, owinContext);
- if (!updateResult)
- return Content("Update claims has failed.");
- await signInAsUser(userManager, id);
- setXsrf(id);
- return Redirect("/Index");
- }
- #endregion
- /// <summary>
- /// Issues a XSRF cookie to the user. This cookie expires in 8 hrs.
- /// </summary>
- /// <param name="username"></param>
- /// <returns></returns>
- private HttpCookie getXsrfCookie(string username)
- {
- if (string.IsNullOrWhiteSpace(username))
- return null;
- var value = string.Format("{0}.S41T", username).GetMd5Hash();
- var cookie = new HttpCookie("XSRF-TOKEN", value);
- cookie.Expires = DateTime.Now.AddHours(8);
- #if !DEBUG
- cookie.Secure = true;
- #endif
- var key = username.GetMd5Hash();
- try
- {
- using (var db = new UWPortalEntities())
- {
- var token = db.XsrfTokens.SingleOrDefault(x => x.Username == username && x.Key == key);
- if (token == null)
- {
- token = new XsrfToken() { Username = username, Token = value, Key = key, TimeCreated = DateTime.Now };
- db.XsrfTokens.Add(token);
- }
- else
- token.Token = value;
- db.SaveChanges();
- }
- return cookie;
- }
- catch (Exception e)
- {
- Elmah.ErrorLog.GetDefault(null).Log(new Error(e));
- e.LogError(string.Format("XSRF error for: {0}, key: {1}, value: {2}", username, key, value));
- return null;
- }
- }
- /// <summary>
- /// Gets various student data for the specified user.
- /// </summary>
- /// <param name="username"></param>
- /// <returns></returns>
- private static async Task<StudentClaim> getStudent2(string username)
- {
- var progress = string.Empty;
- var studentData = new StudentClaim() { isStudent = false, claims = new List<Claim>() };
- try
- {
- progress += "getstudent2; ";
- var apiResult = await ApiController.Proxy("student/terms2", username);
- if (apiResult.data == null)
- return studentData;
- progress += "got api result; ";
- var termData = JsonConvert.DeserializeObject<AcadTermData>(apiResult.data.ToString());
- if (!string.IsNullOrWhiteSpace(termData.studentId))
- studentData.claims.Add(new Claim("Student Number", termData.studentId));
- if (termData.academicTerms == null)
- return studentData;
- progress += "got term data; ";
- var date = DateTime.Now;
- var termCode = "0";
- var currentTerms = new List<AcadTerm>();
- // Check if current term exists
- termCode = date.ToTermCode();
- currentTerms = termData.academicTerms.Where(t => t.termCode == termCode).ToList();
- progress += "past cur term; ";
- if (currentTerms.Count == 0)
- {
- // Check if next term exists
- date = date.AddMonths(4);
- termCode = date.ToTermCode();
- currentTerms = termData.academicTerms.Where(t => t.termCode == termCode).ToList();
- progress += "past next term; ";
- if (currentTerms.Count == 0)
- {
- date = date.AddMonths(-8);
- // Check up to 3 terms back
- for (int i = 0; i < 3; i++)
- {
- progress += "get term " + i + "; ";
- termCode = date.ToTermCode();
- currentTerms = termData.academicTerms.Where(t => t.termCode == termCode).ToList();
- if (currentTerms.Count > 0)
- break;
- date = date.AddMonths(-4);
- }
- }
- }
- else
- {
- // There is a current term, but check the next anyways in case it's a different career
- date = date.AddMonths(4);
- termCode = date.ToTermCode();
- var nextTerms = termData.academicTerms.Where(t => t.termCode == termCode).ToList();
- if(nextTerms.Count > 0)
- {
- foreach(var nextTerm in nextTerms)
- studentData.claims.Add(new Claim("FutureCareer", nextTerm.academicCareer));
- }
- }
- progress += "past term check; ";
- progress += "past stdNum; ";
- //Check whether term exists
- if (currentTerms.Count > 0)
- {
- studentData.isStudent = true;
- progress += "is student; ";
- foreach (var currentTerm in currentTerms)
- {
- studentData.claims.AddRange(new List<Claim> {
- new Claim("Career", currentTerm.academicCareer),
- new Claim("Form of Study", currentTerm.formOfStudyCode),
- new Claim("Faculty", currentTerm.academicGroupCode),
- new Claim("Level", currentTerm.academicLevel)
- });
- progress += "done main claims; ";
- foreach (var plan in currentTerm.plans)
- {
- studentData.claims.AddRange(new List<Claim> {
- new Claim("Department", plan.academicOrgCode),
- new Claim("Plan Title", plan.planName)
- });
- }
- progress += "done plan claims; ";
- // Get college affiliations
- var colleges = await ApiController.Proxy("student/collegeAffiliations", username);
- if (colleges.data != null)
- {
- var affiliations = JsonConvert.DeserializeObject<StudentCollegeAffiliations>(colleges.data.ToString());
- foreach (var college in affiliations.Colleges)
- studentData.claims.Add(new Claim("CollegeAffiliation", college));
- }
- progress += "done affiliation claims; ";
- }
- progress += "done claims; ";
- }
- return studentData;
- }
- catch (Exception e)
- {
- Elmah.ErrorLog.GetDefault(null).Log(new Error(e));
- processLoginException(e, progress, username);
- return studentData;
- }
- }
- /// <summary>
- /// Gets residence information for the specified user.
- /// </summary>
- /// <param name="username"></param>
- /// <returns></returns>
- private static async Task<ResidenceInfo> getResidenceInfo(string username)
- {
- var rezInfo = new ResidenceInfo { isInRez = false };
- var apiResult = await ApiController.Proxy("student/rezInfo", username);
- if (apiResult.data == null)
- return rezInfo;
- var rezData = JsonConvert.DeserializeObject<RezData>(apiResult.data.ToString());
- if (rezData.ResidenceData == null)
- return rezInfo;
- var now = DateTime.Now;
- var start = DateTime.Parse(rezData.ResidenceData.ContractStartDate);
- var end = DateTime.Parse(rezData.ResidenceData.ContractEndDate);
- if (start <= now && end > now)
- {
- rezInfo.isInRez = true;
- rezInfo.communityName = rezData.ResidenceData.CommunityName;
- rezInfo.roomCode = rezData.ResidenceData.RoomCode;
- rezInfo.llCode = rezData.ResidenceData.LivingLearningCode;
- rezInfo.llName = rezData.ResidenceData.LivingLearningName;
- }
- return rezInfo;
- }
- public ActionResult Secured()
- {
- return View((User as ClaimsPrincipal).Claims);
- }
- }
- public class StudentClaim
- {
- public bool isStudent { get; set; }
- public List<Claim> claims { get; set; }
- }
- public class ResidenceInfo
- {
- public bool isInRez { get; set; }
- public string communityName { get; set; }
- public string roomCode { get; set; }
- // Living Learning Code/Name
- public string llCode { get; set; }
- public string llName { get; set; }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement