summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Fearn <jfearn@redhat.com>2020-05-07 11:41:39 +1000
committerJeff Fearn <jfearn@redhat.com>2020-05-07 11:41:39 +1000
commit353af77714fd29aeab330255808636a9088eecc2 (patch)
treeecaadcb99519d2b53a96e7dfe9661b498156827a /extensions/AuthJWT
parentBumped version to 5.0.4 (diff)
downloadbugzilla-353af77714fd29aeab330255808636a9088eecc2.tar.gz
bugzilla-353af77714fd29aeab330255808636a9088eecc2.tar.bz2
bugzilla-353af77714fd29aeab330255808636a9088eecc2.zip
Bug 478886 - Open Source Red Hat BugzillaRelease-5.0.4-rh44
Import Red Hat Bugzilla changes.
Diffstat (limited to 'extensions/AuthJWT')
-rw-r--r--extensions/AuthJWT/Config.pm18
-rw-r--r--extensions/AuthJWT/Extension.pm140
-rw-r--r--extensions/AuthJWT/lib/Login.pm130
-rw-r--r--extensions/AuthJWT/lib/Source.pm178
-rw-r--r--extensions/AuthJWT/lib/Util.pm47
-rw-r--r--extensions/AuthJWT/template/en/default/authjwt/README16
-rw-r--r--extensions/AuthJWT/template/en/default/hook/README5
-rw-r--r--extensions/AuthJWT/template/en/default/hook/admin/admin-end_links_left.html.tmpl8
-rw-r--r--extensions/AuthJWT/template/en/default/hook/global/user-error-errors.html.tmpl23
-rw-r--r--extensions/AuthJWT/template/en/default/pages/authjwt/settings.html.tmpl110
10 files changed, 675 insertions, 0 deletions
diff --git a/extensions/AuthJWT/Config.pm b/extensions/AuthJWT/Config.pm
new file mode 100644
index 000000000..f38ff40f1
--- /dev/null
+++ b/extensions/AuthJWT/Config.pm
@@ -0,0 +1,18 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package Bugzilla::Extension::AuthJWT;
+
+use 5.10.1;
+use strict;
+use warnings;
+
+use constant NAME => 'AuthJWT';
+
+use constant REQUIRED_MODULES =>
+ [{package => 'Crypt-JWT', module => 'Crypt::JWT', version => 0},];
+
+use constant OPTIONAL_MODULES => [];
+
+__PACKAGE__->NAME;
diff --git a/extensions/AuthJWT/Extension.pm b/extensions/AuthJWT/Extension.pm
new file mode 100644
index 000000000..c8d6d2d45
--- /dev/null
+++ b/extensions/AuthJWT/Extension.pm
@@ -0,0 +1,140 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package Bugzilla::Extension::AuthJWT;
+
+use 5.10.1;
+use strict;
+use warnings;
+
+use parent qw(Bugzilla::Extension);
+
+# This code for this is in ../extensions/AuthJWT/lib/Util.pm
+use Bugzilla::Extension::AuthJWT::Util;
+use Bugzilla::Extension::AuthJWT::Source;
+
+our $VERSION = '0.01';
+
+sub db_schema_abstract_schema {
+ my ($self, $args) = @_;
+
+ my $schema = $args->{schema};
+
+ $schema->{authjwt_source} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1},
+ kid => {TYPE => 'LONGTEXT', NOTNULL => 1},
+ cert => {TYPE => 'LONGTEXT', NOTNULL => 1},
+ comment => {TYPE => 'LONGTEXT', NOTNULL => 1, DEFAULT => "''"},
+ isactive => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE'},
+ ],
+ INDEXES => [authjwt_source_kid_idx => {FIELDS => [qw(kid)], TYPE => 'UNIQUE'},],
+ };
+
+ # authjwt groups, specifies who is NOT allowed to use it!
+ $schema->{authjwt_groups} = {
+ FIELDS => [
+ authjwt_source_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'authjwt_source', COLUMN => 'id', DELETE => 'CASCADE'}
+ },
+ group_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'groups', COLUMN => 'id', DELETE => 'CASCADE'}
+ },
+ ],
+ INDEXES => [
+ authjwt_authjwt_groups_idx =>
+ {FIELDS => [qw(authjwt_source_id group_id)], TYPE => 'UNIQUE'},
+ ],
+ };
+
+ return;
+}
+
+sub auth_login_methods {
+ my ($self, $args) = @_;
+ my $modules = $args->{'modules'};
+ if (exists $modules->{'AuthJWT'}) {
+ $modules->{'AuthJWT'} = 'Bugzilla/Extension/AuthJWT/Login.pm';
+ }
+
+ return;
+}
+
+sub page_before_template {
+ my ($self, $args) = @_;
+
+ my $page = $args->{page_id};
+ my $vars = $args->{vars};
+ my $cgi = Bugzilla->cgi;
+ my $p = $cgi->Vars;
+
+ # If we aren't viewing a AuthJWT page, we don't need to do anything
+ return unless ($page =~ m{^authjwt/});
+
+ if ($page eq 'authjwt/settings.html') {
+ my $action = $cgi->param('action') || "";
+ my $params;
+ foreach my $key (keys(%$p)) {
+ next if ($key eq 'id');
+ $params->{$key} = $p->{$key};
+ }
+ delete($params->{action});
+
+ if ($action eq 'create') {
+ $params->{kid} = delete($params->{source_kid});
+ my $groups = delete $params->{groups};
+ my $source = Bugzilla::Extension::AuthJWT::Source->create($params);
+ $source->set_groups($groups);
+ $source->update();
+ }
+ elsif ($action eq 'save') {
+ $params->{isactive} = 0 unless defined $params->{isactive};
+ my $id = delete($params->{source_id});
+ $params->{kid} = delete($params->{source_kid});
+ my $groups = delete $params->{groups};
+ my $source = Bugzilla::Extension::AuthJWT::Source->new($id);
+ $source->set_all($params);
+ $source->set_groups($groups);
+ $source->update();
+ }
+ elsif ($action eq 'delete') {
+ my $id = delete($params->{source_id});
+ my $source = Bugzilla::Extension::AuthJWT::Source->new($id);
+ $source->remove_from_db();
+ }
+
+ $vars->{'groups'} = [Bugzilla::Group->get_all];
+ $args->{vars}->{sources} = Bugzilla::Extension::AuthJWT::Source->match();
+ $args->{vars}->{'doc_section'}
+ = 'extensions/AuthJWT/index-admin.html#bugzilla-extension-authjwt-identity-providers';
+
+ }
+ else {
+ ThrowUserError('authjwt_no_such_page');
+ }
+
+ return;
+}
+
+__PACKAGE__->NAME;
+
+__END__
+
+=head1 Name
+
+Bugzilla::Extension::AuthJWT - JWT authentication support for Bugzilla
+
+=head1 Version
+
+Version 0.01
+
+=head1 Description
+
+This module will allow you to set up your Bugzilla server as an SP and configure
+multiple Sources to authenticate against.
+
diff --git a/extensions/AuthJWT/lib/Login.pm b/extensions/AuthJWT/lib/Login.pm
new file mode 100644
index 000000000..66b225d1b
--- /dev/null
+++ b/extensions/AuthJWT/lib/Login.pm
@@ -0,0 +1,130 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package Bugzilla::Extension::AuthJWT::Login;
+use 5.10.1;
+use strict;
+use warnings;
+use base qw(Bugzilla::Auth::Login);
+
+use Bugzilla;
+use Bugzilla::Constants
+ qw(AUTH_NODATA AUTH_ERROR USAGE_MODE_BROWSER AUTH_NO_SUCH_USER AUTH_LOCKOUT AUTH_LOGINFAILED);
+use Bugzilla::Util;
+
+use Bugzilla::Error;
+use Bugzilla::Token;
+
+use Crypt::JWT qw(decode_jwt);
+use Bugzilla::Extension::AuthJWT::Util qw(sources_kids user_can_use);
+use Data::Dumper;
+use File::Basename;
+
+use constant {
+ requires_verification => 0,
+ user_can_create_account => 0,
+ extern_id_used => 0,
+ can_change_email => 0,
+ requires_persistence => 0
+};
+
+# This method is only available to web services. A JWT key can never
+# be used to authenticate a Web request.
+sub get_login_info {
+ my ($self) = @_;
+ my $params = Bugzilla->input_params;
+ my $cgi = Bugzilla->cgi;
+
+ my ($jwt);
+
+ if (!i_am_webservice()) {
+ return {failure => AUTH_NODATA};
+ }
+
+ # Only allow JWTs on JSONRPC
+ unless (basename($0) eq 'jsonrpc.cgi') {
+ return {failure => AUTH_NODATA};
+ }
+
+ if ($ENV{MOD_PERL}) {
+
+ # use require as we may not be running in mod_perl mode.
+ require Apache2::RequestUtil;
+ require Apache2::Connection;
+
+ my $req = Apache2::RequestUtil->request;
+ $jwt = $req->headers_in->{'Authorization'};
+ }
+ else {
+ # You need to expose this header in Apache
+ # RewriteRule ^jsonrpc.cgi - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+ $jwt = $ENV{HTTP_AUTHORIZATION};
+ }
+
+ my $origin = $cgi->http('Origin');
+
+ # If we aren't using a JWT, or there is no origin, bail out
+ if (!$jwt || !$origin || $origin eq '') {
+ return {failure => AUTH_NODATA};
+ }
+
+ # If we are an XHR and there is no JWT explode
+ if ("$origin/" ne Bugzilla->params->{'urlbase'} && !$jwt) {
+ return ({
+ failure => AUTH_ERROR,
+ user_error => 'authjwt_invalid_xhr',
+ details => {origin => $origin}
+ });
+ }
+
+ $jwt =~ s/^Bearer\s*//;
+
+ my $kid_keys = sources_kids();
+
+ my ($header, $token);
+ eval {
+ ($header, $token)
+ = decode_jwt(token => $jwt, kid_keys => $kid_keys, decode_header => 1);
+ };
+
+ if ($@ || !$header->{kid}) {
+ return ({
+ failure => AUTH_ERROR,
+ user_error => 'authjwt_invalid_jwt',
+ details => {error => $@ // 'No JWT KID header detected'}
+ });
+ }
+
+## BUGBUG some JWTs have a valid-for origin, should we validate $origin against that?
+
+ my $field = 'email';
+ my $val = $token->{$field};
+ unless ($val && $val ne '') {
+ return ({
+ failure => AUTH_ERROR,
+ user_error => 'authjwt_missing_mapfield',
+ details => {field => $field}
+ });
+ }
+
+ my $user;
+ $user = Bugzilla::User->new({name => $val});
+ unless ($user) {
+ return {failure => AUTH_NO_SUCH_USER};
+ }
+
+ if ($user->account_is_locked_out) {
+ return {failure => AUTH_LOCKOUT, user => $user};
+ }
+
+ # enforce group restrictions
+ unless (user_can_use($header->{kid}, $user)) {
+ return {failure => AUTH_ERROR, user_error => 'authjwt_user_not_allowed'};
+ }
+
+ return {user_id => $user->id};
+}
+
+1;
+
diff --git a/extensions/AuthJWT/lib/Source.pm b/extensions/AuthJWT/lib/Source.pm
new file mode 100644
index 000000000..3d82f4d68
--- /dev/null
+++ b/extensions/AuthJWT/lib/Source.pm
@@ -0,0 +1,178 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package Bugzilla::Extension::AuthJWT::Source;
+
+use strict;
+use warnings;
+use 5.10.1;
+use Scalar::Util qw(blessed);
+
+use Bugzilla::Constants;
+use Bugzilla::Field;
+use Bugzilla::Util;
+use Bugzilla::Error;
+
+use base qw(Bugzilla::Object);
+
+use Bugzilla::Extension::AuthJWT::Util;
+
+use constant DB_TABLE => 'authjwt_source';
+use constant NAME_FIELD => 'kid';
+use constant LIST_ORDER => 'kid';
+
+use constant DB_COLUMNS => qw(
+ id
+ kid
+ cert
+ comment
+ isactive
+);
+
+use constant UPDATE_COLUMNS => qw(
+ kid
+ cert
+ comment
+ isactive
+);
+
+use constant VALIDATORS => {
+ kid => \&_check_cert,
+ cert => \&_check_cert,
+ comment => \&_check_cert,
+ isactive => \&Bugzilla::Object::check_boolean,
+};
+
+###############################
+#### Validators ####
+###############################
+
+sub _check_cert {
+ my ($invocant, $cert) = @_;
+ $cert = trim($cert);
+ return ($cert);
+}
+
+###############################
+#### Methods ####
+###############################
+
+sub set_kid { $_[0]->set('kid', $_[1]); return; }
+sub set_cert { $_[0]->set('cert', $_[1]); return; }
+sub set_comment { $_[0]->set('comment', $_[1]); return; }
+sub set_isactive { $_[0]->set('isactive', $_[1]); return; }
+
+# groups specify who is NOT allowed to use it!
+sub set_groups {
+ my $self = shift;
+ my $group_ids = shift;
+
+ my @groups;
+ if (ref $group_ids) {
+ @groups = @$group_ids;
+ }
+ else {
+ @groups = ($group_ids);
+ }
+
+ my $dbh = Bugzilla->dbh;
+
+ $dbh->do(q{DELETE FROM authjwt_groups WHERE authjwt_source_id = ?},
+ undef, $self->id);
+
+ my $sth = $dbh->prepare(q{INSERT INTO authjwt_groups VALUES (?, ?)});
+
+ foreach my $group_id (@groups) {
+ next unless $group_id && $group_id =~ /(\d+)/;
+ my $id = $1;
+ my $group = Bugzilla::Group->new($id);
+ $sth->execute($self->id, $id) if ($group);
+ }
+
+ delete $self->{groups};
+
+ return;
+}
+
+###############################
+#### Accessors ####
+###############################
+
+sub id { return ($_[0]->{id}); }
+sub kid { return ($_[0]->{kid}); }
+sub cert { return ($_[0]->{cert}); }
+sub comment { return ($_[0]->{comment}); }
+sub isactive { return ($_[0]->{isactive}); }
+
+sub groups {
+ my $self = shift;
+
+ unless ($self->{groups}) {
+ my $dbh = Bugzilla->dbh;
+
+ my $ids
+ = $dbh->selectcol_arrayref(
+ q{SELECT DISTINCT group_id FROM authjwt_groups WHERE authjwt_source_id = ?},
+ undef, $self->id);
+
+ $self->{groups} = $ids;
+ }
+ return ($self->{groups});
+}
+
+1;
+
+__END__
+
+=head1 Description
+
+Bugzilla::Extension::AuthJWT::Source - A module for encapsulating Sources of JWTs.
+
+=head1 Fields
+
+=over 4
+
+=item id
+
+The index for this Source in the database.
+
+=item kid
+
+The key ID to validate on.
+
+=item cert
+
+The public certificate to validate this kid.
+
+=back
+
+=head1 Accessors
+
+These methods allow you to get the specified field for the JWT Source.
+
+=over 4
+
+=item id
+
+=item kid
+
+=item cert
+
+=item comment
+
+=back
+
+=head1 Methods
+
+These methods allow you to set the specified field for the JWT Source.
+
+=over 4
+
+=item set_kid
+
+=item set_cert
+
+=item set comment
+
+=back
diff --git a/extensions/AuthJWT/lib/Util.pm b/extensions/AuthJWT/lib/Util.pm
new file mode 100644
index 000000000..0e613eb9d
--- /dev/null
+++ b/extensions/AuthJWT/lib/Util.pm
@@ -0,0 +1,47 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package Bugzilla::Extension::AuthJWT::Util;
+
+use 5.10.1;
+use strict;
+use warnings;
+
+use Bugzilla::Extension::AuthJWT::Source;
+use JSON;
+use Bugzilla::User;
+
+use parent qw(Exporter);
+our @EXPORT = qw(
+ sources_kids
+ user_can_use
+);
+
+# This file can be loaded by your extension via
+# "use Bugzilla::Extension::AuthJWT::Util". You can put functions
+# used by your extension in here. (Make sure you also list them in
+# @EXPORT.)
+
+sub sources_kids {
+ my %sources;
+
+ foreach my $src (@{Bugzilla::Extension::AuthJWT::Source->match()}) {
+ $sources{$src->kid} = $src->cert;
+ }
+
+ my $sources = encode_json(\%sources);
+ return ($sources);
+}
+
+sub user_can_use {
+ my ($kid, $user) = @_;
+
+ my $can = 1;
+ my $source = Bugzilla::Extension::AuthJWT::Source->check({kid => $kid});
+ $can = 0 if (any { $user->in_group_id($_->id) } @{$source->groups()});
+
+ return ($can);
+}
+
+1;
diff --git a/extensions/AuthJWT/template/en/default/authjwt/README b/extensions/AuthJWT/template/en/default/authjwt/README
new file mode 100644
index 000000000..4e13121ed
--- /dev/null
+++ b/extensions/AuthJWT/template/en/default/authjwt/README
@@ -0,0 +1,16 @@
+Normal templates go in this directory. You can load them in your
+code like this:
+
+use Bugzilla::Error;
+my $template = Bugzilla->template;
+$template->process('authjwt/some-template.html.tmpl')
+ or ThrowTemplateError($template->error());
+
+That would be how to load a file called <kbd>some-template.html.tmpl</kbd> that
+was in this directory.
+
+Note that you have to be careful that the full path of your template
+never conflicts with a template that exists in Bugzilla or in
+another extension, or your template might override that template. That's why
+we created this directory called 'authjwt' for you, so you
+can put your templates in here to help avoid conflicts. \ No newline at end of file
diff --git a/extensions/AuthJWT/template/en/default/hook/README b/extensions/AuthJWT/template/en/default/hook/README
new file mode 100644
index 000000000..e6c4add58
--- /dev/null
+++ b/extensions/AuthJWT/template/en/default/hook/README
@@ -0,0 +1,5 @@
+Template hooks go in this directory. Template hooks are called in normal
+Bugzilla templates like [% Hook.process('some-hook') %].
+More information about them can be found in the documentation of
+Bugzilla::Extension. (Do "perldoc Bugzilla::Extension" from the main
+Bugzilla directory to see that documentation.) \ No newline at end of file
diff --git a/extensions/AuthJWT/template/en/default/hook/admin/admin-end_links_left.html.tmpl b/extensions/AuthJWT/template/en/default/hook/admin/admin-end_links_left.html.tmpl
new file mode 100644
index 000000000..8a9311444
--- /dev/null
+++ b/extensions/AuthJWT/template/en/default/hook/admin/admin-end_links_left.html.tmpl
@@ -0,0 +1,8 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #%]
+
+[% class = user.in_group('editcomponents') ? "" : "forbidden" %]
+<dt class="[% class FILTER html %]"><a href="page.cgi?id=authjwt/settings.html">JWT Source Settings</a></dt>
+<dd class="[% class FILTER html %]">Administration of AuthJWT Sources.</dd>
diff --git a/extensions/AuthJWT/template/en/default/hook/global/user-error-errors.html.tmpl b/extensions/AuthJWT/template/en/default/hook/global/user-error-errors.html.tmpl
new file mode 100644
index 000000000..267edb62e
--- /dev/null
+++ b/extensions/AuthJWT/template/en/default/hook/global/user-error-errors.html.tmpl
@@ -0,0 +1,23 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #%]
+
+[% IF error == "authjwt_user_not_allowed" %]
+ [% title = "Permission denied" %]
+ The user is not permitted to use this JWT source. Usually due to
+ group restrictions.
+[% ELSIF error == "authjwt_invalid_jwt" %]
+ [% title = "Invalid JWT" %]
+ An invalid JWT was provided. [% error FILTER html %]
+[% ELSIF error == "authjwt_invalid_xhr" %]
+ [% title = "Invalid XHR" %]
+ ALERT An invalid XHR request was detected from [% origin FILTER html %]
+[% ELSIF error == "authjwt_missing_mapfield" %]
+ [% title = "Missing map field" %]
+ The JWT was missing the expected mapping field '[% field FILTER html %]'.
+[% ELSIF error == "authjwt_no_such_page" %]
+ [% title = "Invalid Page Selected" %]
+ The page you tried to load is not supported.
+[% END %]
+
diff --git a/extensions/AuthJWT/template/en/default/pages/authjwt/settings.html.tmpl b/extensions/AuthJWT/template/en/default/pages/authjwt/settings.html.tmpl
new file mode 100644
index 000000000..ad2bb8ec6
--- /dev/null
+++ b/extensions/AuthJWT/template/en/default/pages/authjwt/settings.html.tmpl
@@ -0,0 +1,110 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #%]
+
+[%# INTERFACE:
+ # settings: array of product objects
+ #%]
+
+[% PROCESS global/variables.none.tmpl %]
+
+[% PROCESS global/header.html.tmpl
+ title = "Edit AuthJWT Sources"
+ h1 = "This lets you add, edit, or delete Sources for JWTs"
+ style = "table {width: 100%;} table tr {vertical-align: top;} #source_kid {width: 32em}"
+%]
+
+<h2>AuthJWT Sources</h2>
+ <table id="jwt_admin_table">
+ <thead>
+ <tr>
+ <th>kid</th>
+ <th>Cert</th>
+ <th title="Adding groups here will prevent them from using this IDP">Exclude Groups</th>
+ <th>Comment</th>
+ <th>Active</th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ [% FOREACH source IN sources %]
+ <tr>
+<form method="POST" action="page.cgi?id=authjwt/settings.html">
+ <td>
+ <input type="hidden" name="source_id" value="[% source.id FILTER html %]">
+ <input name="source_kid" id="source_kid" value="[% source.kid FILTER html %]">
+ </td>
+ <td><textarea cols="64" rows="10" wrap="soft" name="cert" id="cert">[% source.cert FILTER html %]</textarea></td>
+ <td>
+ <select name="groups" id="groups" multiple="multiple">
+ [% FOREACH group IN groups %]
+ <option value="[% group.id FILTER html %]" [% IF source.groups.contains(group.id) %] selected="selected"[% END %]>
+ [% group.name FILTER html %]
+ </option>
+ [% END %]
+ </select>
+ </td>
+ <td><input name="comment" id="comment" value="[% source.comment FILTER html %]"></td>
+ <td><input type="checkbox" name="isactive" id="isactive"[% ' checked="checked"' IF source.isactive %]></td>
+ <td><button type="submit" name="action" value="save">Save</button>
+ <button type="submit" name="action" value="delete">Delete</button></td>
+</form>
+ </tr>
+ [% END %]
+ <tbody>
+ </table>
+
+
+<h2>Create a new AuthJWT Source</h2>
+ <table>
+ <thead>
+ <tr>
+ <th>kid</th>
+ <th>Cert</th>
+ <th title="Adding groups here will prevent them from using this IDP">Exclude Groups</th>
+ <th>Comment</th>
+ <th>Active</th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ <form method="POST" action="page.cgi?id=authjwt/settings.html">
+ <tr>
+ <td>
+ <input name="source_kid" id="source_kid">
+ </td>
+ <td><textarea cols="64" rows="10" wrap="soft" name="cert" id="cert"></textarea></td>
+ <td>
+ <select name="groups" id="groups" multiple="multiple">
+ [% FOREACH group IN groups %]
+ <option value="[% group.id FILTER html %]">
+ [% group.name FILTER html %]
+ </option>
+ [% END %]
+ </select>
+ </td>
+ <td><input name="comment" id="comment"></td>
+ <td><input type="checkbox" name="isactive" id="isactive"></td>
+ <td><button type="submit" name="action" value="create">Create</button></td>
+ </tr>
+ </form>
+ <tbody>
+ </table>
+
+<script>
+$(document).ready(function() {
+ $('#jwt_admin_table').DataTable({
+ ordering: true,
+ stateSave: true,
+ pagingType: 'full_numbers',
+ aLengthMenu: [[10, 25, 50, 100, -1], [10, 25, 50, 100, 'All']],
+ fixedHeader: true,
+[% IF user.settings.def_table_size.value %]
+ iDisplayLength: [% user.settings.def_table_size.value FILTER html %],
+[%- END %]
+ });
+});
+</script>
+
+[% PROCESS global/footer.html.tmpl %]