Skip to content

Commit

Permalink
Multiple issues addressed. (AzureAD#25)
Browse files Browse the repository at this point in the history
1. When the group membership to be removed is inlined in the path, it is now removed correctly from the group.
2. Members already part of the group cannot be re-added to the group.
3. Patching of the extension attribute used to fail if the user was not created using the extension attribute. That has been fixed.
4. Sundry formatting changes and fixes.
  • Loading branch information
ankitC authored May 5, 2020
1 parent 49d65f5 commit 5820269
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 99 deletions.
39 changes: 18 additions & 21 deletions Microsoft.SCIM.WebHostSample/Controllers/TokenController.cs
Original file line number Diff line number Diff line change
@@ -1,54 +1,51 @@
// Copyright (c) Microsoft Corporation.// Licensed under the MIT license.

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

namespace Microsoft.SCIM.WebHostSample.Controllers
{
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;

// Controller for generating a bearer token for authorization during testing.
// This is not meant to replace proper Oauth for authentication purposes.
[Route("scim/token")]
[ApiController]
public class TokenController : ControllerBase
{
private readonly IConfiguration _configuration;

private const int defaultTokenExpiration = 120;
private readonly IConfiguration configuration;
private const int defaultTokenExpirationTimeInMins = 120;

public TokenController(IConfiguration Configuration)
{
_configuration = Configuration;
this.configuration = Configuration;
}

private string GenerateJSONWebToken()
{
// Create token key
SymmetricSecurityKey securityKey =
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(this._configuration["Token:IssuerSigningKey"]));
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(this.configuration["Token:IssuerSigningKey"]));
SigningCredentials credentials =
new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

// Set token expiration
DateTime startTime = DateTime.UtcNow;
DateTime expiryTime;
double tokenExpiration;
if (double.TryParse(this._configuration["Token:TokenLifetimeInMins"], out tokenExpiration))
if (double.TryParse(this.configuration["Token:TokenLifetimeInMins"], out double tokenExpiration))
expiryTime = startTime.AddMinutes(tokenExpiration);
else
expiryTime = startTime.AddMinutes(defaultTokenExpiration);
expiryTime = startTime.AddMinutes(defaultTokenExpirationTimeInMins);

// Generate the token
JwtSecurityToken token =
new JwtSecurityToken(
this._configuration["Token:TokenIssuer"],
this._configuration["Token:TokenAudience"],
this.configuration["Token:TokenIssuer"],
this.configuration["Token:TokenAudience"],
null,
notBefore: startTime,
expires: expiryTime,
Expand Down
18 changes: 7 additions & 11 deletions Microsoft.SCIM.WebHostSample/Program.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
// Copyright (c) Microsoft Corporation.// Licensed under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

namespace Microsoft.SCIM.WebHostSample
{
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
Program.CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Expand Down
106 changes: 53 additions & 53 deletions Microsoft.SCIM.WebHostSample/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,35 +1,33 @@
// Copyright (c) Microsoft Corporation.// Licensed under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using Microsoft.SCIM.WebHostSample.Provider;
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

namespace Microsoft.SCIM.WebHostSample
{
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using Microsoft.SCIM.WebHostSample.Provider;

public class Startup
{
private readonly IWebHostEnvironment _env;
private readonly IConfiguration _configuration;
private readonly IWebHostEnvironment environment;
private readonly IConfiguration configuration;

public IMonitor MonitoringBehavior { get; set; }
public IProvider ProviderBehavior { get; set; }

public Startup(IWebHostEnvironment env, IConfiguration configuration)
{
this._env = env;
this._configuration = configuration;
this.environment = env;
this.configuration = configuration;

this.MonitoringBehavior = new ConsoleMonitor();
this.ProviderBehavior = new InMemoryProvider();
Expand All @@ -39,7 +37,7 @@ public Startup(IWebHostEnvironment env, IConfiguration configuration)
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
if (_env.IsDevelopment())
if (this.environment.IsDevelopment())
{
// Development environment code
// Validation for bearer token for authorization used during testing.
Expand All @@ -51,20 +49,20 @@ public void ConfigureServices(IServiceCollection services)
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters =
new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = false,
ValidateIssuerSigningKey = false,
ValidIssuer = this._configuration["Token:TokenIssuer"],
ValidAudience = this._configuration["Token:TokenAudience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(this._configuration["Token:IssuerSigningKey"]))
};
});
.AddJwtBearer(options =>
{
options.TokenValidationParameters =
new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = false,
ValidateIssuerSigningKey = false,
ValidIssuer = this.configuration["Token:TokenIssuer"],
ValidAudience = this.configuration["Token:TokenAudience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(this.configuration["Token:IssuerSigningKey"]))
};
});
}
else
{
Expand All @@ -79,33 +77,32 @@ public void ConfigureServices(IServiceCollection services)
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
.AddJwtBearer(options =>
{
options.Authority = this.configuration["Token:TokenIssuer"];
options.Audience = this.configuration["Token:TokenAudience"];
options.Events = new JwtBearerEvents
{
options.Authority = this._configuration["Token:TokenIssuer"];
options.Audience = this._configuration["Token:TokenAudience"];
options.Events = new JwtBearerEvents
OnTokenValidated = context =>
{
OnTokenValidated = context =>
{
// NOTE: You can optionally take action when the OAuth 2.0 bearer token was validated.
// NOTE: You can optionally take action when the OAuth 2.0 bearer token was validated.

return Task.CompletedTask;
},
OnAuthenticationFailed = AuthenticationFailed
};
});
return Task.CompletedTask;
},
OnAuthenticationFailed = AuthenticationFailed
};
});
}

services.AddControllers().AddNewtonsoftJson();

services.AddSingleton(typeof(IProvider), this.ProviderBehavior);
services.AddSingleton(typeof(IMonitor), this.MonitoringBehavior);
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
{
if (_env.IsDevelopment())
if (this.environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
Expand All @@ -127,10 +124,13 @@ public void Configure(IApplicationBuilder app)
private Task AuthenticationFailed(AuthenticationFailedContext arg)
{
// For debugging purposes only!
var s = $"{{AuthenticationFailed: '{arg.Exception.Message}'}}";
string authenticationExceptionMessage = $"{{AuthenticationFailed: '{arg.Exception.Message}'}}";

arg.Response.ContentLength = s.Length;
arg.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(s), 0, s.Length);
arg.Response.ContentLength = authenticationExceptionMessage.Length;
arg.Response.Body.WriteAsync(
Encoding.UTF8.GetBytes(authenticationExceptionMessage),
0,
authenticationExceptionMessage.Length);

return Task.FromException(arg.Exception);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,17 @@
<EmbeddedResource Update="SystemForCrossDomainIdentityManagementServiceResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>SystemForCrossDomainIdentityManagementServiceResources.Designer.cs</LastGenOutput>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Update="SystemForCrossDomainIdentityManagementProtocolResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>SystemForCrossDomainIdentityManagementProtocolResources.Designer.cs</LastGenOutput>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Update="SystemForCrossDomainIdentityManagementSchemasResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>SystemForCrossDomainIdentityManagementSchemasResources.Designer.cs</LastGenOutput>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,10 @@ public static string ToString(IReadOnlyCollection<IFilter> filters)
Filter clone = new Filter(filter);
clone.ComparisonValue = placeholder;
string currentFilter = clone.Serialize();
string encodedFilter = HttpUtility.UrlEncode(currentFilter).Replace(placeholder, filter.ComparisonValueEncoded, StringComparison.InvariantCulture);
string encodedFilter =
HttpUtility
.UrlEncode(currentFilter)
.Replace(placeholder, filter.ComparisonValueEncoded, StringComparison.InvariantCulture);
if (string.IsNullOrWhiteSpace(allFilters))
{
allFilters =
Expand Down Expand Up @@ -333,11 +336,13 @@ public static string ToString(IReadOnlyCollection<IFilter> filters)

public static bool TryParse(string filterExpression, out IReadOnlyCollection<IFilter> filters)
{
if(filterExpression == null)
string expression = filterExpression?.Trim()?.Unquote();

if (string.IsNullOrWhiteSpace(expression))
{
throw new ArgumentNullException(nameof(filterExpression));
}
string expression = filterExpression.Trim().Unquote();

try
{
IReadOnlyCollection<IFilter> buffer = new FilterExpression(expression).ToFilters();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace Microsoft.SCIM
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.Serialization;
using Newtonsoft.Json;

Expand All @@ -32,7 +33,13 @@ public string Value
{
get
{
return JsonConvert.SerializeObject(this.values);
if (this.values == null)
{
return null;
}

string result = JsonConvert.SerializeObject(this.values);
return result;
}

set
Expand All @@ -41,6 +48,26 @@ public string Value
}
}

[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
if (this.Value == null)
{
if
(
this?.Path?.AttributePath != null &&
this.Path.AttributePath.Contains(AttributeNames.Members, StringComparison.OrdinalIgnoreCase) &&
this.Name == SCIM.OperationName.Remove &&
this.Path?.SubAttributes?.Count == 1
)
{
this.Value = this.Path.SubAttributes.First().ComparisonValue;
IPath path = SCIM.Path.Create(AttributeNames.Members);
this.Path = path;
}
}
}

public override string ToString()
{
string allValues = string.Join(Environment.NewLine, this.Value);
Expand Down
Loading

0 comments on commit 5820269

Please sign in to comment.