Skip to content

Commit

Permalink
sync with master (#45)
Browse files Browse the repository at this point in the history
* Fixed build warnings

* Add files via upload

* Multiple issues addressed. (#25)

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.

* Get ResourceTypes test condition (#26)

* Get ResourceTypes test condition

* fix Post enterprise user test, user enterprise schema for department

Co-authored-by: Plamen Stoyanov <[email protected]>

* Update README.md

* Added support for Custom Protocol (#33)

Signed-off-by: Dan Elkis <[email protected]>

Co-authored-by: RyanE <[email protected]>
Co-authored-by: ArvindHarinder1 <[email protected]>
Co-authored-by: Ankit K Chheda <[email protected]>
Co-authored-by: plamenGo <[email protected]>
Co-authored-by: Plamen Stoyanov <[email protected]>
Co-authored-by: Dan Elkis <[email protected]>
  • Loading branch information
7 people authored Nov 10, 2020
1 parent 137b015 commit 68688e1
Show file tree
Hide file tree
Showing 20 changed files with 4,588 additions and 111 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 @@ -2,6 +2,7 @@

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<NeutralLanguage>en</NeutralLanguage>
</PropertyGroup>

<ItemGroup>
Expand Down Expand Up @@ -50,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 @@ -134,7 +134,7 @@ private set
string encodedValue = this.comparisonValue;
foreach (KeyValuePair<string, string> encoding in Filter.ReservedCharacterEncodingsPerRfc2396.Value)
{
encodedValue = encodedValue.Replace(encoding.Key, encoding.Value);
encodedValue = encodedValue.Replace(encoding.Key, encoding.Value, StringComparison.InvariantCulture);
}
this.comparisonValueEncoded = encodedValue;
}
Expand Down Expand Up @@ -185,7 +185,7 @@ private static IReadOnlyDictionary<string, string> InitializeReservedCharacter39
new Dictionary<string, string>(Filter.ReservedCharactersPerRfc3986.Value.Length);
foreach (char character in Filter.ReservedCharactersPerRfc3986.Value)
{
string from = character.ToString();
string from = character.ToString(CultureInfo.InvariantCulture);
string to = HttpUtility.UrlEncode(from);
result.Add(from, to);
}
Expand Down 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);
string encodedFilter =
HttpUtility
.UrlEncode(currentFilter)
.Replace(placeholder, filter.ComparisonValueEncoded, StringComparison.InvariantCulture);
if (string.IsNullOrWhiteSpace(allFilters))
{
allFilters =
Expand Down Expand Up @@ -333,7 +336,13 @@ public static string ToString(IReadOnlyCollection<IFilter> filters)

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

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

try
{
IReadOnlyCollection<IFilter> buffer = new FilterExpression(expression).ToFilters();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,9 @@ private int Group
CultureInfo.InvariantCulture,
SystemForCrossDomainIdentityManagementProtocolResources.ExceptionInvalidFilterTemplate,
this.Text);
#pragma warning disable CA1303 // Do not pass literals as localized parameters
throw new ArgumentOutOfRangeException(message, nameof(this.Group));
#pragma warning restore CA1303 // Do not pass literals as localized parameters
}
this.groupValue = value;
}
Expand All @@ -247,7 +249,9 @@ private int Level
CultureInfo.InvariantCulture,
SystemForCrossDomainIdentityManagementProtocolResources.ExceptionInvalidFilterTemplate,
this.Text);
#pragma warning disable CA1303 // Do not pass literals as localized parameters
throw new ArgumentOutOfRangeException(message, nameof(this.Level));
#pragma warning restore CA1303 // Do not pass literals as localized parameters
}
this.levelValue = value;
}
Expand Down Expand Up @@ -604,7 +608,7 @@ private void Initialize(Group left, Group @operator, Group right)
}

string nextExpression = remainder.Substring(indexNextFilter);
int indexClosingBracket = remainder.IndexOf(FilterExpression.BracketClose);
int indexClosingBracket = remainder.IndexOf(FilterExpression.BracketClose, StringComparison.InvariantCulture);
int nextExpressionLevel;
int nextExpressionGroup;
if (indexClosingBracket >= 0 && indexClosingBracket < indexLogicalOperator)
Expand Down Expand Up @@ -738,7 +742,7 @@ private static bool TryParse(string input, out string comparisonValue)
}
else
{
int index = input.IndexOf(FilterExpression.Space);
int index = input.IndexOf(FilterExpression.Space, StringComparison.InvariantCulture);
if (index >= 0)
{
// If unquoted string comparison values were to be rejected,
Expand Down
Loading

0 comments on commit 68688e1

Please sign in to comment.