Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/customer-key-store/Controllers/KeysController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Microsoft.InformationProtection.Web.Controllers
{
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.Extensions;
Expand Down Expand Up @@ -39,11 +40,11 @@ public IActionResult GetKey(string keyName)

[HttpPost]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public IActionResult Decrypt(string keyName, string keyId, [FromBody] ippw.EncryptedData encryptedData)
public async Task<IActionResult> Decrypt(string keyName, string keyId, [FromBody] ippw.EncryptedData encryptedData)
{
try
{
var decryptedData = keyManager.Decrypt(HttpContext.User, keyName, keyId, encryptedData);
var decryptedData = await keyManager.Decrypt(HttpContext.User, keyName, keyId, encryptedData).ConfigureAwait(false);

return Ok(decryptedData);
}
Expand Down
3 changes: 2 additions & 1 deletion src/customer-key-store/Models/Authorizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
namespace Microsoft.InformationProtection.Web.Models
{
using System.Security.Claims;
using System.Threading.Tasks;
public interface IAuthorizer
{
void CanUserAccessKey(ClaimsPrincipal user, KeyStoreData key);
Task ProcessAccessRequest(ClaimsPrincipal user, KeyStoreData key);
}
}
26 changes: 18 additions & 8 deletions src/customer-key-store/Models/EmailAuthorizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ namespace Microsoft.InformationProtection.Web.Models
{
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;

using Microsoft.InformationProtection.Web.Models.Extensions;
public class EmailAuthorizer : IAuthorizer
Expand All @@ -12,14 +13,7 @@ public class EmailAuthorizer : IAuthorizer
private const string UpnClaim = ClaimTypes.Upn;
private HashSet<string> validEmails = new HashSet<string>(System.StringComparer.OrdinalIgnoreCase);

public void AddEmail(string email)
{
email.ThrowIfNull(nameof(email));

validEmails.Add(email.Trim());
}

public void CanUserAccessKey(ClaimsPrincipal user, KeyStoreData key)
public static string GetEmailFromClaims(ClaimsPrincipal user)
{
string email = null;

Expand All @@ -39,6 +33,20 @@ public void CanUserAccessKey(ClaimsPrincipal user, KeyStoreData key)
}
}

return email;
}

public void AddEmail(string email)
{
email.ThrowIfNull(nameof(email));

validEmails.Add(email.Trim());
}

public Task ProcessAccessRequest(ClaimsPrincipal user, KeyStoreData key)
{
string email = EmailAuthorizer.GetEmailFromClaims(user);

if(email == null)
{
throw new System.ArgumentException("The email or upn claim is required");
Expand All @@ -48,6 +56,8 @@ public void CanUserAccessKey(ClaimsPrincipal user, KeyStoreData key)
{
throw new CustomerKeyStore.Models.KeyAccessException("User does not have access to the key");
}

return Task.FromResult(true);
}
}
}
5 changes: 3 additions & 2 deletions src/customer-key-store/Models/KeyManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ namespace Microsoft.InformationProtection.Web.Models
{
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.InformationProtection.Web.Models.Extensions;
using sg = System.Globalization;

Expand Down Expand Up @@ -39,7 +40,7 @@ public KeyData GetPublicKey(Uri requestUri, string keyName)
return new KeyData(publicKey, cache);
}

