Skip to content
Commits on Source (3)
......@@ -40,14 +40,6 @@ namespace Coscine.Api.STS.Controllers
{
await _signInManager.SignOutAsync();
foreach (var cookie in Request.Cookies.Keys)
{
if (cookie != MergeController.MERGETOKENKEY)
{
Response.Cookies.Delete(cookie);
}
}
ViewBag.LoginUrl = UrlGenerator.GetLoginUrl(Request) + "?logout=true";
return Redirect(ViewBag.LoginUrl);
......
......@@ -28,12 +28,6 @@ namespace Coscine.Api.STS.Controllers
var userIdString = User.Identity.Name;
var userId = new Guid(userIdString);
if (Request.Cookies.ContainsKey(MergeController.MERGETOKENKEY))
{
string mergeCallbackUrl = UrlGenerator.MergeCallbackRedirectUrl();
return Redirect(mergeCallbackUrl);
}
ProcessSignIn(User);
// Process where the user will be redirected to
......
using Coscine.Api.STS.Data;
using Coscine.Api.STS.Utils;
using Coscine.Database.Models;
using Coscine.JwtHandler;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Coscine.Api.STS.Controllers
{
public class MergeController : Controller
{
public const string MERGETOKENKEY = "coscine.mergetoken";
private readonly SignInManager<CoscineUser> _signInManager;
public MergeController(SignInManager<CoscineUser> signInManager)
{
_signInManager = signInManager;
}
[Route("[controller]/login")]
public ActionResult Login(string returnUrl = null)
{
var contents = GetContents();
if (contents != null && contents.Any((claim) => claim.Type == "LoginMethod"))
{
var loginMethodClaim = contents.Where((claim) => claim.Type == "LoginMethod").First();
switch (loginMethodClaim.Value)
{
case "orcid":
string orcidUrl = ORCiDHandler.GetORCiDOAuthUrl() + UrlGenerator.ORCiDRedirectUrl();
return Redirect(orcidUrl);
case "shibboleth":
string shibbolethUrl = UrlGenerator.ShibbolethRedirectUrl();
return Redirect(shibbolethUrl);
default:
break;
}
}
// If something is wrong with the token or method, just redirect back to login page and invalidate the token
DeleteCookie();
string loginUrl = UrlGenerator.GetLoginUrl(Request);
return Redirect(loginUrl);
}
[Route("[controller]/callback")]
public async Task<ActionResult> Callback(string returnUrl = null)
{
var contents = GetContents();
DeleteCookie();
var validMerge = false;
var userModel = new UserModel();
var userIdString = User.Identity.Name;
var userId = new Guid(userIdString);
var mergeFromUser = userModel.GetById(userId);
var externalAuthenticatorModel = new ExternalAuthenticatorModel();
var externalIdModel = new ExternalIdModel();
// Check if logged in user has a possible login from the defined LoginMethod
if (contents != null && contents.Any((claim) => claim.Type == "LoginMethod"))
{
var loginMethodClaim = contents.Where((claim) => claim.Type == "LoginMethod").First();
switch (loginMethodClaim.Value)
{
case "orcid":
var orcidAuthItem = externalAuthenticatorModel.GetWhere((externalAuthenticator) => externalAuthenticator.DisplayName == "ORCiD");
var orcidMapping = externalIdModel.GetAllWhere((map) => map.UserId == userId && map.ExternalAuthenticatorId == orcidAuthItem.Id);
validMerge = orcidMapping.Count() > 0;
break;
case "shibboleth":
var shibbolethAuthItem = externalAuthenticatorModel.GetWhere((externalAuthenticator) => externalAuthenticator.DisplayName == "Shibboleth");
var shibbolethMapping = externalIdModel.GetAllWhere((map) => map.UserId == userId && map.ExternalAuthenticatorId == shibbolethAuthItem.Id);
validMerge = shibbolethMapping.Count() > 0;
break;
default:
break;
}
}
if (validMerge && contents?.Any((claim) => claim.Type == "UserGuid") == true)
{
var userGuidString = contents.First((claim) => claim.Type == "UserGuid");
var correctGuid = Guid.TryParse(userGuidString.Value, out Guid userGuid);
if (correctGuid)
{
var userList = userModel.GetAllWhere((user) => user.Id == userGuid);
if (userList.Any())
{
var mergeIntoUser = userList.First();
if (mergeFromUser.Id != mergeIntoUser.Id)
{
var mergeUtil = new MergeUtil();
mergeUtil.MergeFromUserIntoUser(mergeFromUser, mergeIntoUser);
// Logout the old account and login the user into his merged account after merging has finished
await _signInManager.SignOutAsync();
var coscineUser = new CoscineUser()
{
UserName = mergeIntoUser.Id.ToString(),
Email = mergeIntoUser.EmailAddress ?? ""
};
var result = await _signInManager.UserManager.CreateAsync(coscineUser);
await _signInManager.SignInAsync(coscineUser, isPersistent: false);
return Redirect(UrlGenerator.ExtendReturnUrl(returnUrl, Request));
}
}
}
}
else if (contents?.Any((claim) => claim.Type == "UserGuid") == true)
{
var userGuidString = contents.First((claim) => claim.Type == "UserGuid");
var coscineUser = new CoscineUser()
{
UserName = userGuidString.Value,
Email = ""
};
await _signInManager.SignInAsync(coscineUser, isPersistent: false);
return Redirect(UrlGenerator.ExtendReturnUrl(returnUrl, Request));
}
string loginUrl = UrlGenerator.GetLoginUrl(Request);
return Redirect(loginUrl);
}
private IEnumerable<Claim> GetContents()
{
if (Request.Cookies.ContainsKey(MERGETOKENKEY))
{
var mergeToken = Request.Cookies[MERGETOKENKEY];
var handler = new JWTHandler(Program.Configuration);
if (handler.TryValidateToken(mergeToken))
{
return handler.GetContents(mergeToken);
}
}
return null;
}
private void DeleteCookie()
{
if (Request.Cookies.ContainsKey(MERGETOKENKEY))
{
Response.Cookies.Delete(MERGETOKENKEY);
}
}
}
}
......@@ -6,7 +6,7 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TargetFramework>net6.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Version>4.1.0</Version>
<Version>4.1.1</Version>
</PropertyGroup>
<PropertyGroup>
<Authors>RWTH Aachen University</Authors>
......@@ -21,6 +21,7 @@
<PackageReference Include="Coscine.Action" Version="3.*-*" />
<PackageReference Include="Coscine.ActiveDirectory" Version="2.*-*" />
<PackageReference Include="Coscine.ApiCommons" Version="2.*-*" />
<PackageReference Include="Coscine.Database" Version="2.22.0" />
<PackageReference Include="Coscine.JwtHandler" Version="2.*-*" />
<PackageReference Include="Coscine.Metadata" Version="2.*-*" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.4" />
......
using Coscine.Action;
using Coscine.Action.EventArgs;
using Coscine.ActiveDirectory;
using Coscine.Configuration;
using Coscine.Database.DataModel;
using Coscine.Database.Models;
using Coscine.Database.Util;
using LinqKit;
using System.Linq;
namespace Coscine.Api.STS.Utils
{
public class MergeUtil
{
private readonly ExternalIdModel _externalIdModel;
private readonly ProjectModel _projectModel;
private readonly ProjectRoleModel _projectRoleModel;
private readonly ResourceModel _resourceModel;
private readonly RoleModel _roleModel;
private readonly TOSModel _tosModel;
private readonly ApiTokenModel _apiTokenModel;
private readonly UserModel _userModel;
private readonly UserDisciplineModel _userDisciplineModel;
private readonly Emitter _emitter;
public MergeUtil() : this(Program.Configuration)
{
}
public MergeUtil(IConfiguration configuration)
{
_externalIdModel = new ExternalIdModel();
_projectModel = new ProjectModel();
_projectRoleModel = new ProjectRoleModel();
_resourceModel = new ResourceModel();
_roleModel = new RoleModel();
_tosModel = new TOSModel();
_apiTokenModel = new ApiTokenModel();
_userModel = new UserModel();
_userDisciplineModel = new UserDisciplineModel();
_emitter = new Emitter(configuration);
}
public void MergeFromUserIntoUser(User mergeFromUser, User mergeIntoUser)
{
MergeProjectRole(mergeIntoUser, mergeFromUser);
MergeUserDiscipline(mergeIntoUser, mergeFromUser);
MergeResourceOwner(mergeIntoUser, mergeFromUser);
MergeTOSAccepted(mergeIntoUser, mergeFromUser);
MergeApiTokens(mergeIntoUser, mergeFromUser);
MergeUserProperties(mergeIntoUser, mergeFromUser);
// ExternalId gets merged last, in case something goes wrong
MergeExternalId(mergeIntoUser, mergeFromUser);
DeleteUser(mergeFromUser);
}
private void MergeExternalId(User mergeIntoUser, User mergeFromUser)
{
foreach (var externalId in _externalIdModel.GetAllWhere((entry) => entry.UserId == mergeFromUser.Id))
{
// Check if entry already exists
if (_externalIdModel.GetAllWhere(
(entry) =>
entry.UserId == mergeIntoUser.Id
&& entry.ExternalAuthenticatorId == externalId.ExternalAuthenticatorId
).Count() == 0)
{
externalId.UserId = mergeIntoUser.Id;
_externalIdModel.Update(externalId);
}
else
{
_externalIdModel.Delete(externalId);
}
}
}
private void MergeProjectRole(User mergeIntoUser, User mergeFromUser)
{
foreach (var projectRole in _projectRoleModel.GetAllWhere((entry) => entry.UserId == mergeFromUser.Id))
{
var project = _projectModel.GetById(projectRole.ProjectId);
var role = _roleModel.GetById(projectRole.RoleId);
// Check if entry already exists
if (_projectRoleModel.GetAllWhere(
(entry) =>
entry.UserId == mergeIntoUser.Id
&& entry.ProjectId == projectRole.ProjectId
).Count() == 0)
{
_emitter.EmitUserRemove(new UserEventArgs(Program.Configuration)
{
Project = project,
Role = role,
User = mergeFromUser,
SilentMode = true
});
projectRole.UserId = mergeIntoUser.Id;
_projectRoleModel.Update(projectRole);
_emitter.EmitUserAdd(new UserEventArgs(Program.Configuration)
{
Project = project,
Role = role,
User = mergeIntoUser,
SilentMode = true
});
}
else
{
_projectRoleModel.Delete(projectRole);
_emitter.EmitUserRemove(new UserEventArgs(Program.Configuration)
{
Project = project,
Role = role,
User = mergeFromUser,
SilentMode = true
});
}
}
}
private void MergeUserDiscipline(User mergeIntoUser, User mergeFromUser)
{
foreach (var userDiscipline in _userDisciplineModel.GetAllWhere((entry) => entry.UserId == mergeFromUser.Id))
{
// Check if entry already exists
if (_userDisciplineModel.GetAllWhere(
(entry) =>
entry.UserId == mergeIntoUser.Id
&& entry.DisciplineId == userDiscipline.DisciplineId
).Count() == 0)
{
userDiscipline.UserId = mergeIntoUser.Id;
_userDisciplineModel.Update(userDiscipline);
}
else
{
_userDisciplineModel.Delete(userDiscipline);
}
}
}
private void MergeResourceOwner(User mergeIntoUser, User mergeFromUser)
{
// Get every resource, also get the deleted ones
foreach (
var resource in DatabaseConnection.ConnectToDatabase((db) =>
{
return
(from tableEntry in db.Resources.AsExpandable()
where tableEntry.Creator == mergeFromUser.Id
select tableEntry).ToList();
})
)
{
resource.Creator = mergeIntoUser.Id;
_resourceModel.Update(resource);
}
}
private void MergeTOSAccepted(User mergeIntoUser, User mergeFromUser)
{
foreach (var tosAccepted in _tosModel.GetAllWhere((entry) => entry.UserId == mergeFromUser.Id))
{
// Check if entry already exists
if (_tosModel.GetAllWhere(
(entry) =>
entry.UserId == mergeIntoUser.Id
&& entry.Version == tosAccepted.Version
).Count() == 0)
{
tosAccepted.UserId = mergeIntoUser.Id;
_tosModel.Update(tosAccepted);
}
else
{
_tosModel.Delete(tosAccepted);
}
}
}
private void MergeApiTokens(User mergeIntoUser, User mergeFromUser)
{
foreach (var apiToken in _apiTokenModel.GetAllWhere((entry) => entry.UserId == mergeFromUser.Id))
{
apiToken.UserId = mergeIntoUser.Id;
_apiTokenModel.Update(apiToken);
}
}
private void MergeUserProperties(User mergeIntoUser, User mergeFromUser)
{
if (string.IsNullOrEmpty(mergeIntoUser.EmailAddress) && !string.IsNullOrEmpty(mergeFromUser.EmailAddress))
{
mergeIntoUser.EmailAddress = mergeFromUser.EmailAddress;
}
if (string.IsNullOrEmpty(mergeIntoUser.Surname) && !string.IsNullOrEmpty(mergeFromUser.Surname))
{
mergeIntoUser.Surname = mergeIntoUser.Surname;
}
if (string.IsNullOrEmpty(mergeIntoUser.Givenname) && !string.IsNullOrEmpty(mergeFromUser.Givenname))
{
mergeIntoUser.Givenname = mergeIntoUser.Givenname;
}
if (mergeIntoUser.TitleId == null && mergeFromUser.TitleId != null)
{
mergeIntoUser.TitleId = mergeFromUser.TitleId;
}
if (string.IsNullOrEmpty(mergeIntoUser.Organization) && !string.IsNullOrEmpty(mergeFromUser.Organization))
{
mergeIntoUser.Organization = mergeFromUser.Organization;
}
if (string.IsNullOrEmpty(mergeIntoUser.Institute) && !string.IsNullOrEmpty(mergeFromUser.Institute))
{
mergeIntoUser.Institute = mergeFromUser.Institute;
}
_userModel.Update(mergeIntoUser);
}
private void DeleteUser(User user)
{
_userModel.Delete(user);
ADHandler.DeleteUser(user, Program.Configuration);
}
}
}