1
0
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:
Uncled1023 2019-01-19 23:27:53 -08:00
parent 389023c3eb
commit e027ac44d6
20 changed files with 425 additions and 268 deletions

View File

@ -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)
{

View File

@ -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; }
}
}

View 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; }
}
}

View File

@ -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>

View File

@ -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">

View File

@ -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">

View File

@ -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) { }

View File

@ -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) { }

View File

@ -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) { }

View File

@ -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)

View File

@ -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");

View File

@ -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; }
}
}

View File

@ -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">

View File

@ -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>

View File

@ -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">&times;</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>

View File

@ -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">&times;</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">&times;</button>' + parseErrorMessage(response.responseText) + '</div>');
}
});
}).always(function () {
enableButton('#delete_account', 'Delete Account');
});;
} else {
enableButton('#delete_account', 'Delete Account');
}
});
});

View File

@ -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">&times;</button>Settings Saved!</div>');

View File

@ -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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</button>' + parseErrorMessage(response.responseText) + '</div>');
}
}).always(function () {
enableButton('.clientSubmit', submitText);
});
}

View File

@ -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;

View File

@ -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">