Koha/koha-tmpl/intranet-tmpl/prog/en/modules/admin/identity_providers.tt
David Cook a24f890403
Bug 32138: Fix OIDC default mapping
This patch fixes the OIDC default mapping to have the Koha fields on
the left (ie key) and the OIDC standard claim fields on the right (ie value).

1. Apply the patch
2. Go to http://localhost:8081/cgi-bin/koha/admin/identity_providers.pl?op=add_form
3. Choose "OIDC" for "Protocol"
4. Click "Add default OIDC mapping"
5. Note the following is displayed:
{
  "email": "email",
  "firstname": "given_name",
  "surname": "family_name"
}

Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
2022-11-09 13:40:28 -03:00

579 lines
30 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[% USE raw %]
[% USE Asset %]
[% USE Branches %]
[% USE Categories %]
[% SET footerjs = 1 %]
[% INCLUDE 'doc-head-open.inc' %]
<title>
[% IF op == 'add_form' %]
New identity provider &rsaquo; [% ELSIF op == 'edit_form' %]
Edit identity provider &rsaquo; [% END %]
Identity providers &rsaquo; Administration &rsaquo; Koha
</title>
[% INCLUDE 'doc-head-close.inc' %]
</head>
<body id="admin_identity_providers" class="admin">
[% INCLUDE 'header.inc' %]
[% INCLUDE 'prefs-admin-search.inc' %]
<nav id="breadcrumbs" aria-label="Breadcrumb" class="breadcrumb">
<ol>
<li>
<a href="/cgi-bin/koha/mainpage.pl">Home</a>
</li>
<li>
<a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a>
</li>
[% IF op == 'add_form' %]
<li>
<a href="/cgi-bin/koha/admin/identity_providers.pl">Identity providers</a>
</li>
<li>
<a href="#" aria-current="page">
New
</a>
</li>
[% ELSIF op == 'edit_form' %]
<li>
<a href="/cgi-bin/koha/admin/identity_providers.pl">Identity providers</a>
</li>
<li>
<a href="#" aria-current="page">
Edit
</a>
</li>
[% ELSE %]
<li>
<a href="#" aria-current="page">
Identity providers
</a>
</li>
[% END %]
</ol>
</nav>
<div class="main container-fluid">
<div class="row">
<div class="col-sm-10 col-sm-push-2">
<main>
[% FOREACH m IN messages %]
<div class="dialog [% m.type | html %]" id="identity_provider_action_result_dialog">
[% SWITCH m.code %]
[% CASE 'error_on_update' %]
<span>An error occurred trying to open the identity provider for editing. The passed id is invalid.</span>
[% CASE 'error_on_insert' %]
<span>An error occurred when adding a new identity provider.</span>
[% CASE 'success_on_update' %]
<span>Identity provider updated successfully.</span>
[% CASE 'success_on_insert' %]
<div>Identity provider added successfully.</div>
<div>You will need to restart Koha for the provider to work.</div>
[% CASE %]
<span>[% m.code | html %]</span>
[% END %]
</div>
[% END %]
<div class="dialog message" id="identity_provider_delete_success" style="display: none;"></div>
<div class="dialog alert" id="identity_provider_delete_error" style="display: none;"></div>
[% IF op == 'add_form' %]
<h1>New identity provider</h1>
<div class="page-section">
<form action="/cgi-bin/koha/admin/identity_providers.pl" id="add" name="add" class="validated" method="post">
<input type="hidden" name="op" value="add" />
<fieldset class="rows">
<legend id="identity_provider_basic">Basic configuration</legend>
<ol>
<li>
<label for="code" class="required">Code: </label>
<input type="text" name="code" id="code" size="60" class="required" required="required" />
<span class="required">Required</span>
<div class="hint">Code that identifies this provider. Only alphanumeric and "_" characters are allowed</div>
</li>
<li>
<label for="description" class="required">Description: </label>
<input type="text" name="description" id="description" size="60" class="required" required="required" />
<span class="required">Required</span>
<div class="hint">User friendly name of this provider</div>
</li>
<li>
<label for="protocol">Protocol: </label>
<select name="protocol" id="protocol">
<option value="OAuth">OAuth</option>
<option value="OIDC">OIDC</option>
<!-- Not implemented yet
<option value="LDAP">LDAP</option>
<option value="CAS">CAS</option>
-->
</select>
<div class="hint">Choose the protocol this external identity provider uses</div>
</li>
</ol>
</fieldset>
<fieldset class="rows">
<legend id="identity_provider_advanced">Advanced configuration</legend>
<ol>
<li>
<label for="config" class="required json">Configuration: </label>
<textarea name="config" id="config" class="required" cols="75" rows="10"></textarea>
<span class="required">Required</span>
<div class="hint">Provider's main configuration. <button class="more btn btn-ligth" data-target="config"><i class="fa fa-caret-down"></i> More</button></div>
<div class="hint more-config" style="display: none">
<div>This configuration differs for each protocol.</div>
<div>It is recommended to add the default configuration, and then replace with appropriate values</div>
</div>
<div class="hint">
<button class="btn btn-ligth defaults" data-default-target="config" id="default-config">Add default OAuth configuration</button>
</div>
</li>
<li>
<label for="mapping" class="required json">Mapping: </label>
<textarea name="mapping" id="mapping" class="required" cols="75" rows="10"></textarea>
<span class="required">Required</span>
<div class="hint">Map provider's result to Koha patron's fields. <button class="more btn btn-ligth" data-target="mapping"><i class="fa fa-caret-down"></i> More</button></div>
<div class="hint more-mapping" style="display: none">
<div>It is recommended to add the default mapping, and then modify to suit this provider's response</div>
<div>Keys represent Koha's fields, and values represent the keys in provider's result</div>
<div>For nested values in provider's results, you can use dot separation.</div>
<div>For example, <i>firstname: "users.0.name"</i> will match the 'name' attribute of the first object in the array named 'users', and place it in 'firstname' field</div>
<div>If you plan to use auto register feature, either <i>userid</i> or <i>cardnumber</i> must be present in this mapping</div>
</div>
<div class="hint">
<button class="btn btn-ligth defaults" data-default-target="mapping" id="default-mapping">Add default OAuth mapping</button>
</div>
</li>
<li>
<label for="matchpoint">Matchpoint: </label>
<select name="matchpoint" id="matchpoint">
<option value="email">Email</option>
<option value="userid">User id</option>
<option value="cardnumber">Card number</option>
</select>
<div class="hint">Koha patron's field that will be used to match provider's user with Koha's</div>
<div class="hint">It must be present in mapping</div>
</li>
<li>
<label for="icon_url">Icon URL: </label>
<input type="text" name="icon_url" id="icon_url" size="60" />
</li>
</ol>
</fieldset>
<fieldset class="rows">
<legend id="identity_provider_domain">Domain configuration</legend>
<ol>
<li>
<label for="domain" class="required">Domain: </label>
<input type="text" name="domain" id="domain" class="required" size="60" />
<span class="required">Required</span>
<div class="hint">Use &ast; for any domain. You can add new domains later on the dedicated admin page.</div>
</li>
<li>
<label for="default_library_id">Default library: </label>
<select id="default_library_id" name="default_library_id" class="mandatory">
[% PROCESS options_for_libraries libraries => Branches.all( unfiltered => 1, do_not_select_my_library => 1 ) %]
</select>
<span class="required">Required</span>
<div class="hint">Use this library for the patron on auto register</div>
</li>
<li>
<label for="default_category_id">Default category: </label>
[% SET categories = Categories.all() %]
<select name="default_category_id" id="default_category_id" class="mandatory">
[% FOREACH category IN categories %]
<option value="[% category.categorycode | html %]">[% category.description | html %]</option>
[% END %]
</select>
<span class="required">Required</span>
<div class="hint">Use this category for the patron on auto register</div>
</li>
<li>
<label for="allow_opac">Allow OPAC: </label>
<select name="allow_opac" id="allow_opac">
<option value="1">Yes</option>
<option value="0" selected="selected">No</option>
</select>
<div class="hint">Allow OPAC access to users from this domain to login with this identity provider.</div>
</li>
<li>
<label for="allow_opac">Allow staff: </label>
<select name="allow_staff" id="allow_staff">
<option value="1">Yes</option>
<option value="0" selected="selected">No</option>
</select>
<div class="hint">Allow staff access to users from this domain to login with this identity provider.</div>
</li>
<li>
<label for="auto_register">Auto register: </label>
<select name="auto_register" id="auto_register">
<option value="1">Yes</option>
<option value="0" selected="selected">No</option>
</select>
<div class="hint">Allow users to auto register on login.</div>
</li>
<li>
<label for="update_on_auth">Update on login: </label>
<select name="update_on_auth" id="update_on_auth">
<option value="1">Yes</option>
<option value="0" selected="selected">No</option>
</select>
<div class="hint">Update user data on login.</div>
</ol>
</fieldset>
<fieldset class="action">
<input type="submit" value="Submit" />
<a class="cancel" href="/cgi-bin/koha/admin/identity_providers.pl">Cancel</a>
</fieldset>
</form>
</div>
[% END %]
[% IF op == 'edit_form' %]
<h1>Edit identity provider</h1>
<div class="page-section">
<form action="/cgi-bin/koha/admin/identity_providers.pl" id="edit_save" name="edit_save" class="validated" method="post">
<input type="hidden" name="op" value="edit_save" />
<input type="hidden" name="identity_provider_id" value="[%- identity_provider.identity_provider_id | html -%]" />
<fieldset class="rows">
<legend id="identity_provider_basic">Basic configuration</legend>
<ol>
<li>
<label for="code" class="required">Code: </label>
<input type="text" name="code" id="code" size="60" class="required" required="required" value="[%- identity_provider.code | html -%]"/>
<span class="required">Required</span>
<div class="hint">Code that identifies this provider. Only alphanumeric and "_" characters are allowed</div>
</li>
<li>
<label for="description" class="required">Description: </label>
<input type="text" name="description" id="description" size="60" class="required" required="required" value="[%- identity_provider.description | html -%]"/>
<span class="required">Required</span>
<div class="hint">User friendly name of this provider</div>
</li>
<li>
<label for="protocol">Protocol: </label>
<select name="protocol" id="protocol">
[% IF identity_provider.protocol == 'OAuth' %]
<option value="OAuth" selected="selected">OAuth</option>
<option value="OIDC">OIDC</option>
<!-- Not implemented yet
<option value="LDAP">LDAP</option>
<option value="CAS">CAS</option>
-->
[% ELSE %]
<option value="OAuth">OAuth</option>
<option value="OIDC" selected="selected">OIDC</option>
<!-- Not implemented yet
<option value="LDAP">LDAP</option>
<option value="CAS">CAS</option>
-->
[% END %]
</select>
<div class="hint">Choose the protocol this external identity provider uses</div>
</li>
</ol>
</fieldset>
<fieldset class="rows">
<legend id="identity_provider_advanced">Advanced configuration</legend>
<ol>
<li>
<label for="config" class="required json">Configuration: </label>
<textarea name="config" id="config" class="required" cols="75" rows="10">[%- identity_provider.config | html -%]</textarea>
<span class="required">Required</span>
<div class="hint">Provider's main configuration. <button class="more btn btn-ligth" data-target="config"><i class="fa fa-caret-down"></i> More</button></div>
<div class="hint more-config" style="display: none">
<div>This configuration differs for each protocol.</div>
<div>It is recommended to add the default configuration, and then replace with appropriate values</div>
</div>
<div class="hint">
<button class="btn btn-ligth defaults" data-default-target="config" id="default-config">Add default [%- identity_provider.protocol | html -%] configuration</button>
</div>
</li>
<li>
<label for="mapping" class="required json">Mapping: </label>
<textarea name="mapping" id="mapping" class="required" cols="75" rows="10">[%- identity_provider.mapping | html -%]</textarea>
<span class="required">Required</span>
<div class="hint">Map provider's result to Koha patron's fields. <button class="more btn btn-ligth" data-target="mapping"><i class="fa fa-caret-down"></i> More</button></div>
<div class="hint more-mapping" style="display: none">
<div>It is recommended to add the default mapping, and then modify to suit this provider's response</div>
<div>Keys represent Koha's fields, and values represent the keys in provider's result</div>
<div>For nested values in provider's results, you can use dot separation.</div>
<div>For example, <i>firstname: "users.0.name"</i> will match the 'name' attribute of the first object in the array named 'users', and place it in 'firstname' field</div>
<div>If you plan to use auto register feature, either <i>userid</i> or <i>cardnumber</i> must be present in this mapping</div>
</div>
<div class="hint">
<button class="btn btn-ligth defaults" data-default-target="mapping" id="default-mapping">Add default [%- identity_provider.protocol | html -%] mapping</button>
</div>
</li>
<li>
<label for="matchpoint">Matchpoint: </label>
<select name="matchpoint" id="matchpoint">
[%- IF identity_provider.matchpoint == 'email' -%]
<option value="email" selected="selected">Email</option>
[%- ELSE -%]
<option value="email">Email</option>
[%- END -%]
[%- IF identity_provider.matchpoint == 'userid' -%]
<option value="userid" selected="selected">User id</option>
[%- ELSE -%]
<option value="userid">User id</option>
[%- END -%]
[%- IF identity_provider.matchpoint == 'cardnumber' -%]
<option value="cardnumber" selected="selected">Card number</option>
[%- ELSE -%]
<option value="cardnumber">Card number</option>
[%- END -%]
</select>
<div class="hint">Koha patron's field that will be used to match provider's user with Koha's</div>
<div class="hint">It must be present in mapping</div>
</li>
<li>
<label for="icon_url">Icon URL: </label>
<input type="text" name="icon_url" id="icon_url" size="60" value="[%- identity_provider.icon_url | html -%]"/>
</li>
</ol>
</fieldset>
<fieldset class="action">
<input type="submit" value="Submit" />
<a class="cancel" href="/cgi-bin/koha/admin/identity_providers.pl">Cancel</a>
</fieldset>
</form>
</div>
[% END %]
[% IF op == 'list' %]
<div id="toolbar" class="btn-toolbar">
<a class="btn btn-default" id="new_identity_provider" href="/cgi-bin/koha/admin/identity_providers.pl?op=add_form"><i class="fa fa-plus"></i> New identity provider</a>
</div>
<h1>Identity providers</h1>
<div class="page-section">
<table id="identity_providers">
<thead>
<tr>
<th>Code</th>
<th>Description</th>
<th>Protocol</th>
<th data-class-name="actions noExport">Actions</th>
</tr>
</thead>
</table>
</div>
[% END %]
<div id="delete_confirm_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="delete_confirm_modal_label" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="closebtn" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="delete_confirm_modal_label">Delete identity provider</h3>
</div>
<div class="modal-body">
<div id="delete_confirm_dialog"></div>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-default" id="delete_confirm_modal_button" role="button" data-toggle="modal">Delete</a>
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>
</div>
</div> <!-- /.modal-content -->
</div> <!-- /.modal-dialog -->
</div> <!-- #delete_confirm_modal -->
</main>
</div> <!-- /.col-sm-10.col-sm-push-2 -->
<div class="col-sm-2 col-sm-pull-10">
<aside>
[% INCLUDE 'admin-menu.inc' %]
</aside>
</div> <!-- /.col-sm-2.col-sm-pull-10 -->
</div> <!-- /.row -->
[% MACRO jsinclude BLOCK %]
[% Asset.js("js/admin-menu.js") | $raw %]
[% INCLUDE 'datatables.inc' %]
<script>
$(document).ready(function() {
var identity_providers_url = '/api/v1/auth/identity_providers';
window.identity_providers = $("#identity_providers").kohaTable({
"ajax": {
"url": identity_providers_url
},
'language': {
'emptyTable': '<div class="dialog message">'+_("There are no identity providers defined.")+'</div>'
},
"columnDefs": [ {
"targets": [0,1,2],
"render": function (data, type, row, meta) {
if ( type == 'display' ) {
if ( data != null ) {
return data.escapeHtml();
}
else {
return "Default";
}
}
return data;
}
} ],
"columns": [
{
"data": "code",
"searchable": true,
"orderable": true
},
{
"data": "description",
"searchable": true,
"orderable": true
},
{
"data": "protocol",
"searchable": true,
"orderable": true
},
{
"data": function( row, type, val, meta ) {
var result = '<a class="btn btn-default btn-xs" role="button" href="/cgi-bin/koha/admin/identity_providers.pl?op=edit_form&amp;identity_provider_id='+ encodeURIComponent(row.identity_provider_id) +'"><i class="fa fa-pencil" aria-hidden="true"></i> '+_("Edit")+'</a>'+"\n";
result += '<a class="btn btn-default btn-xs delete_identity_provider" role="button" href="#" data-toggle="modal" data-target="#delete_confirm_modal" data-auth-provider-id="'+ encodeURIComponent(row.identity_provider_id) +'" data-auth-provider-code="'+ encodeURIComponent(row.code.escapeHtml()) +'"><i class="fa fa-trash" aria-hidden="true"></i> '+_("Delete")+'</a>'+"\n";
result += '<a class="btn btn-default btn-xs edit_domains" role="button" href="/cgi-bin/koha/admin/identity_providers.pl?domain_ops=1&amp;identity_provider_id='+ encodeURIComponent(row.identity_provider_id) +'"><i class="fa fa-cog" aria-hidden="true"></i> '+_("Manage Domains")+'</a>';
return result;
},
"searchable": false,
"orderable": false
}
],
createdRow: function (row, data, dataIndex) {
if ( data.debug ) {
$(row).addClass('debug');
}
},
});
$('#identity_providers').on( "click", '.delete_identity_provider', function () {
var identity_provider_id = $(this).data('auth-provider-id');
var identity_provider_code = decodeURIComponent($(this).data('auth-provider-code'));
$("#delete_confirm_dialog").html(
_("You are about to delete the '%s' identity provider.").format(identity_provider_code)
);
$("#delete_confirm_modal_button").data('auth-provider-id', identity_provider_id);
$("#delete_confirm_modal_button").data('auth-provider-code', identity_provider_code);
});
$("#delete_confirm_modal_button").on( "click", function () {
var identity_provider_id = $(this).data('auth-provider-id');
var identity_provider_code = $(this).data('auth-provider-code');
$.ajax({
method: "DELETE",
url: identity_providers_url+"/"+identity_provider_id
}).success(function() {
window.identity_providers.api().ajax.reload(function (data) {
$("#identity_provider_action_result_dialog").hide();
$("#identity_provider_delete_success").html(_("Server '%s' deleted successfully.").format(identity_provider_code)).show();
});
}).fail(function () {
$("#identity_provider_delete_error").html(_("Error deleting server '%s'. Check the logs.").format(identity_provider_code)).show();
}).done(function () {
$("#delete_confirm_modal").modal('hide');
});
});
$.validator.addMethod('json', function(value, element) {
if (this.optional(element) && value === '') return true;
try {
JSON.parse(value)
} catch (error) {
return false;
}
return true;
}, _("Not a valid JSON"));
$.validator.addMethod('alphanum', function(value, element) {
if (this.optional(element) && value === '') return true;
return /^[a-zA-Z0-9_]+$/.test(value);
}, _("Value must have alphanumeric characters or '_'"));
$('#config, #mapping').each(function() {
$(this).rules('add', {
required: true,
json: true
});
});
$('button.more').on('click', function(event) {
event.preventDefault();
var target = $(this).hide().data('target');
$('.more-'+target).show();
});
$('#code').rules('add', {
alphanum: true,
required: true
});
var defaults = {
OIDC: {
config: {
key: "<enter client id>",
secret: "<enter client secret>",
well_known_url: "<enter openid configuration endpoint>",
scope: "openid email"
},
mapping: {
email: "email",
firstname: "given_name",
surname: "family_name"
}
},
OAuth: {
config: {
key: "<enter client id>",
secret: "<enter client secret>",
authorize_url: "<enter authorization endpoint>",
token_url: "<enter token endpoint>",
userinfo_url: "<enter user info endpoint (optional)>",
scope: "email"
},
mapping: {
email: "email",
firstname: "given_name",
surname: "family_name"
}
}
};
$('#protocol').on('change', function() {
var protocol = $(this).val();
$('#default-config').html(_("Add default %s configuration").format(protocol));
$('#default-mapping').html(_("Add default %s mapping").format(protocol));
});
$('button.defaults').on('click', function(event) {
event.preventDefault();
var target = $(this).data('defaultTarget');
if($('#'+target).val() !== '' && !confirm(_("Are you sure you want to replace current %s contents?").format(target))) {
return;
}
var protocol = $('#protocol').val();
$('#'+target).val(JSON.stringify(defaults[protocol][target], null, 2));
})
});
</script>
[% END %]
[% INCLUDE 'intranet-bottom.inc' %]