mirror of https://github.com/abpframework/abp.git
7 changed files with 342 additions and 2 deletions
@ -0,0 +1,38 @@ |
|||
@using Volo.Abp.Account.Web.Pages |
|||
@using Volo.Abp.Account.Web.Pages.Account |
|||
@model Volo.Abp.Account.Web.Pages.ConsentModel.ScopeViewModel |
|||
|
|||
@* TODO: Should re-format this, just made copy/paste *@ |
|||
|
|||
<li class="list-group-item"> |
|||
<label> |
|||
<input class="consent-scopecheck" |
|||
type="checkbox" |
|||
name="ScopesConsented" |
|||
id="scopes_@Model.Name" |
|||
value="@Model.Name" |
|||
checked="@Model.Checked" |
|||
disabled="@Model.Required" /> |
|||
@if (Model.Required) |
|||
{ |
|||
<input type="hidden" |
|||
name="ScopesConsented" |
|||
value="@Model.Name" /> |
|||
} |
|||
<strong>@Model.DisplayName</strong> |
|||
@if (Model.Emphasize) |
|||
{ |
|||
<span class="glyphicon glyphicon-exclamation-sign"></span> |
|||
} |
|||
</label> |
|||
@if (Model.Required) |
|||
{ |
|||
<span><em>(required)</em></span> |
|||
} |
|||
@if (Model.Description != null) |
|||
{ |
|||
<div class="consent-description"> |
|||
<label for="scopes_@Model.Name">@Model.Description</label> |
|||
</div> |
|||
} |
|||
</li> |
|||
@ -0,0 +1,76 @@ |
|||
@page |
|||
@using Volo.Abp.Account.Web.Pages |
|||
@using Volo.Abp.Account.Web.Pages.Account |
|||
@model ConsentModel |
|||
<abp-card id="IdentityServerConsentWrapper"> |
|||
<abp-card-header> |
|||
<div class="row"> |
|||
<div class="col-md-12"> |
|||
<h2> |
|||
@if (Model.ClientLogoUrl != null) |
|||
{ |
|||
<img src="@Model.ClientLogoUrl"> |
|||
} |
|||
@Model.ClientName |
|||
<small>is requesting your permission</small> |
|||
</h2> |
|||
</div> |
|||
</div> |
|||
</abp-card-header> |
|||
<abp-card-body> |
|||
<form method="post" asp-page="/Account/Consent"> |
|||
<input type="hidden" asp-for="ReturnUrl" /> |
|||
<input type="hidden" asp-for="ReturnUrlHash" /> |
|||
|
|||
<div>Uncheck the permissions you do not wish to grant.</div> |
|||
|
|||
@if (Model.IdentityScopes.Any()) |
|||
{ |
|||
<h3>Personal Information</h3> |
|||
|
|||
<ul class="list-group"> |
|||
@foreach (var scope in Model.IdentityScopes) |
|||
{ |
|||
@Html.Partial("Account/_ScopeListItem", scope) |
|||
} |
|||
</ul> |
|||
} |
|||
|
|||
@if (Model.ResourceScopes.Any()) |
|||
{ |
|||
<h3>Application Access</h3> |
|||
|
|||
<ul class="list-group"> |
|||
@foreach (var scope in Model.ResourceScopes) |
|||
{ |
|||
@Html.Partial("Account/_ScopeListItem", scope) |
|||
} |
|||
</ul> |
|||
} |
|||
|
|||
@if (Model.AllowRememberConsent) |
|||
{ |
|||
<div class="form-check"> |
|||
<label asp-for="@Model.ConsentInput.RememberConsent" class="form-check-label"> |
|||
<input asp-for="@Model.ConsentInput.RememberConsent" class="form-check-input" /> |
|||
<strong>Remember My Decision</strong> |
|||
</label> |
|||
</div> |
|||
} |
|||
|
|||
<div> |
|||
<button name="UserDecision" value="yes" class="btn btn-primary" autofocus>Yes, Allow</button> |
|||
<button name="UserDecision" value="no" class="btn">No, Do Not Allow</button> |
|||
@if (Model.ClientUrl != null) |
|||
{ |
|||
<a class="pull-right btn btn-secondary" target="_blank" href="@Model.ClientUrl"> |
|||
<strong>@Model.ClientName</strong> |
|||
</a> |
|||
} |
|||
</div> |
|||
|
|||
<div asp-validation-summary="All" class="text-danger"></div> |
|||
|
|||
</form> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
@ -0,0 +1,218 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using IdentityServer4.Models; |
|||
using IdentityServer4.Services; |
|||
using IdentityServer4.Stores; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc.RazorPages; |
|||
|
|||
namespace Volo.Abp.Account.Web.Pages |
|||
{ |
|||
//TODO: Move this into the Account folder!!!
|
|||
public class ConsentModel : AbpPageModel |
|||
{ |
|||
[HiddenInput] |
|||
[BindProperty(SupportsGet = true)] |
|||
public string ReturnUrl { get; set; } |
|||
|
|||
[HiddenInput] |
|||
[BindProperty(SupportsGet = true)] |
|||
public string ReturnUrlHash { get; set; } |
|||
|
|||
[BindProperty] |
|||
public ConsentInputModel ConsentInput { get; set; } |
|||
|
|||
public string ClientName { get; set; } |
|||
public string ClientUrl { get; set; } |
|||
public string ClientLogoUrl { get; set; } |
|||
public bool AllowRememberConsent { get; set; } |
|||
|
|||
public List<ScopeViewModel> IdentityScopes { get; set; } |
|||
|
|||
public List<ScopeViewModel> ResourceScopes { get; set; } |
|||
|
|||
private readonly IIdentityServerInteractionService _interaction; |
|||
private readonly IClientStore _clientStore; |
|||
private readonly IResourceStore _resourceStore; |
|||
|
|||
public ConsentModel( |
|||
IIdentityServerInteractionService interaction, |
|||
IClientStore clientStore, |
|||
IResourceStore resourceStore) |
|||
{ |
|||
_interaction = interaction; |
|||
_clientStore = clientStore; |
|||
_resourceStore = resourceStore; |
|||
} |
|||
|
|||
public async Task OnGet() |
|||
{ |
|||
var request = await _interaction.GetAuthorizationContextAsync(ReturnUrl); |
|||
if (request == null) |
|||
{ |
|||
throw new ApplicationException($"No consent request matching request: {ReturnUrl}"); |
|||
} |
|||
|
|||
var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId); |
|||
if (client == null) |
|||
{ |
|||
throw new ApplicationException($"Invalid client id: {request.ClientId}"); |
|||
} |
|||
|
|||
var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested); |
|||
if (resources == null || (!resources.IdentityResources.Any() && !resources.ApiResources.Any())) |
|||
{ |
|||
throw new ApplicationException($"No scopes matching: {request.ScopesRequested.Aggregate((x, y) => x + ", " + y)}"); |
|||
} |
|||
|
|||
ConsentInput = new ConsentInputModel |
|||
{ |
|||
RememberConsent = true, |
|||
ScopesConsented = new List<string>() |
|||
}; |
|||
|
|||
ClientName = client.ClientId; //TODO: Consider to create a ClientInfoModel
|
|||
ClientUrl = client.ClientUri; |
|||
ClientLogoUrl = client.LogoUri; |
|||
AllowRememberConsent = client.AllowRememberConsent; |
|||
|
|||
IdentityScopes = resources.IdentityResources.Select(x => CreateScopeViewModel(x, true)).ToList(); |
|||
ResourceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => CreateScopeViewModel(x, true)).ToList(); |
|||
|
|||
if (resources.OfflineAccess) |
|||
{ |
|||
ResourceScopes = ResourceScopes.Union(new[] {GetOfflineAccessScope(true)}).ToList(); |
|||
} |
|||
} |
|||
|
|||
public async Task<IActionResult> OnPost(string userDecision) |
|||
{ |
|||
var result = await ProcessConsentAsync(); |
|||
|
|||
if (result.IsRedirect) |
|||
{ |
|||
return Redirect(result.RedirectUri); |
|||
} |
|||
|
|||
if (result.HasValidationError) |
|||
{ |
|||
ModelState.AddModelError("", result.ValidationError); |
|||
} |
|||
|
|||
throw new ApplicationException("Error: "); |
|||
} |
|||
|
|||
private async Task<ProcessConsentResult> ProcessConsentAsync() |
|||
{ |
|||
var result = new ProcessConsentResult(); |
|||
|
|||
ConsentResponse grantedConsent = null; |
|||
|
|||
if (ConsentInput.UserDecision == "no") |
|||
{ |
|||
grantedConsent = ConsentResponse.Denied; |
|||
} |
|||
else |
|||
{ |
|||
if (ConsentInput.ScopesConsented != null && ConsentInput.ScopesConsented.Any()) |
|||
{ |
|||
var scopes = ConsentInput.ScopesConsented; |
|||
|
|||
grantedConsent = new ConsentResponse |
|||
{ |
|||
RememberConsent = ConsentInput.RememberConsent, |
|||
ScopesConsented = scopes.ToArray() |
|||
}; |
|||
} |
|||
else |
|||
{ |
|||
result.ValidationError = "You must pick at least one permission"; |
|||
} |
|||
} |
|||
|
|||
if (grantedConsent != null) |
|||
{ |
|||
// validate return url is still valid
|
|||
var request = await _interaction.GetAuthorizationContextAsync(ReturnUrl); |
|||
if (request == null) return result; |
|||
|
|||
// communicate outcome of consent back to identityserver
|
|||
await _interaction.GrantConsentAsync(request, grantedConsent); |
|||
|
|||
// indicate that's it ok to redirect back to authorization endpoint
|
|||
result.RedirectUri = ReturnUrl; //TODO: ReturnUrlHash?
|
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
|
|||
private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) |
|||
{ |
|||
return new ScopeViewModel |
|||
{ |
|||
Name = identity.Name, |
|||
DisplayName = identity.DisplayName, |
|||
Description = identity.Description, |
|||
Emphasize = identity.Emphasize, |
|||
Required = identity.Required, |
|||
Checked = check || identity.Required |
|||
}; |
|||
} |
|||
|
|||
public ScopeViewModel CreateScopeViewModel(Scope scope, bool check) |
|||
{ |
|||
return new ScopeViewModel |
|||
{ |
|||
Name = scope.Name, |
|||
DisplayName = scope.DisplayName, |
|||
Description = scope.Description, |
|||
Emphasize = scope.Emphasize, |
|||
Required = scope.Required, |
|||
Checked = check || scope.Required |
|||
}; |
|||
} |
|||
|
|||
private ScopeViewModel GetOfflineAccessScope(bool check) |
|||
{ |
|||
return new ScopeViewModel |
|||
{ |
|||
Name = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess, |
|||
DisplayName = "Offline Access", //TODO: Localize
|
|||
Description = "Access to your applications and resources, even when you are offline", |
|||
Emphasize = true, |
|||
Checked = check |
|||
}; |
|||
} |
|||
|
|||
public class ConsentInputModel |
|||
{ |
|||
public string UserDecision { get; set; } |
|||
|
|||
public List<string> ScopesConsented { get; set; } |
|||
|
|||
public bool RememberConsent { get; set; } |
|||
} |
|||
|
|||
public class ScopeViewModel |
|||
{ |
|||
public string Name { get; set; } |
|||
public string DisplayName { get; set; } |
|||
public string Description { get; set; } |
|||
public bool Emphasize { get; set; } |
|||
public bool Required { get; set; } |
|||
public bool Checked { get; set; } |
|||
} |
|||
|
|||
public class ProcessConsentResult |
|||
{ |
|||
public bool IsRedirect => RedirectUri != null; |
|||
public string RedirectUri { get; set; } |
|||
|
|||
public bool HasValidationError => ValidationError != null; |
|||
public string ValidationError { get; set; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers |
|||
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI |
|||
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap |
|||
Loading…
Reference in new issue