added in UI shell for creating/editing resources (clients with "introspection" set and no grants or other parameters)

pull/604/head
Justin Richer 2014-05-26 15:41:50 -04:00
parent b8908b6efe
commit 0775785ce3
6 changed files with 823 additions and 3 deletions

View File

@ -15,4 +15,5 @@
<li><a href="manage/#user/profile">View Profile Information</a></li>
<li class="divider"></li>
<li class="nav-header">Developer</li>
<li><a href="manage/#dev/dynreg">Self-service client registration</a><li>
<li><a href="manage/#dev/dynreg">Self-service client registration</a><li>
<li><a href="manage/#dev/resource">Self-service protected resource registration</a><li>

View File

@ -29,6 +29,7 @@
<script type="text/javascript" src="resources/js/scope.js"></script>
<script type="text/javascript" src="resources/js/whitelist.js"></script>
<script type="text/javascript" src="resources/js/dynreg.js"></script>
<script type="text/javascript" src="resources/js/rsreg.js"></script>
<script type="text/javascript" src="resources/js/token.js"></script>
<script type="text/javascript" src="resources/js/admin.js"></script>
</c:if>

View File

@ -426,6 +426,10 @@ var AppRouter = Backbone.Router.extend({
"dev/dynreg/new":"newDynReg",
"dev/dynreg/edit":"editDynReg",
"dev/resource":"resReg",
"dev/resource/new":"newResReg",
"dev/resource/edit":"editResReg",
"": "root"
},
@ -456,6 +460,7 @@ var AppRouter = Backbone.Router.extend({
this.systemScopeListView = new SystemScopeListView({model:this.systemScopeList});
this.tokensListView = new TokenListView({model: {access: this.accessTokensList, refresh: this.refreshTokensList}, clientList: this.clientList, systemScopeList: this.systemScopeList});
this.dynRegRootView = new DynRegRootView({systemScopeList: this.systemScopeList});
this.resRegRootView = new ResRegRootView({systemScopeList: this.systemScopeList});
this.breadCrumbView = new BreadCrumbView({
collection:new Backbone.Collection()
@ -893,13 +898,66 @@ var AppRouter = Backbone.Router.extend({
this.breadCrumbView.collection.add([
{text:"Home", href:""},
{text:"Client Registration", href:"manage/#dev/dynreg"},
{text:"Edit", href:"manage/#dev/dynreg/new"}
{text:"Edit", href:"manage/#dev/dynreg/edit"}
]);
setPageTitle("Edit a Dynamically Registered Client");
// note that this doesn't actually load the client, that's supposed to happen elsewhere...
},
resReg:function() {
this.breadCrumbView.collection.reset();
this.breadCrumbView.collection.add([
{text:"Home", href:""},
{text:"Protected Resource Registration", href:"manage/#dev/resource"}
]);
this.resRegRootView.load(function() {
$('#content').html(app.resRegRootView.render().el);
setPageTitle("Self-service Protected Resource Registration");
});
},
newResReg:function() {
this.breadCrumbView.collection.reset();
this.breadCrumbView.collection.add([
{text:"Home", href:""},
{text:"Protected Resource Registration", href:"manage/#dev/resource"},
{text:"New", href:"manage/#dev/resource/new"}
]);
var client = new ResRegClient();
var view = new ResRegEditView({model: client, systemScopeList:this.systemScopeList});
view.load(function() {
client.set({
scope: _.uniq(_.flatten(app.systemScopeList.defaultDynRegScopes().pluck("value"))).join(" "),
token_endpoint_auth_method: 'client_secret_basic',
}, { silent: true });
$('#content').html(view.render().el);
view.delegateEvents();
setPageTitle("Dynamically Register a New Protected Resource");
});
},
editResReg:function() {
this.breadCrumbView.collection.reset();
this.breadCrumbView.collection.add([
{text:"Home", href:""},
{text:"Protected Resource Registration", href:"manage/#dev/resource"},
{text:"Edit", href:"manage/#dev/resource/edit"}
]);
setPageTitle("Edit a Dynamically Registered Protected Resource");
// note that this doesn't actually load the client, that's supposed to happen elsewhere...
},
profile:function() {
this.breadCrumbView.collection.reset();
this.breadCrumbView.collection.add([
@ -937,6 +995,7 @@ $(function () {
$.get('resources/template/scope.html', _load);
$.get('resources/template/whitelist.html', _load);
$.get('resources/template/dynreg.html', _load);
$.get('resources/template/rsreg.html', _load);
$.get('resources/template/token.html', _load);
jQuery.ajaxSetup({async:true});

View File

@ -0,0 +1,458 @@
var ResRegClient = Backbone.Model.extend({
idAttribute: "client_id",
defaults:{
client_id:null,
client_secret:null,
redirect_uris:[],
client_name:null,
client_uri:null,
logo_uri:null,
contacts:[],
tos_uri:null,
token_endpoint_auth_method:null,
scope:null,
grant_types:[],
response_types:[],
policy_uri:null,
jwks_uri:null,
application_type:null,
sector_identifier_uri:null,
subject_type:null,
request_object_signing_alg:null,
userinfo_signed_response_alg:null,
userinfo_encrypted_response_alg:null,
userinfo_encrypted_response_enc:null,
id_token_signed_response_alg:null,
id_token_encrypted_response_alg:null,
id_token_encrypted_response_enc:null,
default_max_age:null,
require_auth_time:false,
default_acr_values:null,
initiate_login_uri:null,
post_logout_redirect_uri:null,
request_uris:[],
client_description:null,
registration_access_token:null,
registration_client_uri:null
},
sync: function(method, model, options){
if (model.get('registration_access_token')) {
var headers = options.headers ? options.headers : {};
headers['Authorization'] = 'Bearer ' + model.get('registration_access_token');
options.headers = headers;
}
return this.constructor.__super__.sync(method, model, options);
},
urlRoot:'resource'
});
var ResRegRootView = Backbone.View.extend({
tagName: 'span',
initialize:function() {
},
events:{
"click #newreg":"newReg",
"click #editreg":"editReg"
},
load:function(callback) {
if (this.options.systemScopeList.isFetched) {
callback();
return;
}
$('#loadingbox').sheet('show');
$('#loading').html('<span class="label" id="loading-scopes">Scopes</span> ');
$.when(this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}}))
.done(function() {
$('#loadingbox').sheet('hide');
callback();
});
},
render:function() {
$(this.el).html($('#tmpl-rsreg').html());
return this;
},
newReg:function(e) {
e.preventDefault();
this.remove();
app.navigate('dev/resource/new', {trigger: true});
},
editReg:function(e) {
e.preventDefault();
var clientId = $('#clientId').val();
var token = $('#regtoken').val();
var client = new DynRegClient({
client_id: clientId,
registration_access_token: token
});
var self = this;
client.fetch({success: function() {
var view = new ResRegEditView({model: client, systemScopeList: app.systemScopeList});
view.load(function() {
$('#content').html(view.render().el);
view.delegateEvents();
setPageTitle("Dynamically Register a New Protected Resource");
app.navigate('dev/resource/edit', {trigger: true});
self.remove();
});
}, error: function() {
$('#modalAlert div.modal-body').html("Invalid resource or registration access token.");
$("#modalAlert").modal({ // wire up the actual modal functionality and show the dialog
"backdrop" : "static",
"keyboard" : true,
"show" : true // ensure the modal is shown immediately
});
}});
}
});
var ResRegEditView = Backbone.View.extend({
tagName: 'span',
initialize:function() {
if (!this.template) {
this.template = _.template($('#tmpl-rsreg-resource-form').html());
}
this.redirectUrisCollection = new Backbone.Collection();
this.scopeCollection = new Backbone.Collection();
this.contactsCollection = new Backbone.Collection();
this.defaultAcrValuesCollection = new Backbone.Collection();
this.requestUrisCollection = new Backbone.Collection();
},
load:function(callback) {
if (this.options.systemScopeList.isFetched) {
callback();
return;
}
$('#loadingbox').sheet('show');
$('#loading').html('<span class="label" id="loading-scopes">Scopes</span> ');
$.when(this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}}))
.done(function() {
$('#loadingbox').sheet('hide');
callback();
});
},
events:{
"click .btn-save":"saveClient",
"click .btn-cancel": function() { window.history.back(); return false; },
"click .btn-delete":"deleteClient",
"change #logoUri input":"previewLogo"
},
deleteClient:function (e) {
e.preventDefault();
if (confirm("Are you sure sure you would like to delete this client?")) {
var self = this;
this.model.destroy({
success:function () {
self.remove();
app.navigate('dev/dynreg', {trigger: true});
},
error:function (error, response) {
console.log("An error occurred when deleting a client");
//Pull out the response text.
var responseJson = JSON.parse(response.responseText);
//Display an alert with an error message
$('#modalAlert div.modal-header').html(responseJson.error);
$('#modalAlert div.modal-body').html(responseJson.error_description);
$("#modalAlert").modal({ // wire up the actual modal functionality and show the dialog
"backdrop" : "static",
"keyboard" : true,
"show" : true // ensure the modal is shown immediately
});
}
});
app.clientListView.delegateEvents();
}
return false;
},
previewLogo:function() {
if ($('#logoUri input', this.el).val()) {
$('#logoPreview', this.el).empty();
$('#logoPreview', this.el).attr('src', $('#logoUri input').val());
} else {
$('#logoBlock', this.el).hide();
}
},
disableUnsupportedJOSEItems:function(serverSupported, query) {
var supported = ['default'];
if (serverSupported) {
supported = _.union(supported, serverSupported);
}
$(query, this.$el).each(function(idx) {
if(_.contains(supported, $(this).val())) {
$(this).prop('disabled', false);
} else {
$(this).prop('disabled', true);
}
});
},
// returns "null" if given the value "default" as a string, otherwise returns input value. useful for parsing the JOSE algorithm dropdowns
defaultToNull:function(value) {
if (value == 'default') {
return null;
} else {
return value;
}
},
// maps from a form-friendly name to the real grant parameter name
grantMap:{
'authorization_code': 'authorization_code',
'password': 'password',
'implicit': 'implicit',
'client_credentials': 'client_credentials',
'redelegate': 'urn:ietf:params:oauth:grant_type:redelegate',
'refresh_token': 'refresh_token'
},
// maps from a form-friendly name to the real response type parameter name
responseMap:{
'code': 'code',
'token': 'token',
'idtoken': 'id_token',
'token-idtoken': 'token id_token',
'code-idtoken': 'code id_token',
'code-token': 'code token',
'code-token-idtoken': 'code token id_token'
},
saveClient:function (e) {
e.preventDefault();
$('.control-group').removeClass('error');
// build the scope object
var scopes = this.scopeCollection.pluck("item").join(" ");
// build the grant type object
var grantTypes = [];
$.each(this.grantMap, function(index,type) {
if ($('#grantTypes-' + index).is(':checked')) {
grantTypes.push(type);
}
});
// build the response type object
var responseTypes = [];
$.each(this.responseMap, function(index,type) {
if ($('#responseTypes-' + index).is(':checked')) {
responseTypes.push(type);
}
});
var contacts = this.contactsCollection.pluck('item');
var userInfo = getUserInfo();
if (userInfo && userInfo.email) {
if (!_.contains(contacts, userInfo.email)) {
contacts.push(userInfo.email);
}
}
var attrs = {
client_name:$('#clientName input').val(),
redirect_uris: this.redirectUrisCollection.pluck("item"),
client_description:$('#clientDescription textarea').val(),
logo_uri:$('#logoUri input').val(),
grant_types: grantTypes,
scope: scopes,
tos_uri: $('#tosUri input').val(),
policy_uri: $('#policyUri input').val(),
client_uri: $('#clientUri input').val(),
application_type: $('#applicationType input').filter(':checked').val(),
jwks_uri: $('#jwksUri input').val(),
subject_type: $('#subjectType input').filter(':checked').val(),
token_endpoint_auth_method: $('#tokenEndpointAuthMethod input').filter(':checked').val(),
response_types: responseTypes,
sector_identifier_uri: $('#sectorIdentifierUri input').val(),
initiate_login_uri: $('#initiateLoginUri input').val(),
post_logout_redirect_uri: $('#postLogoutRedirectUri input').val(),
reuse_refresh_token: $('#reuseRefreshToken').is(':checked'),
require_auth_time: $('#requireAuthTime input').is(':checked'),
default_max_age: parseInt($('#defaultMaxAge input').val()),
contacts: contacts,
request_uris: this.requestUrisCollection.pluck('item'),
default_acr_values: this.defaultAcrValuesCollection.pluck('item'),
request_object_signing_alg: this.defaultToNull($('#requestObjectSigningAlg select').val()),
userinfo_signed_response_alg: this.defaultToNull($('#userInfoSignedResponseAlg select').val()),
userinfo_encrypted_response_alg: this.defaultToNull($('#userInfoEncryptedResponseAlg select').val()),
userinfo_encrypted_response_enc: this.defaultToNull($('#userInfoEncryptedResponseEnc select').val()),
id_token_signed_response_alg: this.defaultToNull($('#idTokenSignedResponseAlg select').val()),
id_token_encrypted_response_alg: this.defaultToNull($('#idTokenEncryptedResponseAlg select').val()),
id_token_encrypted_response_enc: this.defaultToNull($('#idTokenEncryptedResponseEnc select').val()),
token_endpoint_auth_signing_alg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val())
};
// set all empty strings to nulls
for (var key in attrs) {
if (attrs[key] === "") {
attrs[key] = null;
}
}
var _self = this;
this.model.save(attrs, {
success:function () {
// switch to an "edit" view
app.navigate('dev/dynreg/edit', {trigger: true});
_self.remove();
var view = new DynRegEditView({model: _self.model, systemScopeList: _self.options.systemScopeList});
view.load(function() {
// reload
$('#content').html(view.render().el);
view.delegateEvents();
});
},
error:function (error, response) {
console.log("An error occurred when deleting from a list widget");
//Pull out the response text.
var responseJson = JSON.parse(response.responseText);
//Display an alert with an error message
$('#modalAlert div.modal-header').html(responseJson.error);
$('#modalAlert div.modal-body').html(responseJson.error_description);
$("#modalAlert").modal({ // wire up the actual modal functionality and show the dialog
"backdrop" : "static",
"keyboard" : true,
"show" : true // ensure the modal is shown immediately
});
}
});
return false;
},
render:function() {
$(this.el).html(this.template({client: this.model.toJSON()}));
var _self = this;
// build and bind registered redirect URI collection and view
_.each(this.model.get("redirectUris"), function (redirectUri) {
_self.redirectUrisCollection.add(new URIModel({item:redirectUri}));
});
$("#redirectUris .controls",this.el).html(new ListWidgetView({
type:'uri',
placeholder: 'https://',
collection: this.redirectUrisCollection}).render().el);
// build and bind scopes
var scopes = this.model.get("scope");
var scopeSet = scopes ? scopes.split(" ") : [];
_.each(scopeSet, function (scope) {
_self.scopeCollection.add(new Backbone.Model({item:scope}));
});
$("#scope .controls",this.el).html(new ListWidgetView({
placeholder: 'new scope',
autocomplete: _.uniq(_.flatten(this.options.systemScopeList.pluck("value"))),
collection: this.scopeCollection}).render().el);
// build and bind contacts
_.each(this.model.get('contacts'), function (contact) {
_self.contactsCollection.add(new Backbone.Model({item:contact}));
});
$("#contacts .controls", this.el).html(new ListWidgetView({
placeholder: 'new contact',
collection: this.contactsCollection}).render().el);
// build and bind request URIs
_.each(this.model.get('requestUris'), function (requestUri) {
_self.requestUrisCollection.add(new URIModel({item:requestUri}));
});
$('#requestUris .controls', this.el).html(new ListWidgetView({
type: 'uri',
placeholder: 'https://',
collection: this.requestUrisCollection}).render().el);
// build and bind default ACR values
_.each(this.model.get('defaultAcrValues'), function (defaultAcrValue) {
_self.defaultAcrValuesCollection.add(new Backbone.Model({item:defaultAcrValue}));
});
$('#defaultAcrValues .controls', this.el).html(new ListWidgetView({
placeholder: 'new ACR value',
// TODO: autocomplete from spec
collection: this.defaultAcrValuesCollection}).render().el);
this.previewLogo();
// disable unsupported JOSE algorithms
this.disableUnsupportedJOSEItems(app.serverConfiguration.request_object_signing_alg_values_supported, '#requestObjectSigningAlg option');
this.disableUnsupportedJOSEItems(app.serverConfiguration.userinfo_signing_alg_values_supported, '#userInfoSignedResponseAlg option');
this.disableUnsupportedJOSEItems(app.serverConfiguration.userinfo_encryption_alg_values_supported, '#userInfoEncryptedResponseAlg option');
this.disableUnsupportedJOSEItems(app.serverConfiguration.userinfo_encryption_enc_values_supported, '#userInfoEncryptedResponseEnc option');
this.disableUnsupportedJOSEItems(app.serverConfiguration.id_token_signing_alg_values_supported, '#idTokenSignedResponseAlg option');
this.disableUnsupportedJOSEItems(app.serverConfiguration.id_token_encryption_alg_values_supported, '#idTokenEncryptedResponseAlg option');
this.disableUnsupportedJOSEItems(app.serverConfiguration.id_token_encryption_enc_values_supported, '#idTokenEncryptedResponseEnc option');
this.disableUnsupportedJOSEItems(app.serverConfiguration.token_endpoint_auth_signing_alg_values_supported, '#tokenEndpointAuthSigningAlg option');
this.$('.nyi').clickover({
placement: 'right',
title: 'Not Yet Implemented',
content: 'The value of this field will be saved with the client, '
+'but the server does not currently process anything with it. '
+'Future versions of the server library will make use of this.'
});
return this;
}
});

View File

@ -67,7 +67,7 @@
<div class="control-group">
<div class="controls">
<div class="alert alert-error">
<strong>Warning!</strong> You MUST protect your your Client ID, Client Secret, and your Registration Access Token. If
<strong>Warning!</strong> You MUST protect your Client ID, Client Secret, and your Registration Access Token. If
you lose your Client ID or Registration Access Token, you will no longer have access to your client's registration
records and you will need to register a new client.
</div>

View File

@ -0,0 +1,301 @@
<!--
Copyright 2014 The MITRE Corporation
and the MIT Kerberos and Internet Trust Consortium
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- self-service resource registration -->
<script type="text/html" id="tmpl-rsreg">
<div class="row-fluid">
<div class="span5">
<button class="btn btn-large" id="newreg">Register a new protected resource</button>
</div>
<div class="span2">
<strong> - OR - </strong>
</div>
<div class="span5">
<input type="text" id="clientId" placeholder="Enter Resource ID">
<input type="text" id="regtoken" placeholder="Enter Registration Access Token">
<button class="btn btn-large" id="editreg">Edit an existing protected resource</button>
<span class="help-block>Paste in your ID and registration access token to access the resource's properties.</span>
</div>
</script>
<script type="text/html" id="tmpl-rsreg-resource-form">
<h1><%=(client.client_id == null ? 'New' : 'Edit')%> Protected Resource</h1>
<form class="form-horizontal tabbable">
<fieldset>
<div class="well well-small">
<button class="btn btn-small btn-save btn-success"><i class="icon-ok-circle icon-white"></i> Save</button> &nbsp;
<button class="btn btn-small btn-cancel"><i class="icon-ban-circle"></i> Cancel</button>
<% if (client.client_id) { %>
<button class="btn btn-danger btn-delete pull-right"><i class="icon-trash icon-white"></i> Delete</button>
<% } %>
</div>
<ul class="nav nav-tabs">
<li class="active"><a data-target="#resource-main-tab" data-toggle="tab" href="#">Main</a></li>
<li><a data-target="#resource-access-tab" data-toggle="tab" href="#">Access</a></li>
<li><a data-target="#resource-secret-tab" data-toggle="tab" href="#">Credentials</a></li>
<li><a data-target="#resource-json-tab" data-toggle="tab" href="#">JSON</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="resource-main-tab">
<% if (client.client_id) { %>
<div class="control-group">
<div class="controls">
<div class="alert alert-error">
<strong>Warning!</strong> You MUST protect your resource ID, Secret, and your Registration Access Token. If
you lose your ID or Registration Access Token, you will no longer have access to your resource's registration
records and you will need to register a new resource.
</div>
</div>
</div>
<div class="control-group" id="clientId">
<label class="control-label">Client ID</label>
<div class="controls">
<pre><%=client.client_id ? client.client_id : '<code>Will be generated</code>'%></pre>
</div>
</div>
<div class="control-group" id="requireClientSecret">
<label class="control-label">Client Secret</label>
<div class="control-group">
<div class="controls">
<pre><%=client.client_id ? (client.client_secret ? client.client_secret : 'None (public client)') : 'Will be generated'%></pre>
</div>
</div>
</div>
<div class="control-group" id="clientConfigurationUri">
<label class="control-label">Client Configuration URL</label>
<div class="controls">
<pre><%=client.registration_client_uri ? client.registration_client_uri : 'Will be generated'%></pre>
</div>
</div>
<div class="control-group" id="registrationAccessToken">
<label class="control-label">Registration Access Token</label>
<div class="controls">
<pre><%=client.registration_access_token ? client.registration_access_token : 'Will be generated'%></pre>
</div>
</div>
<% } else { %>
<div class="control-group" id="clientId">
<label class="control-label">ID</label>
<div class="controls">
<code>Will be generated</code>
</div>
</div>
<div class="control-group" id="requireClientSecret">
<label class="control-label">Secret</label>
<div class="control-group">
<div class="controls">
<code>Will be generated</code>
</div>
</div>
</div>
<div class="control-group" id="clientConfigurationUri">
<label class="control-label">Configuration URL</label>
<div class="controls">
<code>Will be generated</code>
</div>
</div>
<div class="control-group" id="registrationAccessToken">
<label class="control-label">Registration Access Token</label>
<div class="controls">
<code>Will be generated</code>
</div>
</div>
<% } %>
<div class="control-group" id="clientName">
<label class="control-label">Resource name</label>
<div class="controls">
<input value="<%=client.client_name ? client.client_name : ''%>" maxlength="100" type="text" class="" placeholder="Type something">
<p class="help-block">Human-readable application name</p>
</div>
</div>
<div class="control-group" id="clientDescription">
<label class="control-label">Description</label>
<div class="controls">
<textarea class="input-xlarge" placeholder="Type a description" maxlength="200" rows="3"><%=client.client_description ? client.client_description : ''%></textarea>
<p class="help-block">Human-readable text description</p>
</div>
</div>
<div class="control-group" id="logoUri">
<label class="control-label">Logo</label>
<div class="controls">
<input placeholder="https://" value="<%=client.logo_uri ? client.logo_uri : ''%>" maxlength="1000" type="text" class=""/>
<p class="help-block">URL that points to a logo image, will be displayed on approval page</p>
</div>
</div>
<div class="control-group" id="logoBlock">
<div class="controls">
<!-- TODO: this should be an internally-served placeholder graphic -->
<img src="http://placehold.it/275x200&text=Enter a logo URL" alt="logo" id="logoPreview" width="275px" class="thumbnail" />
</div>
</div>
<div class="control-group" id="tosUri">
<label class="control-label">Terms of Service</label>
<div class="controls">
<input placeholder="https://" value="<%=client.tos_uri ? client.tos_uri : ''%>" maxlength="1000" type="text" class=""/>
<p class="help-block">URL for the Terms of Service of this client, will be displayed to the user</p>
</div>
</div>
<div class="control-group" id="policyUri">
<label class="control-label">Policy</label>
<div class="controls">
<input placeholder="https://" value="<%=client.policy_uri ? client.policy_uri : ''%>" maxlength="1000" type="text" class=""/>
<p class="help-block">URL for the Policy Statement of this client, will be displayed to the user</p>
</div>
</div>
<div class="control-group" id="clientUri">
<label class="control-label">Home Page</label>
<div class="controls">
<input placeholder="https://" value="<%=client.client_uri ? client.client_uri : ''%>" maxlength="1000" type="text" class=""/>
<p class="help-block">URL for the client's home page, will be displayed to the user</p>
</div>
</div>
<div class="control-group" id="applicationType">
<label class="control-label"><span class="label label-default nyi"><i class="icon-road icon-white"></i> NYI </span> Application Type</label>
<div class="controls">
<label class="radio inline">
<input type="radio" name="applicationType" value="NATIVE" <%=(client.application_type == 'NATIVE' ? 'checked' : '')%>> Native
</label>
<label class="radio inline">
<input type="radio" name="applicationType" value="WEB" <%=(client.application_type == 'WEB' ? 'checked' : '')%>> Web
</label>
</div>
</div>
<div class="control-group" id="contacts">
<label class="control-label">Contacts</label>
<div class="controls">
</div>
</div>
</div>
<div class="tab-pane" id="resource-access-tab">
<div class="control-group" id="scope">
<label class="control-label">Scope</label>
<div class="controls">
</div>
</div>
</div>
<div class="tab-pane" id="resource-secret-tab">
<div class="control-group" id="tokenEndpointAuthMethod">
<label class="control-label">Introspection Endpoint Authentication Method</label>
<div class="controls">
<label class="radio">
<input type="radio" name="tokenEndpointAuthMethod" value="client_secret_basic" <%=(client.token_endpoint_auth_method == 'client_secret_basic' ? 'checked' : '')%>> Client Secret over HTTP Basic
</label>
<label class="radio">
<input type="radio" name="tokenEndpointAuthMethod" value="client_secret_post" <%=(client.token_endpoint_auth_method == 'client_secret_post' ? 'checked' : '')%>> Client Secret over HTTP POST
</label>
<label class="radio">
<input type="radio" name="tokenEndpointAuthMethod" value="client_secret_jwt" <%=(client.token_endpoint_auth_method == 'client_secret_jwt' ? 'checked' : '')%>> Client Secret via symmetrically-signed JWT assertion
</label>
<label class="radio">
<input type="radio" name="tokenEndpointAuthMethod" value="private_key_jwt" <%=(client.token_endpoint_auth_method == 'private_key_jwt' ? 'checked' : '')%>> Asymmetrically-signed JWT assertion
</label>
<label class="radio">
<input type="radio" name="tokenEndpointAuthMethod" value="none" <%=(client.token_endpoint_auth_method == 'none' ? 'checked' : '')%>> No authentication
</label>
</div>
</div>
<div class="control-group" id="jwksUri">
<label class="control-label">JWK Set</label>
<div class="controls">
<input placeholder="https://" value="<%=client.jwks_uri ? client.jwks_uri : ''%>" maxlength="1000" type="text" class=""/>
<p class="help-block">URL for the client's JSON Web Key set</p>
</div>
</div>
<div class="control-group" id="tokenEndpointAuthSigningAlg">
<label class="control-label">Introspection Endpoint Authentication Signing Algorithm</label>
<div class="controls">
<select>
<option value="default" <%=client.token_endpoint_auth_signing_alg == null ? 'selected ' : ''%>>Any allowed</option>
<option value="HS256" <%=client.token_endpoint_auth_signing_alg == "HS256" ? 'selected' : ''%>>HMAC using SHA-256 hash algorithm</option>
<option value="HS384" <%=client.token_endpoint_auth_signing_alg == "HS384" ? 'selected' : ''%>>HMAC using SHA-384 hash algorithm</option>
<option value="HS512" <%=client.token_endpoint_auth_signing_alg == "HS512" ? 'selected' : ''%>>HMAC using SHA-512 hash algorithm</option>
<option value="RS256" <%=client.token_endpoint_auth_signing_alg == "RS256" ? 'selected' : ''%>>RSASSA using SHA-256 hash algorithm</option>
<option value="RS384" <%=client.token_endpoint_auth_signing_alg == "RS384" ? 'selected' : ''%>>RSASSA using SHA-384 hash algorithm</option>
<option value="RS512" <%=client.token_endpoint_auth_signing_alg == "RS512" ? 'selected' : ''%>>RSASSA using SHA-512 hash algorithm</option>
<option value="ES256" <%=client.token_endpoint_auth_signing_alg == "ES256" ? 'selected' : ''%>>ECDSA using P-256 curve and SHA-256 hash algorithm</option>
<option value="ES384" <%=client.token_endpoint_auth_signing_alg == "ES384" ? 'selected' : ''%>>ECDSA using P-384 curve and SHA-384 hash algorithm</option>
<option value="ES512" <%=client.token_endpoint_auth_signing_alg == "ES512" ? 'selected' : ''%>>ECDSA using P-512 curve and SHA-512 hash algorithm</option>
</select>
</div>
</div>
</div>
<div class="tab-pane" id="resource-json-tab">
<pre>
<%= JSON.stringify(client, undefined, 2) %>
</pre>
</div>
<div class="well well-small">
<button class="btn btn-small btn-save btn-success"><i class="icon-ok-circle icon-white"></i> Save</button> &nbsp;
<button class="btn btn-small btn-cancel"><i class="icon-ban-circle"></i> Cancel</button>
<% if (client.client_id) { %>
<button class="btn btn-danger btn-delete pull-right"><i class="icon-trash icon-white"></i> Delete</button>
<% } %>
</div>
</fieldset>
</form>
</script>