Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

B hasanov adding roles #33

Merged
merged 14 commits into from
Feb 15, 2021
Merged
13 changes: 5 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,19 @@ RUN apt-get update \


WORKDIR /OoS-Backend
COPY ./OutOfSchool/*.sln ./
COPY ./OutOfSchool/OutOfSchool.Tests/*.csproj ./OutOfSchool.Tests/
COPY ./OutOfSchool/OutOfSchool.WebApi/*.csproj ./OutOfSchool.WebApi/
COPY ./OutOfSchool/IdentityServer/*.csproj ./IdentityServer/
COPY ./OutOfSchool/OutOfSchool.DataAccess/*.csproj ./OutOfSchool.DataAccess/

RUN dotnet restore
RUN dotnet restore ./OutOfSchool.WebApi/OutOfSchool.WebApi.csproj

COPY ./OutOfSchool/ ./
COPY ./OutOfSchool/OutOfSchool.WebApi/ ./OutOfSchool.WebApi/
COPY ./OutOfSchool/OutOfSchool.DataAccess/ ./OutOfSchool.DataAccess/

RUN dotnet build -c $Configuration -o /app
RUN dotnet build ./OutOfSchool.WebApi/OutOfSchool.WebApi.csproj -c $Configuration -o /app

FROM builder AS publish
ARG Configuration=Release
RUN dotnet publish -c $Configuration -o /app
#TODO: Remove unnecessary projects
RUN dotnet publish ./OutOfSchool.WebApi/OutOfSchool.WebApi.csproj -c $Configuration -o /app
FROM base AS final
COPY --from=publish /app .
EXPOSE 5000 5001
Expand Down
126 changes: 95 additions & 31 deletions OutOfSchool/IdentityServer/Controllers/AuthController.cs
Original file line number Diff line number Diff line change
@@ -1,36 +1,54 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using IdentityServer4.Services;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using System.Threading.Tasks;
using OutOfSchool.Services.Models;

namespace IdentityServer.Controllers
{
/// <summary>
/// Handles authentication.
/// Contains methods for log in and sign up.
/// </summary>
public class AuthController : Controller
{
private readonly SignInManager<User> _signInManager;
private readonly UserManager<User> _userManager;
private readonly IIdentityServerInteractionService _interactionService;
private readonly SignInManager<User> signInManager;
private readonly UserManager<User> userManager;
private readonly IIdentityServerInteractionService interactionService;
private readonly RoleManager<IdentityRole> roleManager;

/// <summary>
/// Initializes a new instance of the <see cref="AuthController"/> class.
/// </summary>
/// <param name="userManager"> ASP.Net Core Identity User Manager.</param>
/// <param name="signInManager"> ASP.Net Core Identity Sign in Manager.</param>
/// <param name="roleManager">ASP.Net Core Identity Role Manager.</param>
/// <param name="interactionService"> Identity Server 4 interaction service.</param>
public AuthController(
UserManager<User> userManager,
SignInManager<User> signInManager,
RoleManager<IdentityRole> roleManager,
IIdentityServerInteractionService interactionService)
{
_signInManager = signInManager;
_userManager = userManager;
_interactionService = interactionService;
roleManager = roleManager;
signInManager = signInManager;
userManager = userManager;
interactionService = interactionService;
}

/// <summary>
/// Logging out a user who is authenticated.
/// </summary>
/// <param name="logoutId"> Identifier of cookie captured the current state needed for sign out.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
[HttpGet]
public async Task<IActionResult> Logout(string logoutId)
{
await _signInManager.SignOutAsync();
await signInManager.SignOutAsync();

var logoutRequest = await _interactionService.GetLogoutContextAsync(logoutId);
var logoutRequest = await interactionService.GetLogoutContextAsync(logoutId);

if (string.IsNullOrEmpty(logoutRequest.PostLogoutRedirectUri))
{
Expand All @@ -40,62 +58,108 @@ public async Task<IActionResult> Logout(string logoutId)
return Redirect(logoutRequest.PostLogoutRedirectUri);
}

/// <summary>
/// Generates a view for user to log in.
/// </summary>
/// <param name="returnUrl"> URL used to redirect user back to client.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
[HttpGet]
public async Task<IActionResult> Login(string returnUrl="Login")
public async Task<IActionResult> Login(string returnUrl = "Login")
{
var externalProviders = await _signInManager.GetExternalAuthenticationSchemesAsync();
var externalProviders = await signInManager.GetExternalAuthenticationSchemesAsync();
return View(new LoginViewModel
{
ReturnUrl = returnUrl,
ExternalProviders = externalProviders
ExternalProviders = externalProviders,
});
}

/// <summary>
/// Authenticate user based on model.
/// </summary>
/// <param name="model"> View model that contains credentials for logging in.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel vm)
public async Task<IActionResult> Login(LoginViewModel model)
{
// check if the model is valid
if (!ModelState.IsValid)
{
return View(new LoginViewModel
{
ExternalProviders = await signInManager.GetExternalAuthenticationSchemesAsync(),
});
}

var result = await _signInManager.PasswordSignInAsync(vm.Username, vm.Password, false, false);
var result = await signInManager.PasswordSignInAsync(model.Username, model.Password, false, false);

if (result.Succeeded)
{
return Redirect(vm.ReturnUrl);
return Redirect(model.ReturnUrl);
}
else if (result.IsLockedOut)
{

if (result.IsLockedOut)
{
return BadRequest();
}
return View();

ModelState.AddModelError(string.Empty, "Login or password is wrong");
return View(new LoginViewModel
{
ExternalProviders = await signInManager.GetExternalAuthenticationSchemesAsync(),
});
}

/// <summary>
/// Generates a view for user to register.
/// </summary>
/// <param name="returnUrl"> URL used to redirect user back to client.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
[HttpGet]
public IActionResult Register(string returnUrl="Login")
public IActionResult Register(string returnUrl = "Login")
{
return View(new RegisterViewModel { ReturnUrl = returnUrl });
return View(
new RegisterViewModel { ReturnUrl = returnUrl, AllRoles = roleManager.Roles.ToList() });
}

/// <summary>
/// Creates user based on model.
/// </summary>
/// <param name="model"> View model that contains credentials for signing in.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
[HttpPost]
public async Task<IActionResult> Register(RegisterViewModel vm)
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (!ModelState.IsValid)
{
return View(vm);
model.AllRoles = roleManager.Roles.ToList();
return View(model);
}

var user = new User()
{
UserName = vm.Username,
PhoneNumber = vm.PhoneNumber, CreatingTime = DateTime.Now
UserName = model.Username,
PhoneNumber = model.PhoneNumber,
CreatingTime = DateTime.Now,
};
var result = await _userManager.CreateAsync(user, vm.Password);
var result = await userManager.CreateAsync(user, model.Password);
var selectedRole = roleManager.Roles.First(role => role.Id == model.UserRoleId).Name;
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, false);
var resultRoleAssign = await userManager.AddToRoleAsync(user, selectedRole);
if (resultRoleAssign.Succeeded)
{
await signInManager.SignInAsync(user, false);

return Redirect(vm.ReturnUrl);
return Redirect(model.ReturnUrl);
}
}
return View();

foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}

return View(model);
}
}
}
}
14 changes: 6 additions & 8 deletions OutOfSchool/IdentityServer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,20 @@ RUN apt-get update \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /OoS-Backend
COPY ./OutOfSchool/*.sln ./
COPY ./OutOfSchool/OutOfSchool.Tests/*.csproj ./OutOfSchool.Tests/
COPY ./OutOfSchool/OutOfSchool.WebApi/*.csproj ./OutOfSchool.WebApi/
COPY ./OutOfSchool/IdentityServer/*.csproj ./IdentityServer/
COPY ./OutOfSchool/OutOfSchool.DataAccess/*.csproj ./OutOfSchool.DataAccess/

RUN dotnet restore
RUN dotnet restore ./IdentityServer/OutOfSchool.IdentityServer.csproj

COPY ./OutOfSchool/ ./
COPY ./OutOfSchool/IdentityServer/ ./IdentityServer/
COPY ./OutOfSchool/OutOfSchool.DataAccess/ ./OutOfSchool.DataAccess/

WORKDIR /OoS-Backend
RUN dotnet build -c $Configuration -o /app

RUN dotnet build ./IdentityServer/OutOfSchool.IdentityServer.csproj -c $Configuration -o /app

FROM builder AS publish
ARG Configuration=Release
RUN dotnet publish IdentityServer -c $Configuration -o /app
RUN dotnet publish ./IdentityServer/OutOfSchool.IdentityServer.csproj -c $Configuration -o /app

FROM base AS final
WORKDIR /app
Expand Down
14 changes: 13 additions & 1 deletion OutOfSchool/IdentityServer/OutOfSchool.IdentityServer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,29 @@
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="5.0.2" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.8.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.321">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\OutOfSchool.DataAccess\OutOfSchool.DataAccess.csproj" />
<ProjectReference Include="..\OutOfSchool.WebApi\OutOfSchool.WebApi.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="Data\Migrations\OutOfSchoolMigrations\" />
</ItemGroup>

<ItemGroup>
<Content Remove="stylecop.json" />
<AdditionalFiles Include="stylecop.json">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</AdditionalFiles>
</ItemGroup>

<ProjectExtensions><VisualStudio><UserProperties appsettings_1development_1json__JsonSchema="https://json.schemastore.org/appsettings" appsettings_1release_1json__JsonSchema="https://json.schemastore.org/appsettings" /></VisualStudio></ProjectExtensions>

</Project>
25 changes: 22 additions & 3 deletions OutOfSchool/IdentityServer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using OutOfSchool.IdentityServer;
using OutOfSchool.IdentityServer.Data;
using OutOfSchool.Services;

namespace IdentityServer
Expand Down Expand Up @@ -41,6 +38,13 @@ public static void Main(string[] args)

context.Database.Migrate();
identityContext.Database.Migrate();
var manager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();

if (!manager.Roles.Any())
{
RolesInit(manager);
}

if (!context.Clients.Any())
{
foreach (var client in Config.Clients(clientSecret))
Expand Down Expand Up @@ -80,6 +84,21 @@ public static void Main(string[] args)
host.Run();
}

private static void RolesInit(RoleManager<IdentityRole> manager)
{
var roles = new IdentityRole[]
{
new IdentityRole {Name = "parent"},
new IdentityRole {Name = "organization"},
new IdentityRole {Name = "admin"}
};
foreach (var role in roles)
{
manager.CreateAsync(role).Wait();
}

}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
Expand Down
1 change: 1 addition & 0 deletions OutOfSchool/IdentityServer/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"IdentityServer": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "Auth/Login",
"applicationUrl": "http://localhost:5443;https://localhost:5444",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
Expand Down
10 changes: 8 additions & 2 deletions OutOfSchool/IdentityServer/ViewModels/RegisterViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Identity;

namespace IdentityServer.Controllers
{
Expand All @@ -10,7 +12,7 @@ public class RegisterViewModel
public string Username { get; set; }
[Required(ErrorMessage = "Password is required")]
[RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$",
ErrorMessage = "Password must contain at least one capital, number and symbol.")]
ErrorMessage = "Password must contain at least one capital, number and symbol(@$!%*?&).")]
[DataType(DataType.Password)]
public string Password { get; set; }
[Required(ErrorMessage = "Password confirmation is required")]
Expand All @@ -23,13 +25,17 @@ public class RegisterViewModel
[DataType(DataType.PhoneNumber)]
[Required(ErrorMessage = "Phone number is required")]
[RegularExpression(@"([0-9]{3})([-]?)([0-9]{3})([-]?)([0-9]{2})([-]?)([0-9]{2})",
ErrorMessage = "Phone number format is incorrect. Example: XXX-XXX-XX-XX")]
ErrorMessage = "Phone number format is incorrect. Example: +38XXX-XXX-XX-XX")]
public string PhoneNumber { get; set; }
[DataType(DataType.DateTime)]
public DateTime CreatingTime { get; set; }

[DataType(DataType.DateTime)]
public DateTime? LastLogin { get; set; }
public string ReturnUrl { get; set; }
[Required]
public string UserRoleId { get; set; }
public List<IdentityRole> AllRoles { get; set; }

}
}
Loading