Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • coscine/backend/apis/sts
1 result
Select Git revision
Show changes
Commits on Source (3)
Showing
with 332 additions and 86 deletions
......@@ -24,6 +24,10 @@ For ORCiD:
* Set the values for "coscine/global/orcid/url", "coscine/global/orcid/clientid", "coscine/global/orcid/jwksurl" and "coscine/global/orcid/issuer".
For Shibboleth:
* Set the values for "coscine/global/shibboleth/url", "coscine/global/shibboleth/metadata" and "coscine/global/shibboleth/contact".
### Links
* [Commit convention](docs/ESLintConvention.md)
......
......@@ -20,6 +20,14 @@ Instructions for making it run:
* Set the value "coscine/global/ad/password" in your Consul storage with your Active Directory password
* Have fun!
For ORCiD:
* Set the values for "coscine/global/orcid/url", "coscine/global/orcid/clientid", "coscine/global/orcid/jwksurl" and "coscine/global/orcid/issuer".
For Shibboleth:
* Set the values for "coscine/global/shibboleth/url", "coscine/global/shibboleth/metadata" and "coscine/global/shibboleth/contact".
### Links
* [Commit convention](docs/ESLintConvention.md)
......

using Coscine.STS.Models;
using Coscine.STS.Models;
using Microsoft.AspNetCore.Mvc;
using System.IdentityModel.Claims;
using System.Web.Security;
using System.IdentityModel;
using System.IdentityModel.Configuration;
using System.IdentityModel.Protocols.WSTrust;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using System.Threading.Tasks;
using System;
using Coscine.STS.Utils;
using System.Net;
using Coscine.STS.Data;
using Microsoft.AspNetCore.Identity;
namespace Coscine.STS.Controllers
{
public class AccountController : Controller
{
private readonly SignInManager<CoscineUser> _signInManager;
public AccountController(SignInManager<CoscineUser> signInManager)
{
_signInManager = signInManager;
}
[Route("[controller]/login")]
public ActionResult Login(string returnUrl)
{
......@@ -35,11 +31,14 @@ namespace Coscine.STS.Controllers
{
if (ModelState.IsValid)
{
var claims = new[] { new System.Security.Claims.Claim(System.IdentityModel.Claims.ClaimTypes.Name, model.UserId.ToString()) };
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var coscineUser = new CoscineUser()
{
UserName = model.UserId.ToString(),
Email = model.UserId.ToString() + "@coscine.com"
};
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
var result = await _signInManager.UserManager.CreateAsync(coscineUser);
await _signInManager.SignInAsync(coscineUser, isPersistent: false);
return Redirect(UrlGenerator.ExtendReturnUrl(returnUrl, Request));
}
......
......@@ -4,14 +4,10 @@ using System.IdentityModel.Services;
using System.IdentityModel.Tokens;
using System.Security.Claims;
using Coscine.STS.Security;
using System.Configuration;
using System.Security.Principal;
using System.Threading;
using System.Web.Security;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Authorization;
using System.Net;
using Microsoft.AspNetCore.Http;
using Coscine.STS.Utils;
namespace Coscine.STS.Controllers
{
......@@ -41,13 +37,8 @@ namespace Coscine.STS.Controllers
}
}
}
var query = Request.Query;
var addedEntries = "";
foreach(var queryEntry in query)
{
addedEntries += "&" + queryEntry.Key + "=" + queryEntry.Value;
}
return Redirect(Program.MainUrl + "/account/login?ReturnUrl=" + WebUtility.UrlEncode(Program.MainUrl) + addedEntries);
string loginUrl = UrlGenerator.GetLoginUrl(Request);
return Redirect(loginUrl);
}
private string ProcessSignIn(Uri url, ClaimsPrincipal user)
......
using Coscine.ApiCommons.Utils;
using Coscine.Database.Model;
using Coscine.Database.Model;
using Coscine.STS.Models;
using Coscine.STS.Utils;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using System;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using System.Net;
using Microsoft.IdentityModel.Logging;
using Coscine.ApiCommons.Models;
using System.Linq;
using Coscine.STS.Data;
using Microsoft.AspNetCore.Identity;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
#region DupFinder Exclusion
namespace Coscine.STS.Controllers
{
public class ORCiDController : Controller
{
private readonly SignInManager<CoscineUser> _signInManager;
public ORCiDController(SignInManager<CoscineUser> signInManager)
{
_signInManager = signInManager;
}
[Route("[controller]/login")]
public ActionResult Login(string returnUrl)
{
......@@ -59,15 +60,16 @@ namespace Coscine.STS.Controllers
ExternalIdModel externalIdModel = new ExternalIdModel();
var mapping = externalIdModel.GetAllWhere((map) => map.ExternalId_Column == ORCiD && map.ExternalAuthenticatorId == orcidAuthItem.Id);
Guid userId;
UserPlainModel userPlainModel = new UserPlainModel(Program.Configuration);
User user;
if (mapping.Count() > 0)
{
userId = mapping.First().UserId;
var userId = mapping.First().UserId;
user = userPlainModel.GetById(userId);
}
else
{
UserPlainModel userPlainModel = new UserPlainModel(Program.Configuration);
var user = new User
user = new User
{
DisplayName = (givenname + " " + surname).Trim(),
EmailAddress = ORCiD + "@orcid.org",
......@@ -81,14 +83,16 @@ namespace Coscine.STS.Controllers
ExternalAuthenticatorId = orcidAuthItem.Id,
UserId = user.Id
});
userId = user.Id;
}
var identityClaims = new[] { new System.Security.Claims.Claim(System.IdentityModel.Claims.ClaimTypes.Name, userId.ToString()) };
var identity = new ClaimsIdentity(identityClaims, CookieAuthenticationDefaults.AuthenticationScheme);
var coscineUser = new CoscineUser()
{
UserName = user.Id.ToString(),
Email = user.EmailAddress
};
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
var result = await _signInManager.UserManager.CreateAsync(coscineUser);
await _signInManager.SignInAsync(coscineUser, isPersistent: false);
return Redirect(UrlGenerator.ExtendReturnUrl(returnUrl, Request));
}
......@@ -97,3 +101,4 @@ namespace Coscine.STS.Controllers
}
}
}
#endregion
\ No newline at end of file
using Coscine.STS.Data;
using Coscine.STS.Models;
using Coscine.STS.Utils;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Coscine.ApiCommons.Models;
using Coscine.Database.Model;
namespace Coscine.STS.Controllers
{
[Route("[controller]/[action]")]
public class ShibbolethController : Controller
{
private readonly SignInManager<CoscineUser> _signInManager;
public ShibbolethController(SignInManager<CoscineUser> signInManager)
{
_signInManager = signInManager;
}
[HttpGet]
public async Task<ActionResult> Callback(string returnUrl = null, string remoteError = null)
{
if (remoteError != null)
{
throw new ArgumentException($"Error from external provider: {remoteError}");
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return Redirect(UrlGenerator.GetLoginUrl(Request));
}
ExternalAuthenticatorModel externalAuthenticatorModel = new ExternalAuthenticatorModel();
var shibbolethAuthItem = externalAuthenticatorModel.GetWhere((externalAuthenticator) => externalAuthenticator.DisplayName == "Shibboleth");
ExternalIdModel externalIdModel = new ExternalIdModel();
var identifier = info.Principal.FindFirstValue(ShibbolethAttributeMapping.Identifier);
identifier = identifier.Substring(identifier.IndexOf(">") + 1);
identifier = identifier.Substring(0, identifier.IndexOf("<"));
var mapping = externalIdModel.GetAllWhere((map) => map.ExternalId_Column == identifier && map.ExternalAuthenticatorId == shibbolethAuthItem.Id);
User user;
UserPlainModel userPlainModel = new UserPlainModel(Program.Configuration);
if (mapping.Count() > 0)
{
var userId = mapping.First().UserId;
user = userPlainModel.GetById(userId);
}
else
{
user = ShibbolethAttributeMapping.CreateUser(info.Principal);
userPlainModel.Insert(user);
externalIdModel.Insert(new ExternalId
{
ExternalId_Column = identifier,
ExternalAuthenticatorId = shibbolethAuthItem.Id,
UserId = user.Id
});
}
var coscineUser = new CoscineUser()
{
UserName = user.Id.ToString(),
Email = user.EmailAddress
};
var result = await _signInManager.UserManager.CreateAsync(coscineUser);
result = await _signInManager.UserManager.AddLoginAsync(coscineUser, info);
await _signInManager.SignInAsync(coscineUser, isPersistent: false);
return Redirect(UrlGenerator.ExtendReturnUrl(returnUrl, Request));
}
[HttpPost]
public ActionResult Login(string returnUrl)
{
var provider = "Saml2";
var redirectUrl = Program.MainUrl + "/Shibboleth/Callback?returnUrl=" + returnUrl;
redirectUrl = UrlGenerator.ExtendReturnUrl(redirectUrl, Request);
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return new ChallengeResult(provider, properties);
}
}
}
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace Coscine.STS.Data
{
public class CoscineDbContext : IdentityDbContext<CoscineUser>
{
public CoscineDbContext(DbContextOptions<CoscineDbContext> options)
: base(options)
{
}
}
}
using System;
using Microsoft.AspNetCore.Identity;
namespace Coscine.STS.Data
{
public class CoscineUser : IdentityUser
{
}
}
......@@ -2,10 +2,7 @@
using Coscine.Database.Model;
using LinqToDB;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace Coscine.STS.Models
{
......
......@@ -2,10 +2,7 @@
using Coscine.Database.Model;
using LinqToDB;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace Coscine.STS.Models
{
......
......@@ -7,9 +7,13 @@ namespace Coscine.STS
{
public class Program : AbstractProgram<ConsulConfiguration>
{
public static string MainUrl { get
public static string HostUrl = $"https://{Dns.GetHostName()}.{System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName}";
public static string ApiPath = $"/coscine/api/{((System.Reflection.Assembly.GetEntryAssembly() != null) ? System.Reflection.Assembly.GetEntryAssembly().GetName().Name : System.Reflection.Assembly.GetExecutingAssembly().GetName().Name)}";
public static string MainUrl {
get
{
return $"https://{Dns.GetHostName()}.{System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName}/coscine/api/{((System.Reflection.Assembly.GetEntryAssembly() != null) ? System.Reflection.Assembly.GetEntryAssembly().GetName().Name : System.Reflection.Assembly.GetExecutingAssembly().GetName().Name)}";
return HostUrl + ApiPath;
}
}
......
......@@ -9,8 +9,8 @@ using System.Reflection;
[assembly: AssemblyDescription("STS is a part of the CoScInE group.")]
[assembly: AssemblyCompany("IT Center, RWTH Aachen University")]
[assembly: AssemblyProduct("STS")]
[assembly: AssemblyVersion("1.6.0.0")]
[assembly: AssemblyFileVersion("1.6.0.0")]
[assembly: AssemblyInformationalVersion("1.6.0.0")]
[assembly: AssemblyVersion("1.7.0.0")]
[assembly: AssemblyFileVersion("1.7.0.0")]
[assembly: AssemblyInformationalVersion("1.7.0.0")]
[assembly: AssemblyCopyright("2019 IT Center, RWTH Aachen University")]
......@@ -10,20 +10,26 @@
<ItemGroup>
<PackageReference Include="Coscine.ApiCommons" Version="1.2.2" />
<PackageReference Include="Coscine.Database" Version="1.10.0" />
<PackageReference Include="Coscine.Database" Version="1.11.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.CookiePolicy" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.HttpsPolicy" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.6" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="2.2.0" />
<PackageReference Include="Microsoft.IdentityModel" Version="7.0.0" />
<PackageReference Include="Microsoft.NET.Sdk.Razor" Version="2.2.0" />
<PackageReference Include="System.DirectoryServices" Version="4.6.0" />
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="4.6.0" />
<PackageReference Include="System.DirectoryServices.Protocols" Version="4.6.0" />
<PackageReference Include="Sustainsys.Saml2.AspNetCore2" Version="2.3.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.5.0" />
</ItemGroup>
......
......@@ -6,24 +6,21 @@ using System.IdentityModel;
using System.IdentityModel.Configuration;
using System.IdentityModel.Protocols.WSTrust;
using System.IdentityModel.Tokens;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace Coscine.STS.Security
{
public class CustomSecurityTokenService : SecurityTokenService
{
static readonly string[] SupportedWebApps = { };
private static readonly string[] SupportedWebApps = { };
public CustomSecurityTokenService(SecurityTokenServiceConfiguration securityTokenServiceConfiguration) : base(securityTokenServiceConfiguration)
{
}
static void ValidateAppliesTo(EndpointReference appliesTo)
private static void ValidateAppliesTo(EndpointReference appliesTo)
{
if (SupportedWebApps == null || SupportedWebApps.Length == 0) return;
......@@ -31,7 +28,7 @@ namespace Coscine.STS.Security
if (!validAppliesTo)
{
throw new InvalidRequestException(String.Format("The 'appliesTo' address '{0}' is not valid.", appliesTo.Uri.OriginalString));
throw new InvalidRequestException(string.Format("The 'appliesTo' address '{0}' is not valid.", appliesTo.Uri.OriginalString));
}
}
......
using Coscine.ApiCommons;
using Coscine.STS.Data;
using Coscine.STS.Security;
using Coscine.STS.Utils;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Sustainsys.Saml2;
using Sustainsys.Saml2.Metadata;
using System;
namespace Coscine.STS
{
......@@ -18,10 +25,59 @@ namespace Coscine.STS
{
base.ConfigureServicesExtension(services);
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(
//o => o.LoginPath = PathString.FromUriComponent(Program.MainUrl + "/account/login")
services.AddDbContext<CoscineDbContext>(
options =>
options.UseInMemoryDatabase("CoscineDbContext")
);
services.AddIdentity<CoscineUser, IdentityRole>()
.AddEntityFrameworkStores<CoscineDbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie()
.AddSaml2(options =>
{
options.SPOptions.EntityId = new EntityId(Program.MainUrl + "/Saml2");
options.SPOptions.PublicOrigin = new Uri(Program.HostUrl);
options.SPOptions.ModulePath = Program.ApiPath + "/Saml2";
options.SPOptions.Compatibility.UnpackEntitiesDescriptorInIdentityProviderMetadata = true;
options.IdentityProviders.Add(
new IdentityProvider(
new EntityId(Program.Configuration.GetStringAndWait("coscine/global/shibboleth/url")), options.SPOptions)
{
MetadataLocation = Program.Configuration.GetStringAndWait("coscine/global/shibboleth/metadata"),
LoadMetadata = true,
});
var contact = new ContactPerson
{
Type = ContactType.Support,
};
contact.EmailAddresses.Add(Program.Configuration.GetStringAndWait("coscine/global/shibboleth/contact"));
options.SPOptions.Contacts.Add(contact);
var attributeConsumingService = new AttributeConsumingService
{
IsDefault = true,
ServiceNames = { new LocalizedName("Saml2", "en") }
};
foreach(var pair in ShibbolethAttributeMapping.LabelMapping)
{
attributeConsumingService.RequestedAttributes.Add(
new RequestedAttribute(pair.Key)
{
FriendlyName = pair.Value,
IsRequired = true,
NameFormat = RequestedAttribute.AttributeNameFormatUri
});
}
options.SPOptions.AttributeConsumingServices.Add(attributeConsumingService);
options.SPOptions.ServiceCertificates.Add(CustomSecurityTokenService.GetCertificate());
});
}
public override void ConfigureExtension(IApplicationBuilder app, IHostingEnvironment env)
......
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Coscine.STS.Utils
{
......
using Coscine.Database.Model;
using System.Collections.Generic;
using System.Security.Claims;
namespace Coscine.STS.Utils
{
public class ShibbolethAttributeMapping
{
public static string Identifier { get; private set; } = "urn:oid:1.3.6.1.4.1.5923.1.1.1.10";
public static Dictionary<string, string> LabelMapping { get; private set; } = new Dictionary<string, string>()
{
{ "urn:oid:2.16.840.1.113730.3.1.241", "DisplayName" },
{ "urn:oid:2.5.4.4", "Surname" },
{ "urn:oid:2.5.4.42", "Givenname" },
{ "urn:oid:0.9.2342.19200300.100.1.3", "EmailAddress" },
{ "urn:oid:1.3.6.1.4.1.5923.1.1.1.9", "Entitlement" },
{ "urn:oid:2.5.4.10", "Organization" },
};
public static void SetUserAttribute(User user, string identifier, object value)
{
switch (identifier)
{
case "urn:oid:2.16.840.1.113730.3.1.241":
user.DisplayName = (string) value;
break;
case "urn:oid:2.5.4.4":
user.Surname = (string)value;
break;
case "urn:oid:2.5.4.42":
user.Givenname = (string)value;
break;
case "urn:oid:0.9.2342.19200300.100.1.3":
user.EmailAddress = (string)value;
break;
case "urn:oid:1.3.6.1.4.1.5923.1.1.1.9":
if (user.Entitlement == null || !user.Entitlement.Contains("employee"))
{
user.Entitlement = (string)value;
}
break;
case "urn:oid:2.5.4.10":
user.Organization = (string)value;
break;
default:
break;
}
}
public static User CreateUser(ClaimsPrincipal principal)
{
User user = new User();
foreach(var key in LabelMapping.Keys)
{
SetUserAttribute(user, key, principal.FindFirstValue(key));
}
return user;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.IdentityModel;
using System.IdentityModel.Configuration;
using System.IdentityModel.Protocols.WSTrust;
using System.IdentityModel.Tokens;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using System.Net;
namespace Coscine.STS.Utils
{
public class UrlGenerator
{
public static string GetLoginUrl(HttpRequest request)
{
var query = request.Query;
var addedEntries = "";
foreach (var queryEntry in query)
{
addedEntries += "&" + queryEntry.Key + "=" + queryEntry.Value;
}
var loginUrl = Program.MainUrl + "/account/login?ReturnUrl=" + WebUtility.UrlEncode(Program.MainUrl) + addedEntries;
return loginUrl;
}
public static string ExtendReturnUrl(string returnUrl, HttpRequest request)
{
string retString = returnUrl;
......
......@@ -21,11 +21,19 @@
<br>
<form action="/coscine/api/Coscine.STS/Account/login?returnUrl=@ViewBag.ReturnUrl" method="post">
<input data-val="true" data-val-required="$t('login_form_id_required')" id="userId" placeholder="User ID" name="UserId" type="text" value="" class="form-control" />
<input data-val="true" id="userId" placeholder="User ID" name="UserId" type="text" value="" class="form-control" />
<input type="submit" id="guidbutton" value="Sign in using User Id"></input>
<input type="hidden" name="wa" value="wsignin1.0" />
</form>
<br>
<br>
<form action="/coscine/api/Coscine.STS/Shibboleth/login?returnUrl=@ViewBag.ReturnUrl" method="post">
<input type="submit" id="guidbutton" value="Sign in using Shibboleth"></input>
<input type="hidden" name="wa" value="wsignin1.0" />
</form>
<script type="text/javascript">
function getORCiDUrl() {
var hookupElement = document.getElementById('loginpage');
......