public DecryptedData Decrypt(ClaimsPrincipal user, string keyName, string keyId, EncryptedData encryptedData)
public async Task<DecryptedData> Decrypt(ClaimsPrincipal user, string keyName, string keyId, EncryptedData encryptedData)
{
user.ThrowIfNull(nameof(user));
keyName.ThrowIfNull(nameof(keyName));
Expand All @@ -48,7 +49,7 @@ public DecryptedData Decrypt(ClaimsPrincipal user, string keyName, string keyId,

var keyData = keyStore.GetKey(keyName, keyId);

keyData.KeyAuth.CanUserAccessKey(user, keyData);
await keyData.KeyAuth.ProcessAccessRequest(user, keyData).ConfigureAwait(false);

if (encryptedData.Algorithm != "RSA-OAEP-256")
{
Expand Down
29 changes: 29 additions & 0 deletions src/customer-key-store/Models/MultiFactorAuthorizer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
namespace Microsoft.InformationProtection.Web.Models
{
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.InformationProtection.Web.Models.Extensions;

public class MultiFactorAuthorizer : IAuthorizer
{
private IAuthorizer mPrimaryAuthorizer;
private IAuthorizer mSecondaryAuthorizer;

public MultiFactorAuthorizer(IAuthorizer primaryAuthorizer, IAuthorizer secondaryAuthorizer)
{
mPrimaryAuthorizer = primaryAuthorizer;
mSecondaryAuthorizer = secondaryAuthorizer;
}

public async Task ProcessAccessRequest(ClaimsPrincipal user, KeyStoreData key)
{
user.ThrowIfNull(nameof(user));

await mPrimaryAuthorizer.ProcessAccessRequest(user, key).ConfigureAwait(false);

await mSecondaryAuthorizer.ProcessAccessRequest(user, key).ConfigureAwait(false);
}
}
}
27 changes: 27 additions & 0 deletions src/customer-key-store/Models/PushAuthorizer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
namespace Microsoft.InformationProtection.Web.Models
{
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.InformationProtection.Web.Models.Extensions;

public class PushAuthorizer : IAuthorizer
{
public PushAuthorizer(string pushService)
{
PushService = pushService;
}

public string PushService { get; private set; }

public Task ProcessAccessRequest(ClaimsPrincipal user, KeyStoreData key)
{
user.ThrowIfNull(nameof(user));
var email = EmailAuthorizer.GetEmailFromClaims(user);

//send email to push notification service
return Task.FromResult(true);
}
}
}
21 changes: 12 additions & 9 deletions src/customer-key-store/Models/RoleAuthorizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace Microsoft.InformationProtection.Web.Models
using System.Collections.Generic;
using System.DirectoryServices;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.InformationProtection.Web.Models.Extensions;
public class RoleAuthorizer : IAuthorizer
Expand All @@ -22,12 +23,18 @@ public RoleAuthorizer(IConfiguration configuration)
ldapPath = configuration["RoleAuthorizer:LDAPPath"];
}

public static string GetRole(string memberOf)
{
memberOf.ThrowIfNull(nameof(memberOf));
return ParseCN(memberOf);
}

public void AddRole(string role)
{
roles.Add(role);
}

public void CanUserAccessKey(string sid)
public Task ProcessAccessRequest(string sid)
{
sid.ThrowIfNull(nameof(sid));

Expand Down Expand Up @@ -63,9 +70,11 @@ public void CanUserAccessKey(string sid)
}
}
}

return Task.FromResult(true);
}

public void CanUserAccessKey(ClaimsPrincipal user, KeyStoreData key)
public Task ProcessAccessRequest(ClaimsPrincipal user, KeyStoreData key)
{
user.ThrowIfNull(nameof(user));

Expand All @@ -85,7 +94,7 @@ public void CanUserAccessKey(ClaimsPrincipal user, KeyStoreData key)
throw new System.ArgumentException(SidClaim + " claim not found");
}

CanUserAccessKey(sid);
return ProcessAccessRequest(sid);
}

private static string ParseCN(string distinguishedName)
Expand Down Expand Up @@ -133,11 +142,5 @@ private static string ParseCN(string distinguishedName)

return role.ToString();
}

public static string GetRole(string memberOf)
{
memberOf.ThrowIfNull(nameof(memberOf));
return ParseCN(memberOf);
}
}
}
14 changes: 14 additions & 0 deletions src/customer-key-store/Models/TestStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@ public TestKeyStore(IConfiguration configuration)
}
}

var pushNotifications = testKey.GetSection("PushNotificationService");
if(pushNotifications != null && pushNotifications.Exists())
{
if(keyAuth == null)
{
keyAuth = new PushAuthorizer(pushNotifications.Value);
}
else
{
//Currently, push authorizer always comes second.
keyAuth = new MultiFactorAuthorizer(keyAuth, new PushAuthorizer(pushNotifications.Value));
}
}

int? expirationTimeInDays = null;
var cacheTime = testKey["CacheExpirationInDays"];
if(cacheTime != null)
Expand Down
1 change: 1 addition & 0 deletions src/customer-key-store/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"Id": "GUID",
"AuthorizedRoles": ["On premises Active Directory groups that you want to have access to this key. If you provide a value for AuthorizedRoles, then remove the line that starts with AuthorizedEmailAddress."],
"AuthorizedEmailAddress": ["Email addresses of users that have access to this key. If you provide a value for AuthorizedEmailAddress, then remove the line that starts with AuthorizedRoles."],
"PushNotificationService":"Service that will handle push notifications for MFA to send an access request to a user's mobile device. This value can be present along with one other of 'AuthorizedRoles' or 'AuthorizedEmailAddress' for multifactor auth.",
"PublicPem" : "The public key in PEM format. Do not include the BEGIN and END lines",
"PrivatePem": "The private key in PEM format. Do not include the BEGIN and END lines"
}
Expand Down