mirror of
https://git.teknik.io/Teknikode/Teknik.git
synced 2023-08-02 14:16:22 +02:00
Added editing of Clients and cleaned up the UI.
This commit is contained in:
parent
389023c3eb
commit
e027ac44d6
@ -12,6 +12,8 @@ using IdentityServer4.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Teknik.Configuration;
|
||||
using Teknik.IdentityServer.Models;
|
||||
@ -473,6 +475,8 @@ namespace Teknik.IdentityServer.Controllers
|
||||
},
|
||||
ClientId = clientId,
|
||||
ClientName = model.Name,
|
||||
ClientUri = model.HomepageUrl,
|
||||
LogoUri = model.LogoUrl,
|
||||
AllowedGrantTypes = new List<string>()
|
||||
{
|
||||
GrantType.AuthorizationCode,
|
||||
@ -480,20 +484,15 @@ namespace Teknik.IdentityServer.Controllers
|
||||
},
|
||||
|
||||
ClientSecrets =
|
||||
{
|
||||
new IdentityServer4.Models.Secret(clientSecret.Sha256())
|
||||
},
|
||||
{
|
||||
new IdentityServer4.Models.Secret(clientSecret.Sha256())
|
||||
},
|
||||
|
||||
RequireConsent = true,
|
||||
|
||||
RedirectUris =
|
||||
{
|
||||
model.RedirectURI
|
||||
},
|
||||
|
||||
PostLogoutRedirectUris =
|
||||
{
|
||||
model.PostLogoutRedirectURI
|
||||
model.CallbackUrl
|
||||
},
|
||||
|
||||
AllowedScopes = model.AllowedScopes,
|
||||
@ -507,6 +506,39 @@ namespace Teknik.IdentityServer.Controllers
|
||||
return new JsonResult(new { success = true, data = new { id = clientId, secret = clientSecret } });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public IActionResult EditClient(EditClientModel model, [FromServices] ConfigurationDbContext configContext)
|
||||
{
|
||||
// Validate it's an actual client
|
||||
var foundClient = configContext.Clients.Where(c => c.ClientId == model.ClientId).FirstOrDefault();
|
||||
if (foundClient != null)
|
||||
{
|
||||
foundClient.ClientName = model.Name;
|
||||
foundClient.ClientUri = model.HomepageUrl;
|
||||
foundClient.LogoUri = model.LogoUrl;
|
||||
configContext.Entry(foundClient).State = EntityState.Modified;
|
||||
|
||||
// Update the redirect URL for this client
|
||||
var results = configContext.Set<ClientRedirectUri>().Where(c => c.ClientId == foundClient.Id).ToList();
|
||||
if (results != null)
|
||||
{
|
||||
configContext.RemoveRange(results);
|
||||
}
|
||||
var newUri = new ClientRedirectUri();
|
||||
newUri.Client = foundClient;
|
||||
newUri.ClientId = foundClient.Id;
|
||||
newUri.RedirectUri = model.CallbackUrl;
|
||||
configContext.Add(newUri);
|
||||
|
||||
// Save all the changed
|
||||
configContext.SaveChanges();
|
||||
|
||||
return new JsonResult(new { success = true });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "Client does not exist." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public IActionResult DeleteClient(DeleteClientModel model, [FromServices] ConfigurationDbContext configContext)
|
||||
{
|
||||
|
@ -9,8 +9,9 @@ namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string RedirectURI { get; set; }
|
||||
public string PostLogoutRedirectURI { get; set; }
|
||||
public string HomepageUrl { get; set; }
|
||||
public string LogoUrl { get; set; }
|
||||
public string CallbackUrl { get; set; }
|
||||
public ICollection<string> AllowedScopes { get; set; }
|
||||
}
|
||||
}
|
||||
|
17
IdentityServer/Models/Manage/EditClientModel.cs
Normal file
17
IdentityServer/Models/Manage/EditClientModel.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class EditClientModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string ClientId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string HomepageUrl { get; set; }
|
||||
public string LogoUrl { get; set; }
|
||||
public string CallbackUrl { get; set; }
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@
|
||||
|
||||
@if (Model.SignOutIframeUrl != null)
|
||||
{
|
||||
<iframe width="0" height="0" class="signout" src="@Model.SignOutIframeUrl"></iframe>
|
||||
<iframe width="0" height="0" style="width:0; height:0; border:0; border:none" class="signout" src="@Model.SignOutIframeUrl"></iframe>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -5,10 +5,10 @@
|
||||
<div class="col-sm-12 text-center">
|
||||
<div class="page-consent">
|
||||
<div class="row page-header">
|
||||
<div class="col-sm-10 col-sm-offset-1">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
@if (Model.ClientLogoUrl != null)
|
||||
{
|
||||
<div class="client-logo"><img src="@Model.ClientLogoUrl"></div>
|
||||
<div class="client-logo"><img src="@Model.ClientLogoUrl" style="max-height: 100px; max-width: 100px;"></div>
|
||||
}
|
||||
<h1>
|
||||
@Model.ClientName
|
||||
@ -18,7 +18,7 @@
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-10 col-sm-offset-1">
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
@await Html.PartialAsync("_ValidationSummary")
|
||||
|
||||
<form asp-action="Index" class="consent-form">
|
||||
|
@ -31,7 +31,7 @@
|
||||
<div class="col-sm-2">
|
||||
@if (grant.ClientLogoUrl != null)
|
||||
{
|
||||
<img src="@grant.ClientLogoUrl">
|
||||
<img src="@grant.ClientLogoUrl" style="max-height: 100px; max-width: 100px;">
|
||||
}
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
|
@ -17,7 +17,7 @@ using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.Areas.API.V1.Controllers
|
||||
{
|
||||
[Authorize(AuthenticationSchemes = "Bearer", Policy = "AnyAPI")]
|
||||
[Authorize(AuthenticationSchemes = "Bearer", Policy = "WriteOnlyAPI")]
|
||||
public class PasteAPIv1Controller : APIv1Controller
|
||||
{
|
||||
public PasteAPIv1Controller(ILogger<Logger> logger, Config config, TeknikEntities dbContext) : base(logger, config, dbContext) { }
|
||||
|
@ -18,7 +18,7 @@ using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.Areas.API.V1.Controllers
|
||||
{
|
||||
[Authorize(AuthenticationSchemes = "Bearer", Policy = "AnyAPI")]
|
||||
[Authorize(AuthenticationSchemes = "Bearer", Policy = "WriteOnlyAPI")]
|
||||
public class ShortenAPIv1Controller : APIv1Controller
|
||||
{
|
||||
public ShortenAPIv1Controller(ILogger<Logger> logger, Config config, TeknikEntities dbContext) : base(logger, config, dbContext) { }
|
||||
|
@ -22,7 +22,7 @@ using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.Areas.API.V1.Controllers
|
||||
{
|
||||
[Authorize(AuthenticationSchemes = "Bearer", Policy = "AnyAPI")]
|
||||
[Authorize(AuthenticationSchemes = "Bearer", Policy = "WriteOnlyAPI")]
|
||||
public class UploadAPIv1Controller : APIv1Controller
|
||||
{
|
||||
public UploadAPIv1Controller(ILogger<Logger> logger, Config config, TeknikEntities dbContext) : base(logger, config, dbContext) { }
|
||||
|
@ -406,8 +406,9 @@ namespace Teknik.Areas.Users.Controllers
|
||||
{
|
||||
Id = client.ClientId,
|
||||
Name = client.ClientName,
|
||||
RedirectURI = string.Join(',', client.RedirectUris),
|
||||
PostLogoutRedirectURI = string.Join(',', client.PostLogoutRedirectUris),
|
||||
HomepageUrl = client.ClientUri,
|
||||
LogoUrl = client.LogoUri,
|
||||
CallbackUrl = string.Join(',', client.RedirectUris),
|
||||
AllowedScopes = client.AllowedScopes
|
||||
});
|
||||
}
|
||||
@ -1197,12 +1198,17 @@ namespace Teknik.Areas.Users.Controllers
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> CreateClient(string name, string redirectUri, string logoutUri, [FromServices] ICompositeViewEngine viewEngine)
|
||||
public async Task<IActionResult> CreateClient(string name, string homepageUrl, string logoUrl, string callbackUrl, [FromServices] ICompositeViewEngine viewEngine)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return Json(new { error = "You must enter a client name" });
|
||||
if (string.IsNullOrEmpty(callbackUrl))
|
||||
return Json(new { error = "You must enter an authorization callback URL" });
|
||||
|
||||
// Validate the code with the identity server
|
||||
var result = await IdentityHelper.CreateClient(_config, User.Identity.Name, name, redirectUri, logoutUri, "openid", "role", "account-info", "security-info", "teknik-api.read", "teknik-api.write");
|
||||
var result = await IdentityHelper.CreateClient(_config, User.Identity.Name, name, homepageUrl, logoUrl, callbackUrl, "openid", "role", "account-info", "security-info", "teknik-api.read", "teknik-api.write");
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
@ -1211,8 +1217,9 @@ namespace Teknik.Areas.Users.Controllers
|
||||
ClientViewModel model = new ClientViewModel();
|
||||
model.Id = client["id"].ToString();
|
||||
model.Name = name;
|
||||
model.RedirectURI = redirectUri;
|
||||
model.PostLogoutRedirectURI = logoutUri;
|
||||
model.HomepageUrl = homepageUrl;
|
||||
model.LogoUrl = logoUrl;
|
||||
model.CallbackUrl = callbackUrl;
|
||||
|
||||
string renderedView = await RenderPartialViewToString(viewEngine, "~/Areas/User/Views/User/Settings/ClientView.cshtml", model);
|
||||
|
||||
@ -1226,6 +1233,70 @@ namespace Teknik.Areas.Users.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> GetClient(string clientId)
|
||||
{
|
||||
Client foundClient = await IdentityHelper.GetClient(_config, User.Identity.Name, clientId);
|
||||
if (foundClient != null)
|
||||
{
|
||||
ClientViewModel model = new ClientViewModel()
|
||||
{
|
||||
Id = foundClient.ClientId,
|
||||
Name = foundClient.ClientName,
|
||||
HomepageUrl = foundClient.ClientUri,
|
||||
LogoUrl = foundClient.LogoUri,
|
||||
CallbackUrl = string.Join(',', foundClient.RedirectUris),
|
||||
AllowedScopes = foundClient.AllowedScopes
|
||||
};
|
||||
|
||||
return Json(new { result = true, client = model });
|
||||
}
|
||||
return new StatusCodeResult(StatusCodes.Status403Forbidden);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> EditClient(string clientId, string name, string homepageUrl, string logoUrl, string callbackUrl, [FromServices] ICompositeViewEngine viewEngine)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return Json(new { error = "You must enter a client name" });
|
||||
if (string.IsNullOrEmpty(callbackUrl))
|
||||
return Json(new { error = "You must enter an authorization callback URL" });
|
||||
|
||||
Client foundClient = await IdentityHelper.GetClient(_config, User.Identity.Name, clientId);
|
||||
|
||||
if (foundClient == null)
|
||||
return Json(new { error = "Client does not exist" });
|
||||
|
||||
// Validate the code with the identity server
|
||||
var result = await IdentityHelper.EditClient(_config, User.Identity.Name, clientId, name, homepageUrl, logoUrl, callbackUrl);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
var client = (JObject)result.Data;
|
||||
|
||||
ClientViewModel model = new ClientViewModel();
|
||||
model.Id = clientId;
|
||||
model.Name = name;
|
||||
model.HomepageUrl = homepageUrl;
|
||||
model.LogoUrl = logoUrl;
|
||||
model.CallbackUrl = callbackUrl;
|
||||
|
||||
string renderedView = await RenderPartialViewToString(viewEngine, "~/Areas/User/Views/User/Settings/ClientView.cshtml", model);
|
||||
|
||||
return Json(new { result = true, clientId = clientId, html = renderedView });
|
||||
}
|
||||
return Json(new { error = result.Message });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Json(new { error = ex.GetFullMessage(true) });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DeleteClient(string clientId)
|
||||
|
@ -333,7 +333,7 @@ namespace Teknik.Areas.Users.Utility
|
||||
var result = await Get(config, manageUrl);
|
||||
if (result.Success)
|
||||
{
|
||||
return (Client)result.Data;
|
||||
return ((JObject)result.Data).ToObject<Client>();
|
||||
}
|
||||
throw new Exception(result.Message);
|
||||
}
|
||||
@ -350,7 +350,7 @@ namespace Teknik.Areas.Users.Utility
|
||||
throw new Exception(result.Message);
|
||||
}
|
||||
|
||||
public static async Task<IdentityResult> CreateClient(Config config, string username, string name, string redirectURI, string postLogoutRedirectURI, params string[] allowedScopes)
|
||||
public static async Task<IdentityResult> CreateClient(Config config, string username, string name, string homepageUrl, string logoUrl, string callbackUrl, params string[] allowedScopes)
|
||||
{
|
||||
var manageUrl = CreateUrl(config, $"Manage/CreateClient");
|
||||
|
||||
@ -359,13 +359,31 @@ namespace Teknik.Areas.Users.Utility
|
||||
{
|
||||
username = username,
|
||||
name = name,
|
||||
redirectURI = redirectURI,
|
||||
postLogoutRedirectURI = postLogoutRedirectURI,
|
||||
homepageUrl = homepageUrl,
|
||||
logoUrl = logoUrl,
|
||||
callbackUrl = callbackUrl,
|
||||
allowedScopes = allowedScopes
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
public static async Task<IdentityResult> EditClient(Config config, string username, string clientId, string name, string homepageUrl, string logoUrl, string callbackUrl)
|
||||
{
|
||||
var manageUrl = CreateUrl(config, $"Manage/EditClient");
|
||||
|
||||
var response = await Post(config, manageUrl,
|
||||
new
|
||||
{
|
||||
username = username,
|
||||
clientId = clientId,
|
||||
name = name,
|
||||
homepageUrl = homepageUrl,
|
||||
logoUrl = logoUrl,
|
||||
callbackUrl = callbackUrl
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
public static async Task<IdentityResult> DeleteClient(Config config, string clientId)
|
||||
{
|
||||
var manageUrl = CreateUrl(config, $"Manage/DeleteClient");
|
||||
|
@ -10,8 +10,9 @@ namespace Teknik.Areas.Users.ViewModels
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string RedirectURI { get; set; }
|
||||
public string PostLogoutRedirectURI { get; set; }
|
||||
public string HomepageUrl { get; set; }
|
||||
public string LogoUrl { get; set; }
|
||||
public string CallbackUrl { get; set; }
|
||||
public ICollection<string> AllowedScopes { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -15,23 +15,23 @@
|
||||
<!form id="registrationForm" action="@Url.SubRouteUrl("user", "User.Register")" method="post" accept-charset="UTF-8">
|
||||
<input name="Register.ReturnUrl" id="registerReturnUrl" type="hidden" value="@Model.ReturnUrl" />
|
||||
<div class="form-group">
|
||||
<label for="registerUsername">Username</label>
|
||||
<label for="registerUsername">Username <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="registerUsername" value="" placeholder="Bob" name="Register.Username" data-val-required="The Username field is required." data-val="true"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="registerPassword">Password</label>
|
||||
<label for="registerPassword">Password <span class="text-danger">*</span></label>
|
||||
<input type="password" class="form-control" id="registerPassword" value="" placeholder="********" name="Register.Password" data-val-required="The Password field is required." data-val="true"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="registerConfirmPassword">Confirm Password</label>
|
||||
<label for="registerConfirmPassword">Confirm Password <span class="text-danger">*</span></label>
|
||||
<input type="password" class="form-control" id="registerConfirmPassword" value="" placeholder="********" name="Register.ConfirmPassword" data-val-required="The Confirm Password field is required." data-val="true"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="registerInviteCode">Invite Code@(Config.UserConfig.InviteCodeRequired ? string.Empty : " (Optional)")</label>
|
||||
<label for="registerInviteCode">Invite Code@(Config.UserConfig.InviteCodeRequired ? " <span class=\"text-danger\">*</span>" : string.Empty)</label>
|
||||
<input type="text" class="form-control" id="registerInviteCode" value="@Model.InviteCode" placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" name="Register.InviteCode"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="registerRecoveryEmail">Recovery Email (Optional)</label>
|
||||
<label for="registerRecoveryEmail">Recovery Email</label>
|
||||
<input type="text" class="form-control" id="registerRecoveryEmail" value="" placeholder="user@example.com" name="Register.RecoveryEmail"/>
|
||||
</div>
|
||||
<p class="text-center">
|
||||
|
@ -2,22 +2,53 @@
|
||||
|
||||
<li class="list-group-item" id="client_@Model.Id">
|
||||
<div class="btn-group btn-group-sm pull-right" role="group" aria-label="...">
|
||||
<button type="button" class="btn btn-primary text-primary editClient" id="editClient_@Model.Id" data-clientId="@Model.Id">Edit</button>
|
||||
<button type="button" class="btn btn-danger text-danger deleteClient" id="deleteClient_@Model.Id" data-clientId="@Model.Id">Delete</button>
|
||||
</div>
|
||||
<h3 class="list-group-item-heading" id="clientName_@Model.Name">@Model.Name</h3>
|
||||
<h3 class="list-group-item-heading" id="clientName_@Model.Name">
|
||||
@Model.Name
|
||||
@if (!string.IsNullOrEmpty(Model.LogoUrl))
|
||||
{
|
||||
<img src="@Model.LogoUrl" class="img-thumbnail" style="max-height: 100px; max-width: 100px;" />
|
||||
}
|
||||
</h3>
|
||||
<hr />
|
||||
<div class="list-group-item-text">
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<strong>Client Id</strong>: @Model.Id
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<div class="row">
|
||||
<div class="col-sm-4"><strong>Redirect Url</strong></div>
|
||||
<div class="col-sm-8">@Model.RedirectURI</div>
|
||||
<div class="col-sm-12">
|
||||
<strong>Client Id</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4"><strong>Logout Url</strong></div>
|
||||
<div class="col-sm-8">@Model.PostLogoutRedirectURI</div>
|
||||
<div class="col-sm-12">
|
||||
@Model.Id
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<strong>Homepage Url</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<a href="@Model.HomepageUrl" target="_blank">@Model.HomepageUrl</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<strong>Authorization Callback Url</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
@Model.CallbackUrl
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -7,46 +7,12 @@
|
||||
}
|
||||
|
||||
<script>
|
||||
var generateTokenURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "GenerateToken" })';
|
||||
var revokeAllTokensURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "RevokeAllTokens" })';
|
||||
var editTokenNameURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "EditTokenName" })';
|
||||
var deleteTokenURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "DeleteToken" })';
|
||||
|
||||
|
||||
var createClientURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "CreateClient" })';
|
||||
var editClientURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "EditClient" })';
|
||||
var deleteClientURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "DeleteClient" })';
|
||||
var getClientURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "GetClient" })';
|
||||
</script>
|
||||
|
||||
<!--
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h2>API Clients</h2>
|
||||
<hr />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<br />
|
||||
<label for="authTokens"><h4>API Clients</h4></label><span class="pull-right"><button type="button" class="btn btn-default" id="create_authToken">Create Auth Token</button> <button type="button" class="btn btn-danger" id="revoke_all_authTokens">Revoke All</button></span>
|
||||
<div id="authTokens" style="overflow-y: auto; max-height: 400px;">
|
||||
<ul class="list-group" id="apiClientList">
|
||||
@if (Model.AuthTokens.Any())
|
||||
{
|
||||
foreach (AuthTokenViewModel token in Model.AuthTokens)
|
||||
{
|
||||
@await Html.PartialAsync("Settings/AuthToken", token)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="list-group-item text-center" id="noAuthTokens">No Authentication Tokens</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h2>Clients</h2>
|
||||
@ -56,8 +22,8 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<br />
|
||||
<label for="clients"><h4>Clients</h4></label><span class="pull-right"><button type="button" class="btn btn-default" id="create_client">Create Client</button></span>
|
||||
<div id="clients" style="overflow-y: auto; max-height: 400px;">
|
||||
<label for="clients"><h4>Clients</h4></label><span class="pull-right"><button type="button" class="btn btn-default" id="createClient">Create Client</button></span>
|
||||
<div id="clients">
|
||||
<ul class="list-group" id="clientList">
|
||||
@if (Model.Clients.Any())
|
||||
{
|
||||
@ -75,4 +41,52 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal fade" id="clientModal" tabindex="-1" role="dialog" aria-labelledby="clientModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header modal-header-default">
|
||||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" id="clientModalLabel">Client Information</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" class="form-control" id="clientId" name="clientId" />
|
||||
<div class="row">
|
||||
<div class="col-sm-12 text-center">
|
||||
<div id="clientStatus">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="clientName">Application Name <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="clientName" name="clientName" data-val-required="The Client Name field is required." data-val="true" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="clientCallbackUrl">Homepage URL</label>
|
||||
<input type="text" class="form-control" id="clientHomepageUrl" name="clientHomepageUrl" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="clientLogoUrl">Logo URL</label>
|
||||
<input type="text" class="form-control" id="clientLogoUrl" name="clientLogoUrl" />
|
||||
</div>
|
||||
<p>
|
||||
<small>
|
||||
The logo will be rendered at a max size of 100x100.
|
||||
</small>
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label for="clientCallbackUrl">Authorization callback URL <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="clientCallbackUrl" name="clientCallbackUrl" data-val-required="The Authorization callback URL field is required." data-val="true" />
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<button class="btn btn-primary pull-right hidden clientSubmit" id="clientCreateSubmit" type="submit" name="clientCreateSubmit">Create Client</button>
|
||||
<button class="btn btn-primary pull-right hidden clientSubmit" id="clientEditSubmit" type="submit" name="clientEditSubmit">Save Client</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<bundle src="js/user.settings.developer.min.js" append-version="true"></bundle>
|
||||
|
@ -15,7 +15,6 @@
|
||||
NewPasswordConfirm: password_confirm
|
||||
}),
|
||||
success: function (response) {
|
||||
enableButton('#change_password_submit', 'Change Password');
|
||||
if (response.result) {
|
||||
$("#top_msg").css('display', 'inline', 'important');
|
||||
$("#top_msg").html('<div class="alert alert-success alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>Password has successfully been changed.</div>');
|
||||
@ -36,6 +35,8 @@
|
||||
});
|
||||
|
||||
$('#delete_account').click(function () {
|
||||
disableButton('#delete_account', 'Deleting Account...');
|
||||
|
||||
bootbox.confirm("Are you sure you want to delete your account?", function (result) {
|
||||
if (result) {
|
||||
$.ajax({
|
||||
@ -55,7 +56,11 @@
|
||||
$("#top_msg").css('display', 'inline', 'important');
|
||||
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' + parseErrorMessage(response.responseText) + '</div>');
|
||||
}
|
||||
});
|
||||
}).always(function () {
|
||||
enableButton('#delete_account', 'Delete Account');
|
||||
});;
|
||||
} else {
|
||||
enableButton('#delete_account', 'Delete Account');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -13,7 +13,6 @@ $(document).ready(function () {
|
||||
Description: blog_desc,
|
||||
}),
|
||||
success: function (response) {
|
||||
enableButton('#update_submit', 'Save');
|
||||
if (response.result) {
|
||||
$("#top_msg").css('display', 'inline', 'important');
|
||||
$("#top_msg").html('<div class="alert alert-success alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>Settings Saved!</div>');
|
||||
|
@ -1,210 +1,131 @@
|
||||
$(document).ready(function () {
|
||||
|
||||
$('#create_client').click(function () {
|
||||
var name, redirectUri, logoutUri;
|
||||
bootbox.prompt("Specify a name for this Client", function (result) {
|
||||
if (result) {
|
||||
name = result;
|
||||
bootbox.prompt("Specify the Redirect URI for this Client", function (result) {
|
||||
if (result) {
|
||||
redirectUri = result;
|
||||
bootbox.prompt("Specify the Logout URI for this Client", function (result) {
|
||||
if (result) {
|
||||
logoutUri = result;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: createClientURL,
|
||||
data: AddAntiForgeryToken({ name: name, redirectUri: redirectUri, logoutUri: logoutUri }),
|
||||
success: function (response) {
|
||||
if (response.result) {
|
||||
var dialog = bootbox.dialog({
|
||||
closeButton: false,
|
||||
buttons: {
|
||||
close: {
|
||||
label: 'Close',
|
||||
className: 'btn-primary',
|
||||
callback: function () {
|
||||
if ($('#noClients')) {
|
||||
$('#noClients').remove();
|
||||
}
|
||||
var item = $(response.html);
|
||||
$('#clientModal').on('shown.bs.modal', function (e) {
|
||||
$("#clientStatus").css('display', 'none', 'important');
|
||||
$("#clientStatus").html('');
|
||||
|
||||
var deleteBtn = item.find('.deleteClient');
|
||||
$('#clientName').focus();
|
||||
});
|
||||
|
||||
deleteBtn.click(function () {
|
||||
var clientId = $(this).attr("data-clientId");
|
||||
deleteClient(clientId);
|
||||
});
|
||||
$('#clientModal').on('hide.bs.modal', function (e) {
|
||||
$("#clientStatus").css('display', 'none', 'important');
|
||||
$("#clientStatus").html('');
|
||||
|
||||
$('#clientList').append(item);
|
||||
}
|
||||
}
|
||||
},
|
||||
title: "Client Secret",
|
||||
message: '<label for="clientSecret">Make sure to copy your client secret now.<br />You won\'t be able to see it again!</label><input type="text" class="form-control" id="clientSecret" value="' + response.secret + '">',
|
||||
});
|
||||
$(this).find('#clientCreateSubmit').addClass('hidden');
|
||||
$(this).find('#clientEditSubmit').addClass('hidden');
|
||||
|
||||
dialog.init(function () {
|
||||
dialog.find('#authToken').click(function () {
|
||||
$(this).select();
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
$("#top_msg").css('display', 'inline', 'important');
|
||||
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' + parseErrorMessage(response) + '</div>');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
clearInputs('#clientModal');
|
||||
});
|
||||
|
||||
|
||||
$('#clientModal').find('#clientCreateSubmit').click(createClient);
|
||||
$('#clientModal').find('#clientEditSubmit').click(editClientSave);
|
||||
|
||||
$("#createClient").click(function () {
|
||||
$('#clientModal').find('#clientCreateSubmit').removeClass('hidden');
|
||||
$('#clientModal').find('#clientCreateSubmit').text('Create Client');
|
||||
|
||||
$('#clientModal').modal('show');
|
||||
});
|
||||
|
||||
$(".editClient").click(function () {
|
||||
var clientId = $(this).attr("data-clientId");
|
||||
editClient(clientId);
|
||||
});
|
||||
|
||||
$(".deleteClient").click(function () {
|
||||
var clientId = $(this).attr("data-clientId");
|
||||
deleteClient(clientId);
|
||||
});
|
||||
|
||||
$('#create_authToken').click(function () {
|
||||
bootbox.prompt("Specify a name for this Auth Token", function (result) {
|
||||
if (result) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: generateTokenURL,
|
||||
data: AddAntiForgeryToken({ name: result }),
|
||||
success: function (response) {
|
||||
if (response.result) {
|
||||
var dialog = bootbox.dialog({
|
||||
closeButton: false,
|
||||
buttons: {
|
||||
close: {
|
||||
label: 'Close',
|
||||
className: 'btn-primary',
|
||||
callback: function () {
|
||||
if ($('#noAuthTokens')) {
|
||||
$('#noAuthTokens').remove();
|
||||
}
|
||||
var item = $(response.result.html);
|
||||
|
||||
var deleteBtn = item.find('.deleteAuthToken');
|
||||
var editBtn = item.find('.editAuthToken');
|
||||
|
||||
deleteBtn.click(function () {
|
||||
var authTokenId = $(this).attr("data-authid");
|
||||
deleteAuthToken(authTokenId);
|
||||
});
|
||||
|
||||
editBtn.click(function () {
|
||||
var authTokenId = $(this).attr("data-authid");
|
||||
editAuthToken(authTokenId);
|
||||
});
|
||||
|
||||
$('#authTokenList').append(item);
|
||||
}
|
||||
}
|
||||
},
|
||||
title: "Authentication Token",
|
||||
message: '<label for="authToken">Make sure to copy your new personal access token now.<br />You won\'t be able to see it again!</label><input type="text" class="form-control" id="authToken" value="' + response.result.token + '">',
|
||||
});
|
||||
|
||||
dialog.init(function () {
|
||||
dialog.find('#authToken').click(function () {
|
||||
$(this).select();
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
$("#top_msg").css('display', 'inline', 'important');
|
||||
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' + parseErrorMessage(response) + '</div>');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#revoke_all_authTokens').click(function () {
|
||||
bootbox.confirm("Are you sure you want to revoke all your clients?<br /><br />This is <b>irreversable</b> and all applications using these clients will stop working.", function (result) {
|
||||
if (result) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: revokeAllClientsURL,
|
||||
data: AddAntiForgeryToken({}),
|
||||
success: function (response) {
|
||||
if (response.result) {
|
||||
$('#authTokenList').html('<li class="list-group-item text-center" id="noAuthTokens">No Authentication Tokens</li>');
|
||||
}
|
||||
else {
|
||||
$("#top_msg").css('display', 'inline', 'important');
|
||||
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' + parseErrorMessage(response) + '</div>');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(".deleteAuthToken").click(function () {
|
||||
var authTokenId = $(this).attr("data-authid");
|
||||
deleteAuthToken(authTokenId);
|
||||
});
|
||||
|
||||
$(".editAuthToken").click(function () {
|
||||
var authTokenId = $(this).attr("data-authid");
|
||||
editAuthToken(authTokenId);
|
||||
});
|
||||
});
|
||||
|
||||
function editAuthToken(authTokenId) {
|
||||
bootbox.prompt("Specify a new name for this Auth Token", function (result) {
|
||||
if (result) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: editTokenNameURL,
|
||||
data: AddAntiForgeryToken({ tokenId: authTokenId, name: result }),
|
||||
success: function (response) {
|
||||
if (response.result) {
|
||||
$('#authTokenName_' + authTokenId).html(response.result.name);
|
||||
}
|
||||
else {
|
||||
$("#top_msg").css('display', 'inline', 'important');
|
||||
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' + parseErrorMessage(response) + '</div>');
|
||||
function createClient() {
|
||||
saveClientInfo(createClientURL, 'Create Client', 'Creating Client...', function (response) {
|
||||
$('#clientModal').modal('hide');
|
||||
|
||||
var dialog = bootbox.dialog({
|
||||
closeButton: false,
|
||||
buttons: {
|
||||
close: {
|
||||
label: 'Close',
|
||||
className: 'btn-primary',
|
||||
callback: function () {
|
||||
if ($('#noClients')) {
|
||||
$('#noClients').remove();
|
||||
}
|
||||
|
||||
var item = $(html);
|
||||
|
||||
processClientItem(item);
|
||||
|
||||
$('#clientList').append(item);
|
||||
}
|
||||
}
|
||||
},
|
||||
title: "Client Secret",
|
||||
message: '<label for="clientSecret">Make sure to copy your client secret now.<br />You won\'t be able to see it again!</label><input type="text" class="form-control" id="clientSecret" value="' + response.secret + '">',
|
||||
});
|
||||
|
||||
dialog.init(function () {
|
||||
dialog.find('#clientSecret').click(function () {
|
||||
$(this).select();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function processClientItem(item) {
|
||||
item.find('.editClient').click(function () {
|
||||
var clientId = $(this).attr("data-clientId");
|
||||
editClient(clientId);
|
||||
});
|
||||
|
||||
item.find('.deleteClient').click(function () {
|
||||
var clientId = $(this).attr("data-clientId");
|
||||
deleteClient(clientId);
|
||||
});
|
||||
}
|
||||
|
||||
function editClient(clientId) {
|
||||
disableButton('.editClient[data-clientId="' + clientId + '"]', 'Loading...');
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: getClientURL,
|
||||
data: AddAntiForgeryToken({ clientId: clientId }),
|
||||
success: function (data) {
|
||||
if (data.result) {
|
||||
$('#clientModal').find('#clientId').val(data.client.id);
|
||||
$('#clientModal').find('#clientName').val(data.client.name);
|
||||
$('#clientModal').find('#clientHomepageUrl').val(data.client.homepageUrl);
|
||||
$('#clientModal').find('#clientLogoUrl').val(data.client.logoUrl);
|
||||
$('#clientModal').find('#clientCallbackUrl').val(data.client.callbackUrl);
|
||||
|
||||
$('#clientModal').find('#clientEditSubmit').removeClass('hidden');
|
||||
$('#clientModal').find('#clientEditSubmit').text('Save Client');
|
||||
|
||||
$('#clientModal').modal('show');
|
||||
|
||||
enableButton('.editClient[data-clientId="' + clientId + '"]', 'Edit');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteAuthToken(authTokenId) {
|
||||
bootbox.confirm("Are you sure you want to revoke this auth token?<br /><br />This is <b>irreversable</b> and all applications using this token will stop working.", function (result) {
|
||||
if (result) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: deleteTokenURL,
|
||||
data: AddAntiForgeryToken({ tokenId: authTokenId }),
|
||||
success: function (response) {
|
||||
if (response.result) {
|
||||
$('#authToken_' + authTokenId).remove();
|
||||
if ($('#authTokenList li').length <= 0) {
|
||||
$('#authTokenList').html('<li class="list-group-item text-center" id="noAuthTokens">No Authentication Tokens</li>');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$("#top_msg").css('display', 'inline', 'important');
|
||||
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' + parseErrorMessage(response) + '</div>');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function editClientSave() {
|
||||
saveClientInfo(editClientURL, 'Save Client', 'Saving Client...', function (response) {
|
||||
$('#client_' + response.clientId).replaceWith(response.html);
|
||||
processClientItem($('#client_' + response.clientId));
|
||||
|
||||
$('#clientModal').modal('hide');
|
||||
|
||||
$("#top_msg").css('display', 'inline', 'important');
|
||||
$("#top_msg").html('<div class="alert alert-success alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>Successfully Saved Client</div>');
|
||||
});
|
||||
}
|
||||
|
||||
function deleteClient(clientId) {
|
||||
disableButton('.deleteClient[data-clientId="' + clientId + '"]', 'Deleting...');
|
||||
bootbox.confirm("<h2>Are you sure you want to delete this client?</h2><br /><br />This is <b>irreversable</b> and all applications using these client credentials will stop working.", function (result) {
|
||||
if (result) {
|
||||
$.ajax({
|
||||
@ -223,7 +144,45 @@ function deleteClient(clientId) {
|
||||
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' + parseErrorMessage(response) + '</div>');
|
||||
}
|
||||
}
|
||||
}).always(function () {
|
||||
enableButton('.deleteClient[data-clientId="' + clientId + '"]', 'Delete');
|
||||
});
|
||||
} else {
|
||||
enableButton('.deleteClient[data-clientId="' + clientId + '"]', 'Delete');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveClientInfo(url, submitText, submitActionText, callback) {
|
||||
var name, homepageUrl, logoUrl, callbackUrl;
|
||||
disableButton('.clientSubmit', submitActionText);
|
||||
|
||||
clientId = $('#clientModal').find('#clientId').val();
|
||||
name = $('#clientModal').find('#clientName').val();
|
||||
homepageUrl = $('#clientModal').find('#clientHomepageUrl').val();
|
||||
logoUrl = $('#clientModal').find('#clientLogoUrl').val();
|
||||
callbackUrl = $('#clientModal').find('#clientCallbackUrl').val();
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: AddAntiForgeryToken({ clientId: clientId, name: name, homepageUrl: homepageUrl, logoUrl: logoUrl, callbackUrl: callbackUrl }),
|
||||
success: function (response) {
|
||||
if (response.result) {
|
||||
if (callback) {
|
||||
callback(response);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$("#clientStatus").css('display', 'inline', 'important');
|
||||
$("#clientStatus").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' + parseErrorMessage(response) + '</div>');
|
||||
}
|
||||
},
|
||||
error: function (response) {
|
||||
$("#clientStatus").css('display', 'inline', 'important');
|
||||
$("#clientStatus").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' + parseErrorMessage(response.responseText) + '</div>');
|
||||
}
|
||||
}).always(function () {
|
||||
enableButton('.clientSubmit', submitText);
|
||||
});
|
||||
}
|
||||
|
@ -135,6 +135,15 @@ function removeAmp(code) {
|
||||
return code;
|
||||
}
|
||||
|
||||
function clearInputs(parent) {
|
||||
$(parent).find('input:text').each(function () {
|
||||
$(this).val('');
|
||||
});
|
||||
$(parent).find('textarea').each(function () {
|
||||
$(this).val('');
|
||||
});
|
||||
}
|
||||
|
||||
String.prototype.hashCode = function () {
|
||||
var hash = 0, i, chr, len;
|
||||
if (this.length === 0) return hash;
|
||||
|
@ -21,7 +21,7 @@
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="@Url.SubRouteUrl("www", "Home.Index")"><img src="@logoPath" height="20" alt="Teknik"></a>
|
||||
<a class="navbar-brand" href="@Url.SubRouteUrl("www", "Home.Index")"><img src="@logoPath" height="20" alt="Teknik" /></a>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
|
Loading…
Reference in New Issue
Block a user