5 [% PROCESS 'i18n.inc' %]
7 [% INCLUDE 'doc-head-open.inc' %]
8 <title>[% FILTER collapse %]
9 [% IF op == 'add_form' %]
10 [% t("New identity provider") | html %] ›
11 [% ELSIF op == 'edit_form' %]
12 [% tx("Modify identity provider '{id_provider}'", {id_provider = identity_provider.code}) | html %] ›
14 [% t("Identity providers") | html %] ›
15 [% t("Administration") | html %] ›
16 [% t("Koha") | html %]
18 [% INCLUDE 'doc-head-close.inc' %]
21 <body id="admin_identity_providers" class="admin">
22 [% INCLUDE 'header.inc' %]
23 [% INCLUDE 'prefs-admin-search.inc' %]
25 [% WRAPPER 'sub-header.inc' %]
26 [% WRAPPER breadcrumbs %]
27 [% WRAPPER breadcrumb_item %]
28 <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a>
31 [% IF op == 'add_form' %]
32 [% WRAPPER breadcrumb_item %]
33 <a href="/cgi-bin/koha/admin/identity_providers.pl">Identity providers</a>
35 [% WRAPPER breadcrumb_item bc_active= 1 %]
36 <span>New identity provider</span>
39 [% ELSIF op == 'edit_form' %]
40 [% WRAPPER breadcrumb_item %]
41 <a href="/cgi-bin/koha/admin/identity_providers.pl">Identity providers</a>
43 [% WRAPPER breadcrumb_item bc_active= 1 %]
44 [% tx("Modify identity provider '{id_provider}'", {id_provider = identity_provider.code}) | html %]
48 [% WRAPPER breadcrumb_item bc_active= 1 %]
49 <span>Identity providers</span>
52 [% END #/ WRAPPER breadcrumbs %]
53 [% END #/ WRAPPER sub-header.inc %]
55 <div class="main container-fluid">
57 <div class="col-sm-10 col-sm-push-2">
60 [% FOREACH m IN messages %]
61 <div class="dialog [% m.type | html %]" id="identity_provider_action_result_dialog">
63 [% CASE 'error_on_update' %]
64 <span>An error occurred trying to open the identity provider for editing. The passed ID is invalid.</span>
65 [% CASE 'error_on_insert' %]
66 <span>An error occurred when adding a new identity provider.</span>
67 [% CASE 'success_on_update' %]
68 <span>Identity provider updated successfully.</span>
69 [% CASE 'success_on_insert' %]
70 <div>Identity provider added successfully.</div>
71 <div>You will need to restart Koha for the provider to work.</div>
73 <span>[% m.code | html %]</span>
78 <div class="dialog message" id="identity_provider_delete_success" style="display: none;"></div>
79 <div class="dialog alert" id="identity_provider_delete_error" style="display: none;"></div>
81 [% IF op == 'add_form' %]
82 <h1>New identity provider</h1>
84 <form action="/cgi-bin/koha/admin/identity_providers.pl" id="add" name="add" class="validated" method="post">
85 <input type="hidden" name="op" value="add" />
86 <fieldset class="rows">
87 <legend id="identity_provider_basic">Basic configuration</legend>
90 <label for="code" class="required">Code: </label>
91 <input type="text" name="code" id="code" size="60" class="required" required="required" />
92 <span class="required">Required</span>
93 <div class="hint">Code that identifies this provider. Only alphanumeric and "_" characters are allowed</div>
96 <label for="description" class="required">Description: </label>
97 <input type="text" name="description" id="description" size="60" class="required" required="required" />
98 <span class="required">Required</span>
99 <div class="hint">User friendly name of this provider</div>
102 <label for="protocol">Protocol: </label>
103 <select name="protocol" id="protocol">
104 <option value="OAuth">OAuth</option>
105 <option value="OIDC">OIDC</option>
106 <!-- Not implemented yet
107 <option value="LDAP">LDAP</option>
108 <option value="CAS">CAS</option>
111 <div class="hint">Choose the protocol this external identity provider uses</div>
116 <fieldset class="rows">
117 <legend id="identity_provider_advanced">Advanced configuration</legend>
120 <label for="config" class="required json">Configuration: </label>
121 <textarea name="config" id="config" class="required" cols="75" rows="10"></textarea>
122 <span class="required">Required</span>
123 <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>
124 <div class="hint more-config" style="display: none">
125 <div>This configuration differs for each protocol.</div>
126 <div>It is recommended to add the default configuration, and then replace with appropriate values</div>
129 <button class="btn btn-ligth defaults" data-default-target="config" id="default-config">Add default OAuth configuration</button>
133 <label for="mapping" class="required json">Mapping: </label>
134 <textarea name="mapping" id="mapping" class="required" cols="75" rows="10"></textarea>
135 <span class="required">Required</span>
136 <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>
137 <div class="hint more-mapping" style="display: none">
138 <div>It is recommended to add the default mapping, and then modify to suit this provider's response</div>
139 <div>Keys represent Koha's fields, and values represent the keys in provider's result</div>
140 <div>For nested values in provider's results, you can use dot separation.</div>
141 <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>
142 <div>If you plan to use auto register feature, either <i>userid</i> or <i>cardnumber</i> must be present in this mapping</div>
145 <button class="btn btn-ligth defaults" data-default-target="mapping" id="default-mapping">Add default OAuth mapping</button>
149 <label for="matchpoint">Matchpoint: </label>
150 <select name="matchpoint" id="matchpoint">
151 <option value="email">Email</option>
152 <option value="userid">User ID</option>
153 <option value="cardnumber">Card number</option>
155 <div class="hint">Koha patron's field that will be used to match provider's user with Koha's</div>
156 <div class="hint">It must be present in mapping</div>
159 <label for="icon_url">Icon URL: </label>
160 <input type="text" name="icon_url" id="icon_url" size="60" />
165 <fieldset class="rows">
166 <legend id="identity_provider_domain">Domain configuration</legend>
169 <label for="domain" class="required">Domain: </label>
170 <input type="text" name="domain" id="domain" class="required" size="60" />
171 <span class="required">Required</span>
172 <div class="hint">Use * for any domain. You can add new domains later on the dedicated admin page.</div>
175 <label for="default_library_id">Default library: </label>
176 <select id="default_library_id" name="default_library_id" class="mandatory">
177 [% PROCESS options_for_libraries libraries => Branches.all( unfiltered => 1, do_not_select_my_library => 1 ) %]
179 <span class="required">Required</span>
180 <div class="hint">Use this library for the patron on auto register</div>
183 <label for="default_category_id">Default category: </label>
184 [% SET categories = Categories.all() %]
185 <select name="default_category_id" id="default_category_id" class="mandatory">
186 [% FOREACH category IN categories %]
187 <option value="[% category.categorycode | html %]">[% category.description | html %]</option>
190 <span class="required">Required</span>
191 <div class="hint">Use this category for the patron on auto register</div>
194 <label for="allow_opac">Allow OPAC: </label>
195 <select name="allow_opac" id="allow_opac">
196 <option value="1">Yes</option>
197 <option value="0" selected="selected">No</option>
199 <div class="hint">Allow OPAC access to users from this domain to login with this identity provider.</div>
202 <label for="allow_staff">Allow staff: </label>
203 <select name="allow_staff" id="allow_staff">
204 <option value="1">Yes</option>
205 <option value="0" selected="selected">No</option>
207 <div class="hint">Allow staff access to users from this domain to login with this identity provider.</div>
210 <label for="auto_register">Auto register: </label>
211 <select name="auto_register" id="auto_register">
212 <option value="1">Yes</option>
213 <option value="0" selected="selected">No</option>
215 <div class="hint">Allow users to auto register on login.</div>
218 <label for="update_on_auth">Update on login: </label>
219 <select name="update_on_auth" id="update_on_auth">
220 <option value="1">Yes</option>
221 <option value="0" selected="selected">No</option>
223 <div class="hint">Update user data on login.</div>
227 <fieldset class="action">
228 <input type="submit" value="Submit" />
229 <a class="cancel" href="/cgi-bin/koha/admin/identity_providers.pl">Cancel</a>
235 [% IF op == 'edit_form' %]
236 <h1>[% tx("Modify identity provider '{id_provider}'", {id_provider = identity_provider.code}) | html %]</h1>
238 <form action="/cgi-bin/koha/admin/identity_providers.pl" id="edit_save" name="edit_save" class="validated" method="post">
239 <input type="hidden" name="op" value="edit_save" />
240 <input type="hidden" name="identity_provider_id" value="[%- identity_provider.identity_provider_id | html -%]" />
241 <fieldset class="rows">
242 <legend id="identity_provider_basic">Basic configuration</legend>
245 <label for="code" class="required">Code: </label>
246 <input type="text" name="code" id="code" size="60" class="required" required="required" value="[%- identity_provider.code | html -%]"/>
247 <span class="required">Required</span>
248 <div class="hint">Code that identifies this provider. Only alphanumeric and "_" characters are allowed</div>
251 <label for="description" class="required">Description: </label>
252 <input type="text" name="description" id="description" size="60" class="required" required="required" value="[%- identity_provider.description | html -%]"/>
253 <span class="required">Required</span>
254 <div class="hint">User friendly name of this provider</div>
257 <label for="protocol">Protocol: </label>
258 <select name="protocol" id="protocol">
259 [% IF identity_provider.protocol == 'OAuth' %]
260 <option value="OAuth" selected="selected">OAuth</option>
261 <option value="OIDC">OIDC</option>
262 <!-- Not implemented yet
263 <option value="LDAP">LDAP</option>
264 <option value="CAS">CAS</option>
267 <option value="OAuth">OAuth</option>
268 <option value="OIDC" selected="selected">OIDC</option>
269 <!-- Not implemented yet
270 <option value="LDAP">LDAP</option>
271 <option value="CAS">CAS</option>
275 <div class="hint">Choose the protocol this external identity provider uses</div>
280 <fieldset class="rows">
281 <legend id="identity_provider_advanced">Advanced configuration</legend>
284 <label for="config" class="required json">Configuration: </label>
285 <textarea name="config" id="config" class="required" cols="75" rows="10">[%- identity_provider.config | html -%]</textarea>
286 <span class="required">Required</span>
287 <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>
288 <div class="hint more-config" style="display: none">
289 <div>This configuration differs for each protocol.</div>
290 <div>It is recommended to add the default configuration, and then replace with appropriate values</div>
293 <button class="btn btn-ligth defaults" data-default-target="config" id="default-config">Add default [%- identity_provider.protocol | html -%] configuration</button>
297 <label for="mapping" class="required json">Mapping: </label>
298 <textarea name="mapping" id="mapping" class="required" cols="75" rows="10">[%- identity_provider.mapping | html -%]</textarea>
299 <span class="required">Required</span>
300 <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>
301 <div class="hint more-mapping" style="display: none">
302 <div>It is recommended to add the default mapping, and then modify to suit this provider's response</div>
303 <div>Keys represent Koha's fields, and values represent the keys in provider's result</div>
304 <div>For nested values in provider's results, you can use dot separation.</div>
305 <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>
306 <div>If you plan to use auto register feature, either <i>userid</i> or <i>cardnumber</i> must be present in this mapping</div>
309 <button class="btn btn-ligth defaults" data-default-target="mapping" id="default-mapping">Add default [%- identity_provider.protocol | html -%] mapping</button>
313 <label for="matchpoint">Matchpoint: </label>
314 <select name="matchpoint" id="matchpoint">
315 [%- IF identity_provider.matchpoint == 'email' -%]
316 <option value="email" selected="selected">Email</option>
318 <option value="email">Email</option>
320 [%- IF identity_provider.matchpoint == 'userid' -%]
321 <option value="userid" selected="selected">User id</option>
323 <option value="userid">User id</option>
325 [%- IF identity_provider.matchpoint == 'cardnumber' -%]
326 <option value="cardnumber" selected="selected">Card number</option>
328 <option value="cardnumber">Card number</option>
331 <div class="hint">Koha patron's field that will be used to match provider's user with Koha's</div>
332 <div class="hint">It must be present in mapping</div>
335 <label for="icon_url">Icon URL: </label>
336 <input type="text" name="icon_url" id="icon_url" size="60" value="[%- identity_provider.icon_url | html -%]"/>
340 <fieldset class="action">
341 <input type="submit" value="Submit" />
342 <a class="cancel" href="/cgi-bin/koha/admin/identity_providers.pl">Cancel</a>
348 [% IF op == 'list' %]
350 <div id="toolbar" class="btn-toolbar">
351 <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>
354 <h1>Identity providers</h1>
355 <div class="page-section">
356 <table id="identity_providers">
362 <th data-class-name="actions noExport">Actions</th>
369 <div id="delete_confirm_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="delete_confirm_modal_label" aria-hidden="true">
370 <div class="modal-dialog">
371 <div class="modal-content">
372 <div class="modal-header">
373 <button type="button" class="closebtn" data-dismiss="modal" aria-hidden="true">×</button>
374 <h3 id="delete_confirm_modal_label">Confirm deletion of identity provider</h3>
376 <div class="modal-body">
377 <div id="delete_confirm_dialog"></div>
379 <div class="modal-footer">
380 <a href="#" class="btn btn-default" id="delete_confirm_modal_button" role="button" data-toggle="modal">Delete</a>
381 <button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>
383 </div> <!-- /.modal-content -->
384 </div> <!-- /.modal-dialog -->
385 </div> <!-- #delete_confirm_modal -->
388 </div> <!-- /.col-sm-10.col-sm-push-2 -->
390 <div class="col-sm-2 col-sm-pull-10">
392 [% INCLUDE 'admin-menu.inc' %]
394 </div> <!-- /.col-sm-2.col-sm-pull-10 -->
395 </div> <!-- /.row -->
398 [% MACRO jsinclude BLOCK %]
399 [% Asset.js("js/admin-menu.js") | $raw %]
400 [% INCLUDE 'datatables.inc' %]
402 $(document).ready(function() {
404 var identity_providers_url = '/api/v1/auth/identity_providers';
405 window.identity_providers = $("#identity_providers").kohaTable({
407 "url": identity_providers_url
410 'emptyTable': '<div class="dialog message">'+_("There are no identity providers defined.")+'</div>'
414 "render": function (data, type, row, meta) {
415 if ( type == 'display' ) {
416 if ( data != null ) {
417 return data.escapeHtml();
433 "data": "description",
443 "data": function( row, type, val, meta ) {
444 var result = '<a class="btn btn-default btn-xs" role="button" href="/cgi-bin/koha/admin/identity_providers.pl?op=edit_form&identity_provider_id='+ encodeURIComponent(row.identity_provider_id) +'"><i class="fa-solid fa-pencil" aria-hidden="true"></i> '+_("Edit")+'</a>'+"\n";
445 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-can" aria-hidden="true"></i> '+_("Delete")+'</a>'+"\n";
446 result += '<a class="btn btn-default btn-xs edit_domains" role="button" href="/cgi-bin/koha/admin/identity_providers.pl?domain_ops=1&identity_provider_id='+ encodeURIComponent(row.identity_provider_id) +'"><i class="fa fa-cog" aria-hidden="true"></i> '+_("Manage domains")+'</a>';
453 createdRow: function (row, data, dataIndex) {
455 $(row).addClass('debug');
460 $('#identity_providers').on( "click", '.delete_identity_provider', function () {
461 var identity_provider_id = $(this).data('auth-provider-id');
462 var identity_provider_code = decodeURIComponent($(this).data('auth-provider-code'));
464 $("#delete_confirm_dialog").html(
465 _("You are about to delete the '%s' identity provider.").format(identity_provider_code)
467 $("#delete_confirm_modal_button").data('auth-provider-id', identity_provider_id);
468 $("#delete_confirm_modal_button").data('auth-provider-code', identity_provider_code);
471 $("#delete_confirm_modal_button").on( "click", function () {
473 var identity_provider_id = $(this).data('auth-provider-id');
474 var identity_provider_code = $(this).data('auth-provider-code');
478 url: identity_providers_url+"/"+identity_provider_id
479 }).success(function() {
480 window.identity_providers.api().ajax.reload(function (data) {
481 $("#identity_provider_action_result_dialog").hide();
482 $("#identity_provider_delete_success").html(_("Server '%s' deleted successfully.").format(identity_provider_code)).show();
484 }).fail(function () {
485 $("#identity_provider_delete_error").html(_("Error deleting server '%s'. Check the logs.").format(identity_provider_code)).show();
486 }).done(function () {
487 $("#delete_confirm_modal").modal('hide');
491 $.validator.addMethod('json', function(value, element) {
492 if (this.optional(element) && value === '') return true;
499 }, _("Not a valid JSON"));
501 $.validator.addMethod('alphanum', function(value, element) {
502 if (this.optional(element) && value === '') return true;
503 return /^[a-zA-Z0-9_]+$/.test(value);
504 }, _("Value must have alphanumeric characters or '_'"));
506 $('#config, #mapping').each(function() {
507 $(this).rules('add', {
513 $('button.more').on('click', function(event) {
514 event.preventDefault();
515 var target = $(this).hide().data('target');
516 $('.more-'+target).show();
519 $('#code').rules('add', {
527 key: "<enter client id>",
528 secret: "<enter client secret>",
529 well_known_url: "<enter openid configuration endpoint>",
530 scope: "openid email"
534 firstname: "given_name",
535 surname: "family_name"
540 key: "<enter client id>",
541 secret: "<enter client secret>",
542 authorize_url: "<enter authorization endpoint>",
543 token_url: "<enter token endpoint>",
544 userinfo_url: "<enter user info endpoint (optional)>",
549 firstname: "given_name",
550 surname: "family_name"
555 $('#protocol').on('change', function() {
556 var protocol = $(this).val();
557 $('#default-config').html(_("Add default %s configuration").format(protocol));
558 $('#default-mapping').html(_("Add default %s mapping").format(protocol));
561 $('button.defaults').on('click', function(event) {
562 event.preventDefault();
563 var target = $(this).data('defaultTarget');
564 if($('#'+target).val() !== '' && !confirm(_("Are you sure you want to replace current %s contents?").format(target))) {
567 var protocol = $('#protocol').val();
568 $('#'+target).val(JSON.stringify(defaults[protocol][target], null, 2));
574 [% INCLUDE 'intranet-bottom.inc' %]