diff --git a/.github/workflows/aspnetcore.yml b/.github/workflows/aspnetcore.yml
new file mode 100644
index 0000000..da8ca1e
--- /dev/null
+++ b/.github/workflows/aspnetcore.yml
@@ -0,0 +1,17 @@
+name: ASP.NET Core CI
+
+on: [push]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@master
+ - name: Setup .NET Core
+ uses: actions/setup-dotnet@v1
+ with:
+ version: 2.2.108
+ - name: Build
+ run: dotnet build ./src/Auth0.sln --configuration Release
diff --git a/Auth0.pptx b/Auth0.pptx
new file mode 100644
index 0000000..d0fea27
Binary files /dev/null and b/Auth0.pptx differ
diff --git a/README.md b/README.md
index 8103b39..c06b9a9 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,10 @@
-# Auth0-Coding-Project
+# Auth0 Coding Project
+
+
+ GitHub Actions status
+
+
+[](https://dev.azure.com/olegburov/Auth0/_build/latest?definitionId=10&branchName=master)
## Problem Overview
@@ -24,4 +30,101 @@ Build a sample application (SPA or Web Application) and a dummy service that dep
- Enable usage of `refresh_token` for mobile applications.
- Add your own custom rule (not from a template) that enriches the user profile.
-Please share the code on GitHub and make sure the README file is clean and clear so customers can understand it.
\ No newline at end of file
+Please share the code on GitHub and make sure the README file is clean and clear so customers can understand it.
+
+## Application flow diagram
+
+
+
+## Getting started
+
+1. Navigate at URL https://onegit-webapp.azurewebsites.net/.
+
+2. Log in using one of the following predefined users:
+
+ - User with the access right `Read Data`.
+
+ * Login: `viewer@olegburov.com`
+ * Password: `Viewer2User`
+
+ - User with the access right `Write Data`.
+
+ * Login: `editor@olegburov.com`
+ * Password: `Editor2User`
+
+ - User with the access right `Delete Data`.
+
+ * Login: `admin@olegburov.com`
+ * Password: `Admin2User`
+
+3. Depending on a user's access right, a security role is assigned with the following scope(s) for calling back-end Web API.
+
+ - The access right `Read Data`
+
+ * Role: `reader`
+ * Scope: `read:repositories`
+
+ - The access right `Write Data`
+
+ * Role: `editor`
+ * Scope: `create:repositories`, `read:repositories` and `update:repositories`
+
+ - The access right `Delete Data`
+
+ * Role: `admin`
+ * Scope: `create:repositories`, `read:repositories`, `update:repositories` and `delete:repositories`
+
+4. Based on a user's role, the front-end WebApp provides differente actions for a user:
+
+ - The role `reader`
+
+ * View existent repositories
+
+ - The role `editor`
+
+ * View existent repositories
+ * Create new repositories
+ * Edit existent repositories
+
+ - The role `admin`
+
+ * View existent repositories
+ * Create new repositories
+ * Edit existent repositories
+ * Delete existent repositories
+
+5. When a user perform granted actions, the front-end WebApp communicates with back-end WebAPI (https://onegit-webapi.azurewebsites.net) using an `access_token` to invoke these actions on behalf of a user.
+
+6. The `access_token` includes a granted scope(s) based on a user's role.
+
+7. The WebAPI provides the following endpoints where each requres a specific scope to be executed:
+
+ - The **List Repositories** operation returns a list of the repositories currently in a database.
+
+ `GET https://{base-url}/api/repositories`
+
+> The scope `read:repositories` is required for service requests.
+
+ - The **Get Repository** operation gets a repository from a database.
+
+ `GET https://{base-url}/api/repositories/{repository-guid}`
+
+> The scope `read:repositories` is required for service requests.
+
+ - The **Create Repository** operation creates a new repository in a database.
+
+ `POST https://{base-url}/api/repositories`
+
+> The scope `create:repositories` is required for service requests.
+
+ - The **Update Repository** operation updates an existent repository to the new one.
+
+ `PUT https://{base-url}/api/repositories/{repository-guid}`
+
+> The scope `update:repositories` is required for service requests.
+
+ - The **Delete Repository** operation removes an repository from database.
+
+ `DELETE https://{base-url}/api/repositories/{repository-guid}`
+
+> The scope `delete:repositories` is required for service requests.
diff --git a/Schema.png b/Schema.png
new file mode 100644
index 0000000..a243763
Binary files /dev/null and b/Schema.png differ
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
new file mode 100644
index 0000000..304d348
--- /dev/null
+++ b/azure-pipelines.yml
@@ -0,0 +1,21 @@
+# ASP.NET Core
+# Build and test ASP.NET Core projects targeting .NET Core.
+# Add steps that run tests, create a NuGet package, deploy, and more:
+# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core
+
+trigger:
+- master
+
+pool:
+ vmImage: 'ubuntu-latest'
+
+variables:
+ buildConfiguration: 'Release'
+
+steps:
+- task: DotNetCoreCLI@2
+ displayName: Build
+ inputs:
+ command: build
+ projects: '**/*.csproj'
+ arguments: '--configuration $(buildConfiguration)'
diff --git a/src/.dockerignore b/src/.dockerignore
new file mode 100644
index 0000000..df2e0fe
--- /dev/null
+++ b/src/.dockerignore
@@ -0,0 +1,9 @@
+.dockerignore
+.env
+.git
+.gitignore
+.vs
+.vscode
+*/bin
+*/obj
+**/.toolstarget
\ No newline at end of file
diff --git a/src/Auth0.sln b/src/Auth0.sln
index 1d2ba6d..f8113ec 100644
--- a/src/Auth0.sln
+++ b/src/Auth0.sln
@@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27428.2037
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneGit", "OneGit\OneGit.csproj", "{E70F2910-44D3-4A7A-BAB0-EEB46D063E62}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneGit.Web", "OneGit.Web\OneGit.Web.csproj", "{E70F2910-44D3-4A7A-BAB0-EEB46D063E62}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneGit.Api", "OneGit.Api\OneGit.Api.csproj", "{9C7AE68D-03F1-4D84-95D9-35887E2CF700}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -15,6 +17,10 @@ Global
{E70F2910-44D3-4A7A-BAB0-EEB46D063E62}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E70F2910-44D3-4A7A-BAB0-EEB46D063E62}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E70F2910-44D3-4A7A-BAB0-EEB46D063E62}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9C7AE68D-03F1-4D84-95D9-35887E2CF700}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9C7AE68D-03F1-4D84-95D9-35887E2CF700}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9C7AE68D-03F1-4D84-95D9-35887E2CF700}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9C7AE68D-03F1-4D84-95D9-35887E2CF700}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/OneGit.Api/Authorization/HasScopeHandler.cs b/src/OneGit.Api/Authorization/HasScopeHandler.cs
new file mode 100644
index 0000000..4363491
--- /dev/null
+++ b/src/OneGit.Api/Authorization/HasScopeHandler.cs
@@ -0,0 +1,29 @@
+using Microsoft.AspNetCore.Authorization;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OneGit.Api.Authorization
+{
+ public class HasScopeHandler : AuthorizationHandler
+ {
+ protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, HasScopeRequirement requirement)
+ {
+ // If user does not have the scope claim, get out of here
+ if (!context.User.HasClaim(claim => claim.Type.Equals("scope") && claim.Issuer == requirement.Issuer))
+ {
+ return Task.CompletedTask;
+ }
+
+ // Split the scopes string into an array
+ var scopes = context.User.FindFirst(claim => claim.Type.Equals("scope") && claim.Issuer == requirement.Issuer).Value.Split(' ');
+
+ // Succeed if the scope array contains the required scope
+ if (scopes.Any(s => s == requirement.Scope))
+ {
+ context.Succeed(requirement);
+ }
+
+ return Task.CompletedTask;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/OneGit.Api/Authorization/HasScopeRequirement.cs b/src/OneGit.Api/Authorization/HasScopeRequirement.cs
new file mode 100644
index 0000000..dff07b4
--- /dev/null
+++ b/src/OneGit.Api/Authorization/HasScopeRequirement.cs
@@ -0,0 +1,17 @@
+using Microsoft.AspNetCore.Authorization;
+using System;
+
+namespace OneGit.Api.Authorization
+{
+ public class HasScopeRequirement : IAuthorizationRequirement
+ {
+ public string Issuer { get; }
+ public string Scope { get; }
+
+ public HasScopeRequirement(string issuer, string scope)
+ {
+ Issuer = issuer ?? throw new ArgumentNullException(nameof(issuer), $"The parameter '{nameof(issuer)}' cannot be null.");
+ Scope = scope ?? throw new ArgumentNullException(nameof(scope), $"The parameter '{nameof(scope)}' cannot be null.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/OneGit.Api/Controllers/RepositoriesController.cs b/src/OneGit.Api/Controllers/RepositoriesController.cs
new file mode 100644
index 0000000..c2b4a1a
--- /dev/null
+++ b/src/OneGit.Api/Controllers/RepositoriesController.cs
@@ -0,0 +1,221 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+using OneGit.Api.Data;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace OneGit.Api.Controllers
+{
+ [Route("api/[controller]")]
+ [Produces("application/json")]
+ public class RepositoriesController : Controller
+ {
+ private ILogger logger;
+ private readonly RepositoryContext context;
+
+ public RepositoriesController(ILogger logger, RepositoryContext context)
+ {
+ this.logger = logger;
+ this.context = context;
+ }
+
+ ///
+ /// List all repositories.
+ ///
+ ///
+ /// Sample request:
+ ///
+ /// GET api/repositories
+ /// Content-Type: application/json
+ /// authorization: Bearer {your-access-token-here}
+ /// {
+ /// }
+ ///
+ ///
+ /// A list of the repositories currently stored in a database.
+ /// Returns the The specified repository.
+ /// If the header 'authorization' is not specified.
+ [HttpGet]
+ [Authorize("read:repositories")]
+ [ProducesResponseType(200)]
+ [ProducesResponseType(401)]
+ public async Task> Get()
+ {
+ return await this.context.Repositories.AsNoTracking().ToListAsync();
+ }
+
+ ///
+ /// Gets the specified repository.
+ ///
+ ///
+ /// Sample request:
+ ///
+ /// GET api/repositories/12345678-90ab-cdef-1234-567890abcdef
+ /// Content-Type: application/json
+ /// authorization: Bearer {your-access-token-here}
+ /// {
+ /// }
+ ///
+ ///
+ /// The repository's identifier.
+ /// The specified repository.
+ /// Returns the The specified repository.
+ /// If the header 'authorization' is not specified.
+ /// If the repository hasn't been found by its identifier.
+ [HttpGet("{id}", Name = "GetRepository")]
+ [Authorize("read:repositories")]
+ [ProducesResponseType(200)]
+ [ProducesResponseType(401)]
+ [ProducesResponseType(404)]
+ public IActionResult Get(Guid id)
+ {
+ var repository = this.context.Repositories.Find(id);
+ if (repository == null)
+ {
+ return NotFound();
+ }
+
+ return new ObjectResult(repository);
+ }
+
+ ///
+ /// Creates a new repository.
+ ///
+ ///
+ /// Sample request:
+ ///
+ /// POST api/repositories
+ /// Content-Type: application/json
+ /// authorization: Bearer {your-access-token-here}
+ /// {
+ /// "name": ".NET Standard",
+ /// "description": "This repo is building the .NET Standard",
+ /// "url": "https://github.com/dotnet/standard"
+ /// }
+ ///
+ ///
+ /// The new repository.
+ /// A newly created repository.
+ /// Returns the newly created repository.
+ /// If the item is null.
+ /// If the header 'authorization' is not specified.
+ [HttpPost]
+ [Authorize("create:repositories")]
+ [ProducesResponseType(201)]
+ [ProducesResponseType(400)]
+ [ProducesResponseType(401)]
+ public IActionResult Post([FromBody] RepositoryModel repository)
+ {
+ if (repository == null)
+ {
+ return BadRequest();
+ }
+
+ try
+ {
+ this.context.Repositories.Add(repository);
+ this.context.SaveChanges();
+ }
+ catch (Exception ex)
+ {
+ this.logger.LogError($"An error occurred while creating entity '{nameof(RepositoryModel)}': {ex.ToString()}");
+ }
+
+ return CreatedAtRoute("GetRepository", new { id = repository.Id }, repository);
+ }
+
+ ///
+ /// Updates the specified repository.
+ ///
+ ///
+ /// Sample request:
+ ///
+ /// PUT api/repositories/12345678-90ab-cdef-1234-567890abcdef
+ /// Content-Type: application/json
+ /// authorization: Bearer {your-access-token-here}
+ /// {
+ /// "name": "SignalR,
+ /// "description": "Incredibly simple real-time web for ASP.NET Core",
+ /// "url": "https://github.com/aspnet/SignalR"
+ /// }
+ ///
+ ///
+ /// The repository's identifier.
+ /// The updated repository.
+ /// No content.
+ /// If the specified repository has been sucessfully deleted.
+ /// If the item is null.
+ /// If the header 'authorization' is not specified.
+ [HttpPut("{id}")]
+ [Authorize("update:repositories")]
+ [ProducesResponseType(204)]
+ [ProducesResponseType(400)]
+ [ProducesResponseType(401)]
+ public async Task Put(Guid id, [FromBody] RepositoryModel repository)
+ {
+ if (repository == null || repository.Id != id)
+ {
+ return BadRequest();
+ }
+
+ try
+ {
+ this.context.Attach(repository).State = EntityState.Modified;
+ await this.context.SaveChangesAsync();
+ }
+ catch (Exception ex)
+ {
+ this.logger.LogError($"An error occurred while updating the entity '{nameof(RepositoryModel)}' with id {id}: {ex.ToString()}");
+ }
+
+ return new NoContentResult();
+ }
+
+ ///
+ /// Deletes the specified repository.
+ ///
+ ///
+ /// Sample request:
+ ///
+ /// DELETE api/repositories/12345678-90ab-cdef-1234-567890abcdef
+ /// Content-Type: application/json
+ /// authorization: Bearer {your-access-token-here}
+ /// {
+ /// }
+ ///
+ ///
+ /// The repository's identifier.
+ /// No content.
+ /// If the specified repository has been sucessfully deleted.
+ /// If the header 'authorization' is not specified.
+ /// If the repository hasn't been found by its identifier.
+ [HttpDelete("{id}")]
+ [Authorize("delete:repositories")]
+ [ProducesResponseType(204)]
+ [ProducesResponseType(401)]
+ [ProducesResponseType(404)]
+ public async Task Delete(Guid id)
+ {
+ var repository = await this.context.Repositories.FindAsync(id);
+ if (repository == null)
+ {
+ return NotFound();
+ }
+
+ try
+ {
+ this.context.Repositories.Remove(repository);
+ await this.context.SaveChangesAsync();
+ }
+ catch (Exception ex)
+ {
+ this.logger.LogError($"An error occurred while deleting the entity '{nameof(RepositoryModel)}' with id '{id}': {ex.ToString()}");
+ }
+
+ return new NoContentResult();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/OneGit/Data/Migrations/20180420161436_Initial.Designer.cs b/src/OneGit.Api/Data/Migrations/20180420161436_Initial.Designer.cs
similarity index 93%
rename from src/OneGit/Data/Migrations/20180420161436_Initial.Designer.cs
rename to src/OneGit.Api/Data/Migrations/20180420161436_Initial.Designer.cs
index a0fad5b..d5aa948 100644
--- a/src/OneGit/Data/Migrations/20180420161436_Initial.Designer.cs
+++ b/src/OneGit.Api/Data/Migrations/20180420161436_Initial.Designer.cs
@@ -1,5 +1,5 @@
//
-using OneGit.Data;
+using OneGit.Api.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
@@ -8,9 +8,9 @@
using Microsoft.EntityFrameworkCore.Storage.Internal;
using System;
-namespace OneGit.Migrations
+namespace OneGit.Api.Migrations
{
- [DbContext(typeof(AppDbContext))]
+ [DbContext(typeof(RepositoryContext))]
[Migration("20180420161436_Initial")]
partial class Initial
{
diff --git a/src/OneGit/Data/Migrations/20180420161436_Initial.cs b/src/OneGit.Api/Data/Migrations/20180420161436_Initial.cs
similarity index 90%
rename from src/OneGit/Data/Migrations/20180420161436_Initial.cs
rename to src/OneGit.Api/Data/Migrations/20180420161436_Initial.cs
index 7c58328..ac71fc3 100644
--- a/src/OneGit/Data/Migrations/20180420161436_Initial.cs
+++ b/src/OneGit.Api/Data/Migrations/20180420161436_Initial.cs
@@ -2,7 +2,7 @@
using System;
using System.Collections.Generic;
-namespace OneGit.Migrations
+namespace OneGit.Api.Migrations
{
public partial class Initial : Migration
{
@@ -12,14 +12,14 @@ protected override void Up(MigrationBuilder migrationBuilder)
name: "Repositories",
columns: table => new
{
- ID = table.Column(nullable: false),
+ Id = table.Column(nullable: false),
Description = table.Column(maxLength: 256, nullable: true),
Name = table.Column(maxLength: 50, nullable: false),
Url = table.Column(maxLength: 100, nullable: true)
},
constraints: table =>
{
- table.PrimaryKey("PK_Repositories", x => x.ID);
+ table.PrimaryKey("PK_Repositories", x => x.Id);
});
}
diff --git a/src/OneGit/Data/Migrations/AppDbContextModelSnapshot.cs b/src/OneGit.Api/Data/Migrations/AppDbContextModelSnapshot.cs
similarity index 87%
rename from src/OneGit/Data/Migrations/AppDbContextModelSnapshot.cs
rename to src/OneGit.Api/Data/Migrations/AppDbContextModelSnapshot.cs
index 1900a05..0449646 100644
--- a/src/OneGit/Data/Migrations/AppDbContextModelSnapshot.cs
+++ b/src/OneGit.Api/Data/Migrations/AppDbContextModelSnapshot.cs
@@ -1,5 +1,5 @@
//
-using OneGit.Data;
+using OneGit.Api.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
@@ -8,9 +8,9 @@
using Microsoft.EntityFrameworkCore.Storage.Internal;
using System;
-namespace OneGit.Migrations
+namespace OneGit.Api.Migrations
{
- [DbContext(typeof(AppDbContext))]
+ [DbContext(typeof(RepositoryContext))]
partial class AppDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
@@ -22,7 +22,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
modelBuilder.Entity("Auth0.RepositoryModel", b =>
{
- b.Property("ID")
+ b.Property("Id")
.ValueGeneratedOnAdd();
b.Property("Description")
@@ -35,7 +35,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("Url")
.HasMaxLength(100);
- b.HasKey("ID");
+ b.HasKey("Id");
b.ToTable("Repositories");
});
diff --git a/src/OneGit.Api/Data/RepositoryDbContext.cs b/src/OneGit.Api/Data/RepositoryDbContext.cs
new file mode 100644
index 0000000..0a5562b
--- /dev/null
+++ b/src/OneGit.Api/Data/RepositoryDbContext.cs
@@ -0,0 +1,13 @@
+using Microsoft.EntityFrameworkCore;
+
+namespace OneGit.Api.Data
+{
+ public class RepositoryContext : DbContext
+ {
+ public RepositoryContext(DbContextOptions options) : base(options)
+ {
+ }
+
+ public DbSet Repositories { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/OneGit.Api/Dockerfile b/src/OneGit.Api/Dockerfile
new file mode 100644
index 0000000..94ff5e8
--- /dev/null
+++ b/src/OneGit.Api/Dockerfile
@@ -0,0 +1,20 @@
+FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
+WORKDIR /app
+EXPOSE 55836
+EXPOSE 44359
+
+FROM microsoft/dotnet:2.1-sdk AS build
+WORKDIR /src
+COPY OneGit.Api/OneGit.Api.csproj OneGit.Api/
+RUN dotnet restore OneGit.Api/OneGit.Api.csproj
+COPY . .
+WORKDIR /src/OneGit.Api
+RUN dotnet build OneGit.Api.csproj -c Release -o /app
+
+FROM build AS publish
+RUN dotnet publish OneGit.Api.csproj -c Release -o /app
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app .
+ENTRYPOINT ["dotnet", "OneGit.Api.dll"]
diff --git a/src/OneGit.Api/Models/RepositoryModel.cs b/src/OneGit.Api/Models/RepositoryModel.cs
new file mode 100644
index 0000000..613dbfe
--- /dev/null
+++ b/src/OneGit.Api/Models/RepositoryModel.cs
@@ -0,0 +1,20 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace OneGit.Api
+{
+ public class RepositoryModel
+ {
+ [Key]
+ public Guid Id { get; set; }
+
+ [Required, StringLength(50)]
+ public string Name { get; set; }
+
+ [StringLength(256)]
+ public string Description { get; set; }
+
+ [Required, StringLength(100)]
+ public string Url { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/OneGit.Api/OneGit.Api.csproj b/src/OneGit.Api/OneGit.Api.csproj
new file mode 100644
index 0000000..927bc49
--- /dev/null
+++ b/src/OneGit.Api/OneGit.Api.csproj
@@ -0,0 +1,31 @@
+
+
+
+ netcoreapp2.2
+ bfe4ce45-dc32-44de-8b70-a210c0e19266
+ Linux
+
+
+
+ bin\Debug\netcoreapp2.1\OneGit.Api.xml
+
+
+
+ bin\Release\netcoreapp2.1\OneGit.Api.xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/OneGit/Program.cs b/src/OneGit.Api/Program.cs
similarity index 53%
rename from src/OneGit/Program.cs
rename to src/OneGit.Api/Program.cs
index 22cf003..0117619 100644
--- a/src/OneGit/Program.cs
+++ b/src/OneGit.Api/Program.cs
@@ -1,18 +1,17 @@
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
-namespace OneGit
+namespace OneGit.Api
{
public class Program
{
public static void Main(string[] args)
{
- BuildWebHost(args).Run();
+ CreateWebHostBuilder(args).Build().Run();
}
- public static IWebHost BuildWebHost(string[] args) =>
+ public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
- .UseStartup()
- .Build();
+ .UseStartup();
}
}
\ No newline at end of file
diff --git a/src/OneGit.Api/Startup.cs b/src/OneGit.Api/Startup.cs
new file mode 100644
index 0000000..c2237e4
--- /dev/null
+++ b/src/OneGit.Api/Startup.cs
@@ -0,0 +1,127 @@
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using OneGit.Api.Authorization;
+using OneGit.Api.Data;
+using Swashbuckle.AspNetCore.Swagger;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+
+namespace OneGit.Api
+{
+ public class Startup
+ {
+ public Startup(IConfiguration configuration)
+ {
+ this.Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddDbContext(options =>
+ options.UseSqlServer(this.Configuration.GetConnectionString("DefaultConnection")));
+
+ services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
+
+ // 1. Add Authentication Services
+ string domain = $"https://{this.Configuration["Auth0:Domain"]}/";
+ services.AddAuthentication(options =>
+ {
+ options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
+ options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
+ })
+ .AddJwtBearer(options =>
+ {
+ options.Authority = domain;
+ options.Audience = this.Configuration["Auth0:ApiIdentifier"];
+ });
+
+ services.AddAuthorization(options =>
+ {
+ options.AddPolicy("read:repositories", policy => policy.Requirements.Add(new HasScopeRequirement(domain, "read:repositories")));
+ options.AddPolicy("create:repositories", policy => policy.Requirements.Add(new HasScopeRequirement(domain, "create:repositories")));
+ options.AddPolicy("update:repositories", policy => policy.Requirements.Add(new HasScopeRequirement(domain, "update:repositories")));
+ options.AddPolicy("delete:repositories", policy => policy.Requirements.Add(new HasScopeRequirement(domain, "delete:repositories")));
+ });
+
+ // register the scope authorization handler
+ services.AddSingleton();
+
+ services.AddSwaggerGen(c =>
+ {
+ c.SwaggerDoc("v1", new Info
+ {
+ Version = "v1",
+ Title = "Repositories API",
+ Description = "A simple example ASP.NET Core Web API",
+ TermsOfService = "None",
+ Contact = new Contact
+ {
+ Name = "Oleg Burov",
+ Email = "oleg.burov@outlook.com",
+ Url = "https://twitter.com/oleg_burov"
+ },
+ License = new License
+ {
+ Name = "MIT",
+ Url = "https://github.com/olegburov/Auth0/blob/master/LICENSE"
+ }
+ });
+
+ c.AddSecurityDefinition("JWT Bearer", new ApiKeyScheme
+ {
+ Description = @"Auth0 Access Token in the header 'Authorization' of HTTP request as a Bearer token.
+ Example: 'Authorization: Bearer {Your-Auth0-access_token-here}'
",
+ Name = "Authorization",
+ In = "header",
+ Type = "apiKey"
+ });
+
+ c.AddSecurityRequirement(new Dictionary>
+ {
+ {"JWT Bearer", new string[] { }},
+ });
+
+ // Set the comments path for the Swagger JSON and UI.
+ var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
+ var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
+ c.IncludeXmlComments(xmlPath);
+ });
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+ else
+ {
+ app.UseHsts();
+ }
+
+ app.UseAuthentication();
+
+ app.UseSwagger();
+ app.UseSwaggerUI(c =>
+ {
+ c.SwaggerEndpoint("/swagger/v1/swagger.json", "Repositories API V1");
+ c.RoutePrefix = string.Empty;
+ });
+
+ app.UseHttpsRedirection();
+ app.UseMvc();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/OneGit.Api/appsettings.Development.json b/src/OneGit.Api/appsettings.Development.json
new file mode 100644
index 0000000..ea10311
--- /dev/null
+++ b/src/OneGit.Api/appsettings.Development.json
@@ -0,0 +1,19 @@
+{
+ "Logging": {
+ "IncludeScopes": false,
+ "LogLevel": {
+ "Default": "Debug",
+ "System": "Information",
+ "Microsoft": "Information"
+ }
+ },
+
+ "Auth0": {
+ "Domain": "{your-domain-here}",
+ "ApiIdentifier": "{your-api-identifier-here}"
+ },
+
+ "ConnectionStrings": {
+ "DefaultConnection": "Server=.;Database=OneGit;Trusted_Connection=True;MultipleActiveResultSets=true"
+ }
+}
diff --git a/src/OneGit.Api/appsettings.json b/src/OneGit.Api/appsettings.json
new file mode 100644
index 0000000..99b2bb8
--- /dev/null
+++ b/src/OneGit.Api/appsettings.json
@@ -0,0 +1,24 @@
+{
+ "Logging": {
+ "IncludeScopes": false,
+ "Debug": {
+ "LogLevel": {
+ "Default": "Warning"
+ }
+ },
+ "Console": {
+ "LogLevel": {
+ "Default": "Warning"
+ }
+ }
+ },
+
+ "Auth0": {
+ "Domain": "{your-domain-here}",
+ "ApiIdentifier": "{your-api-identifier-here}"
+ },
+
+ "ConnectionStrings": {
+ "DefaultConnection": "Server=.;Database=OneGit;Trusted_Connection=True;MultipleActiveResultSets=true"
+ }
+}
\ No newline at end of file
diff --git a/src/OneGit/Controllers/AccountController.cs b/src/OneGit.Web/Controllers/AccountController.cs
similarity index 84%
rename from src/OneGit/Controllers/AccountController.cs
rename to src/OneGit.Web/Controllers/AccountController.cs
index dba2d4b..0a232a0 100644
--- a/src/OneGit/Controllers/AccountController.cs
+++ b/src/OneGit.Web/Controllers/AccountController.cs
@@ -2,11 +2,11 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-using OneGit.Models;
+using OneGit.Web.Models;
using System.Linq;
using System.Threading.Tasks;
-namespace OneGit.Controllers
+namespace OneGit.Web.Controllers
{
[Route("[controller]/[action]")]
public class AccountController : Controller
@@ -37,13 +37,15 @@ public async Task Signout()
[HttpGet]
[Authorize]
- public IActionResult Profile()
+ public async Task Profile()
{
return View(new UserProfileModel()
{
Name = User.Identity.Name,
EmailAddress = User.Claims.FirstOrDefault(c => c.Type == "name")?.Value,
- ProfileImage = User.Claims.FirstOrDefault(c => c.Type == "picture")?.Value
+ ProfileImage = User.Claims.FirstOrDefault(c => c.Type == "picture")?.Value,
+ IdToken = await HttpContext.GetTokenAsync("id_token"),
+ AccessToken = await HttpContext.GetTokenAsync("access_token")
});
}
diff --git a/src/OneGit/Controllers/HomeController.cs b/src/OneGit.Web/Controllers/HomeController.cs
similarity index 56%
rename from src/OneGit/Controllers/HomeController.cs
rename to src/OneGit.Web/Controllers/HomeController.cs
index 7f99ce4..b2a4932 100644
--- a/src/OneGit/Controllers/HomeController.cs
+++ b/src/OneGit.Web/Controllers/HomeController.cs
@@ -1,28 +1,33 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.EntityFrameworkCore;
-using OneGit.Data;
-using OneGit.Models;
+using OneGit.Web.Models;
+using OneGit.Web.Services;
using System;
using System.Diagnostics;
+using System.Linq;
using System.Threading.Tasks;
-
-namespace OneGit.Controllers
+namespace OneGit.Web.Controllers
{
public class HomeController : Controller
{
- private readonly AppDbContext appContext;
+ private RepositoryClient repositoryClient;
- public HomeController(AppDbContext context)
+ public HomeController(RepositoryClient client)
{
- this.appContext = context;
+ this.repositoryClient = client;
}
public async Task Index()
{
- var repositoriesList = await this.appContext.Repositories.AsNoTracking().ToListAsync();
- return View(repositoriesList);
+ var repositories = Enumerable.Empty();
+
+ if (User.Identity.IsAuthenticated)
+ {
+ repositories = await this.repositoryClient.GetAllRepositoriesAsync();
+ }
+
+ return View(repositories);
}
[HttpGet]
@@ -41,17 +46,7 @@ public async Task New(RepositoryModel repository)
return View();
}
- this.appContext.Add(repository);
-
- try
- {
- await this.appContext.SaveChangesAsync();
- }
- catch (Exception ex)
- {
- ViewData["Alert"] = ex.Message;
- return View();
- }
+ await this.repositoryClient.CreateNewRepositoryAsync(repository);
return RedirectToAction("Index");
}
@@ -60,13 +55,13 @@ public async Task New(RepositoryModel repository)
[Authorize(Roles = "admin, editor")]
public async Task Edit(Guid id)
{
- var repository = await this.appContext.Repositories.FindAsync(id);
+ var repository = await this.repositoryClient.GetRepositoryAsync(id);
if (repository == null)
{
return RedirectToAction("Index");
}
-
+
return View(repository);
}
@@ -79,16 +74,7 @@ public async Task Edit(RepositoryModel repository)
return View();
}
- try
- {
- this.appContext.Attach(repository).State = EntityState.Modified;
- await this.appContext.SaveChangesAsync();
- }
- catch (Exception ex)
- {
- ViewData["Alert"] = ex.Message;
- return View();
- }
+ await this.repositoryClient.UpdateRepositoryAsync(repository);
return RedirectToAction("Index");
}
@@ -96,13 +82,7 @@ public async Task Edit(RepositoryModel repository)
[Authorize(Roles = "admin")]
public async Task Delete(Guid id)
{
- var repository = await this.appContext.Repositories.FindAsync(id);
-
- if (repository != null)
- {
- this.appContext.Remove(repository);
- await this.appContext.SaveChangesAsync();
- }
+ await this.repositoryClient.DeleteRepositoryAsync(id);
return RedirectToAction("Index");
}
diff --git a/src/OneGit.Web/Dockerfile b/src/OneGit.Web/Dockerfile
new file mode 100644
index 0000000..6056a7b
--- /dev/null
+++ b/src/OneGit.Web/Dockerfile
@@ -0,0 +1,20 @@
+FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
+WORKDIR /app
+EXPOSE 43163
+EXPOSE 44399
+
+FROM microsoft/dotnet:2.1-sdk AS build
+WORKDIR /src
+COPY OneGit.Web/OneGit.Web.csproj OneGit.Web/
+RUN dotnet restore OneGit.Web/OneGit.Web.csproj
+COPY . .
+WORKDIR /src/OneGit.Web
+RUN dotnet build OneGit.Web.csproj -c Release -o /app
+
+FROM build AS publish
+RUN dotnet publish OneGit.Web.csproj -c Release -o /app
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app .
+ENTRYPOINT ["dotnet", "OneGit.Web.dll"]
diff --git a/src/OneGit/Models/ErrorViewModel.cs b/src/OneGit.Web/Models/ErrorViewModel.cs
similarity index 84%
rename from src/OneGit/Models/ErrorViewModel.cs
rename to src/OneGit.Web/Models/ErrorViewModel.cs
index e7a1696..1847b8d 100644
--- a/src/OneGit/Models/ErrorViewModel.cs
+++ b/src/OneGit.Web/Models/ErrorViewModel.cs
@@ -1,4 +1,4 @@
-namespace OneGit.Models
+namespace OneGit.Web.Models
{
public class ErrorViewModel
{
diff --git a/src/OneGit/Models/RepositoryModel.cs b/src/OneGit.Web/Models/RepositoryModel.cs
similarity index 85%
rename from src/OneGit/Models/RepositoryModel.cs
rename to src/OneGit.Web/Models/RepositoryModel.cs
index a2574ba..159e33c 100644
--- a/src/OneGit/Models/RepositoryModel.cs
+++ b/src/OneGit.Web/Models/RepositoryModel.cs
@@ -1,11 +1,11 @@
using System;
using System.ComponentModel.DataAnnotations;
-namespace OneGit
+namespace OneGit.Web
{
public class RepositoryModel
{
- public Guid ID { get; set; }
+ public Guid Id { get; set; }
[Required, StringLength(50)]
public string Name { get; set; }
diff --git a/src/OneGit/Models/UserProfileModel.cs b/src/OneGit.Web/Models/UserProfileModel.cs
similarity index 59%
rename from src/OneGit/Models/UserProfileModel.cs
rename to src/OneGit.Web/Models/UserProfileModel.cs
index e733108..6135af5 100644
--- a/src/OneGit/Models/UserProfileModel.cs
+++ b/src/OneGit.Web/Models/UserProfileModel.cs
@@ -1,4 +1,4 @@
-namespace OneGit.Models
+namespace OneGit.Web.Models
{
public class UserProfileModel
{
@@ -7,5 +7,9 @@ public class UserProfileModel
public string Name { get; set; }
public string ProfileImage { get; set; }
+
+ public string IdToken { get; set; }
+
+ public string AccessToken { get; set; }
}
}
\ No newline at end of file
diff --git a/src/OneGit.Web/OneGit.Web.csproj b/src/OneGit.Web/OneGit.Web.csproj
new file mode 100644
index 0000000..2910fb5
--- /dev/null
+++ b/src/OneGit.Web/OneGit.Web.csproj
@@ -0,0 +1,18 @@
+
+
+
+ netcoreapp2.2
+ fc544d68-3a59-46ef-bb96-c511696b3ce3
+ Linux
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/OneGit.Web/Program.cs b/src/OneGit.Web/Program.cs
new file mode 100644
index 0000000..24aaa86
--- /dev/null
+++ b/src/OneGit.Web/Program.cs
@@ -0,0 +1,17 @@
+using Microsoft.AspNetCore;
+using Microsoft.AspNetCore.Hosting;
+
+namespace OneGit.Web
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ CreateWebHostBuilder(args).Build().Run();
+ }
+
+ public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
+ WebHost.CreateDefaultBuilder(args)
+ .UseStartup();
+ }
+}
\ No newline at end of file
diff --git a/src/OneGit.Web/Services/RepositoryClient.cs b/src/OneGit.Web/Services/RepositoryClient.cs
new file mode 100644
index 0000000..f40c8d8
--- /dev/null
+++ b/src/OneGit.Web/Services/RepositoryClient.cs
@@ -0,0 +1,114 @@
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Threading.Tasks;
+
+namespace OneGit.Web.Services
+{
+ public class RepositoryClient
+ {
+ private HttpClient client;
+ private ILogger logger;
+ private IHttpContextAccessor httpContextAccessor;
+
+ public RepositoryClient(HttpClient client, ILogger logger, IHttpContextAccessor httpContextAccessor)
+ {
+ this.client = client;
+ this.logger = logger;
+ this.httpContextAccessor = httpContextAccessor;
+
+ var context = this.httpContextAccessor.HttpContext;
+ var token = context.GetTokenAsync("access_token").Result;
+
+ if (token != null)
+ {
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
+ }
+ }
+
+ public async Task> GetAllRepositoriesAsync()
+ {
+ try
+ {
+ var response = await client.GetAsync("api/repositories");
+ response.EnsureSuccessStatusCode();
+
+ return await response.Content.ReadAsAsync>();
+ }
+ catch (HttpRequestException ex)
+ {
+ logger.LogError($"An error occured connecting to values API {ex.ToString()}");
+ return Enumerable.Empty();
+ }
+ }
+
+ public async Task GetRepositoryAsync(Guid id)
+ {
+ try
+ {
+ var response = await client.GetAsync($"api/repositories/{id}");
+ response.EnsureSuccessStatusCode();
+
+ return await response.Content.ReadAsAsync();
+ }
+ catch (HttpRequestException ex)
+ {
+ logger.LogError($"An error occured connecting to values API {ex.ToString()}");
+ return null;
+ }
+ }
+
+ public async Task CreateNewRepositoryAsync(RepositoryModel repository)
+ {
+ try
+ {
+ var response = await client.PostAsJsonAsync("api/repositories", repository);
+ response.EnsureSuccessStatusCode();
+
+ return;
+ }
+ catch (HttpRequestException ex)
+ {
+ logger.LogError($"An error occured connecting to values API {ex.ToString()}");
+ return;
+ }
+ }
+
+ public async Task UpdateRepositoryAsync(RepositoryModel repository)
+ {
+ try
+ {
+ var response = await client.PutAsJsonAsync($"api/repositories/{repository.Id}", repository);
+ response.EnsureSuccessStatusCode();
+
+ return;
+ }
+ catch (HttpRequestException ex)
+ {
+ logger.LogError($"An error occured connecting to values API {ex.ToString()}");
+ return;
+ }
+ }
+
+ public async Task DeleteRepositoryAsync(Guid id)
+ {
+ try
+ {
+ var response = await client.DeleteAsync($"api/repositories/{id}");
+ response.EnsureSuccessStatusCode();
+
+ return;
+ }
+ catch (HttpRequestException ex)
+ {
+ logger.LogError($"An error occured connecting to values API {ex.ToString()}");
+ return;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/OneGit/Startup.cs b/src/OneGit.Web/Startup.cs
similarity index 67%
rename from src/OneGit/Startup.cs
rename to src/OneGit.Web/Startup.cs
index 9aaa110..8e330f9 100644
--- a/src/OneGit/Startup.cs
+++ b/src/OneGit.Web/Startup.cs
@@ -1,23 +1,24 @@
-using OneGit.Data;
-using Microsoft.AspNetCore.Authentication.Cookies;
+using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
-using Microsoft.EntityFrameworkCore;
+using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
+using OneGit.Web.Services;
using System;
+using System.Linq;
using System.Threading.Tasks;
-namespace OneGit
+namespace OneGit.Web
{
public class Startup
{
public Startup(IConfiguration configuration)
{
- Configuration = configuration;
+ this.Configuration = configuration;
}
public IConfiguration Configuration { get; }
@@ -25,25 +26,29 @@ public Startup(IConfiguration configuration)
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
- services.AddDbContext(options =>
- options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
+ services.Configure(options =>
+ {
+ // This lambda determines whether user consent for non-essential cookies is needed for a given request.
+ options.CheckConsentNeeded = context => true;
+ options.MinimumSameSitePolicy = SameSiteMode.None;
+ });
// Add authentication services
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
- options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
+ options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options => options.LoginPath = "/Account/Signin")
.AddOpenIdConnect("Auth0", options =>
- {
+ {
// Set the authority to your Auth0 domain
- options.Authority = $"https://{Configuration["Auth0:Domain"]}";
+ options.Authority = $"https://{this.Configuration["Auth0:Domain"]}";
// Configure the Auth0 Client ID and Client Secret
- options.ClientId = Configuration["Auth0:ClientId"];
- options.ClientSecret = Configuration["Auth0:ClientSecret"];
+ options.ClientId = this.Configuration["Auth0:ClientId"];
+ options.ClientSecret = this.Configuration["Auth0:ClientSecret"];
// Set response type to code
options.ResponseType = "code";
@@ -54,17 +59,20 @@ public void ConfigureServices(IServiceCollection services)
options.Scope.Add("profile");
options.Scope.Add("email");
+ var apiScopes = string.Join(" ", this.Configuration.GetSection("Auth0:ApiScopes").GetChildren().Select(s => s.Value));
+ options.Scope.Add(apiScopes);
+
// Set the correct name claim type
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "nickname",
- RoleClaimType = "https://schemas.quickstarts.com/roles"
+ RoleClaimType = "https://olegburov.com/roles"
};
// Set the callback path, so Auth0 will call back to http://localhost:5000/signin-auth0
// Also ensure that you have added the URL as an Allowed Callback URL in your Auth0 dashboard
options.CallbackPath = new PathString("/signin-auth0");
-
+
// Configure the Claims Issuer to be Auth0
options.ClaimsIssuer = "Auth0";
@@ -76,7 +84,7 @@ public void ConfigureServices(IServiceCollection services)
// handle the logout redirection
OnRedirectToIdentityProviderForSignOut = (context) =>
{
- var logoutUri = $"https://{Configuration["Auth0:Domain"]}/v2/logout?client_id={Configuration["Auth0:ClientId"]}";
+ var logoutUri = $"https://{this.Configuration["Auth0:Domain"]}/v2/logout?client_id={this.Configuration["Auth0:ClientId"]}";
var postLogoutUri = context.Properties.RedirectUri;
if (!string.IsNullOrEmpty(postLogoutUri))
@@ -95,12 +103,23 @@ public void ConfigureServices(IServiceCollection services)
context.HandleResponse();
return Task.CompletedTask;
- }
+ },
+
+ OnRedirectToIdentityProvider = context =>
+ {
+ context.ProtocolMessage.SetParameter("audience", this.Configuration["Auth0:ApiIdentifier"]);
+
+ return Task.FromResult(0);
+ }
};
});
// Add framework services.
- services.AddMvc();
+ services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
+
+ services.AddSingleton();
+
+ services.AddHttpClient(client => client.BaseAddress = new Uri(this.Configuration["Auth0:ApiBaseUrl"]));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@@ -108,15 +127,17 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
- app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
+ app.UseHsts();
}
+ app.UseHttpsRedirection();
app.UseStaticFiles();
+ app.UseCookiePolicy();
app.UseAuthentication();
@@ -128,4 +149,4 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
});
}
}
-}
+}
\ No newline at end of file
diff --git a/src/OneGit/Views/Account/AccessDenied.cshtml b/src/OneGit.Web/Views/Account/AccessDenied.cshtml
similarity index 95%
rename from src/OneGit/Views/Account/AccessDenied.cshtml
rename to src/OneGit.Web/Views/Account/AccessDenied.cshtml
index e626eef..57b0987 100644
--- a/src/OneGit/Views/Account/AccessDenied.cshtml
+++ b/src/OneGit.Web/Views/Account/AccessDenied.cshtml
@@ -1,5 +1,4 @@
-
-@{
+@{
ViewData["Title"] = "Access Denied";
}
diff --git a/src/OneGit.Web/Views/Account/Profile.cshtml b/src/OneGit.Web/Views/Account/Profile.cshtml
new file mode 100644
index 0000000..b76b370
--- /dev/null
+++ b/src/OneGit.Web/Views/Account/Profile.cshtml
@@ -0,0 +1,24 @@
+@model OneGit.Web.Models.UserProfileModel
+@{
+ ViewData["Title"] = "Profile";
+}
+
+@ViewData["Title"]
+
+@Model.Name
+@Model.Name
+
+ @Model.EmailAddress
+
+
+ id_token
+
+ @Model.IdToken
+
+
+
+ access_token
+
+ @Model.AccessToken
+
+
\ No newline at end of file
diff --git a/src/OneGit/Views/Home/Edit.cshtml b/src/OneGit.Web/Views/Home/Edit.cshtml
similarity index 94%
rename from src/OneGit/Views/Home/Edit.cshtml
rename to src/OneGit.Web/Views/Home/Edit.cshtml
index e180497..4483b94 100644
--- a/src/OneGit/Views/Home/Edit.cshtml
+++ b/src/OneGit.Web/Views/Home/Edit.cshtml
@@ -1,5 +1,4 @@
-@model OneGit.RepositoryModel
-
+@model OneGit.Web.RepositoryModel
@{
ViewData["Title"] = "Edit";
}
@@ -20,7 +19,7 @@