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