[Rt-commit] rt branch, 5.0/core-rt-authentoken, created. rt-5.0.0alpha1-264-g8600ff25d8
Craig Kaiser
craig at bestpractical.com
Fri May 8 10:49:26 EDT 2020
The branch, 5.0/core-rt-authentoken has been created
at 8600ff25d85d8d96139a77438621a794eb7e426c (commit)
- Log -----------------------------------------------------------------
commit 8ad58298346f4810164061c1ce2c15c5a18c6aa2
Author: Craig <craig at bestpractical.com>
Date: Mon May 4 17:57:42 2020 -0400
Core RT::Authen::Token
diff --git a/etc/acl.Pg b/etc/acl.Pg
index 41a44b16c1..dc3ca03f37 100644
--- a/etc/acl.Pg
+++ b/etc/acl.Pg
@@ -68,6 +68,8 @@ sub acl {
ObjectCustomRoles
configurations_id_seq
Configurations
+ authtokens_id_seq
+ AuthTokens
);
my $db_user = RT->Config->Get('DatabaseUser');
diff --git a/etc/schema.Oracle b/etc/schema.Oracle
index 2b366299cf..5117ad2ef8 100644
--- a/etc/schema.Oracle
+++ b/etc/schema.Oracle
@@ -554,3 +554,18 @@ CREATE TABLE Configurations (
CREATE INDEX Configurations1 ON Configurations (LOWER(Name), Disabled);
CREATE INDEX Configurations2 ON Configurations (Disabled);
+
+CREATE SEQUENCE AuthTokens_seq;
+CREATE TABLE AuthTokens (
+ id NUMBER(11,0) CONSTRAINT AuthTokens_key PRIMARY KEY,
+ Owner NUMBER(11,0) DEFAULT 0 NOT NULL,
+ Token VARCHAR2(256),
+ Description varchar2(255) DEFAULT '',
+ LastUsed DATE,
+ Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
+ Created DATE,
+ LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
+ LastUpdated DATE
+);
+
+CREATE INDEX AuthTokensOwner ON AuthTokens (Owner);
diff --git a/etc/schema.Pg b/etc/schema.Pg
index c51be5dff3..5f6c3d85fb 100644
--- a/etc/schema.Pg
+++ b/etc/schema.Pg
@@ -796,3 +796,18 @@ CREATE TABLE Configurations (
CREATE INDEX Configurations1 ON Configurations (LOWER(Name), Disabled);
CREATE INDEX Configurations2 ON Configurations (Disabled);
+CREATE SEQUENCE authtokens_id_seq;
+CREATE TABLE AuthTokens (
+ id integer DEFAULT nextval('authtokens_id_seq'),
+ Owner integer NOT NULL DEFAULT 0,
+ Token varchar(256) NULL,
+ Description varchar(255) NOT NULL DEFAULT '',
+ LastUsed timestamp DEFAULT NULL,
+ Creator integer NOT NULL DEFAULT 0,
+ Created timestamp DEFAULT NULL,
+ LastUpdatedBy integer NOT NULL DEFAULT 0,
+ LastUpdated timestamp DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+
+CREATE INDEX AuthTokensOwner ON AuthTokens (Owner);
diff --git a/etc/schema.SQLite b/etc/schema.SQLite
index c51070aa87..bc8b456ecd 100644
--- a/etc/schema.SQLite
+++ b/etc/schema.SQLite
@@ -585,3 +585,16 @@ CREATE TABLE Configurations (
CREATE INDEX Configurations1 ON Configurations (Name, Disabled);
CREATE INDEX Configurations2 ON Configurations (Disabled);
+CREATE TABLE AuthTokens (
+ id INTEGER PRIMARY KEY,
+ Owner int(11) NOT NULL DEFAULT 0,
+ Token varchar(256) collate NOCASE NULL ,
+ Description varchar(255) NOT NULL DEFAULT '',
+ LastUsed timestamp DEFAULT NULL,
+ Creator int(11) NOT NULL DEFAULT 0,
+ Created timestamp DEFAULT NULL,
+ LastUpdatedBy int(11) NOT NULL DEFAULT 0,
+ LastUpdated timestamp DEFAULT NULL
+);
+
+CREATE INDEX AuthTokensOwner on AuthTokens (Owner);
diff --git a/etc/schema.mysql b/etc/schema.mysql
index ca90073340..69cb029093 100644
--- a/etc/schema.mysql
+++ b/etc/schema.mysql
@@ -576,3 +576,17 @@ CREATE TABLE Configurations (
CREATE INDEX Configurations1 ON Configurations (Name, Disabled);
CREATE INDEX Configurations2 ON Configurations (Disabled);
+CREATE TABLE AuthTokens (
+ id int(11) NOT NULL AUTO_INCREMENT,
+ Owner int(11) NOT NULL DEFAULT 0,
+ Token varchar(256) NULL,
+ Description varchar(255) NOT NULL DEFAULT '',
+ LastUsed datetime DEFAULT NULL,
+ Creator int(11) NOT NULL DEFAULT 0,
+ Created datetime DEFAULT NULL,
+ LastUpdatedBy int(11) NOT NULL DEFAULT 0,
+ LastUpdated datetime DEFAULT NULL,
+ PRIMARY KEY (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE INDEX AuthTokensOwner ON AuthTokens (Owner);
diff --git a/etc/upgrade/4.5.5/acl.Oracle b/etc/upgrade/4.5.5/acl.Oracle
new file mode 100644
index 0000000000..4d6b3ca776
--- /dev/null
+++ b/etc/upgrade/4.5.5/acl.Oracle
@@ -0,0 +1,2 @@
+sub acl { return () }
+1;
diff --git a/etc/upgrade/4.5.5/acl.Pg b/etc/upgrade/4.5.5/acl.Pg
new file mode 100644
index 0000000000..40621b4dd4
--- /dev/null
+++ b/etc/upgrade/4.5.5/acl.Pg
@@ -0,0 +1,30 @@
+sub acl {
+ my $dbh = shift;
+
+ my @acls;
+ my @tables = qw (
+ authtokens_id_seq
+ AuthTokens
+ );
+
+ my $db_user = RT->Config->Get('DatabaseUser');
+
+ my $sequence_right
+ = ( $dbh->{pg_server_version} >= 80200 )
+ ? "USAGE, SELECT, UPDATE"
+ : "SELECT, UPDATE";
+
+ foreach my $table (@tables) {
+ # Tables are upper-case, sequences are lowercase in @tables
+ if ( $table =~ /^[a-z]/ ) {
+ push @acls, "GRANT $sequence_right ON $table TO \"$db_user\";"
+ }
+ else {
+ push @acls, "GRANT SELECT, INSERT, UPDATE, DELETE ON $table TO \"$db_user\";"
+ }
+ }
+ return (@acls);
+}
+
+1;
+
diff --git a/etc/upgrade/4.5.5/acl.mysql b/etc/upgrade/4.5.5/acl.mysql
new file mode 100644
index 0000000000..4d6b3ca776
--- /dev/null
+++ b/etc/upgrade/4.5.5/acl.mysql
@@ -0,0 +1,2 @@
+sub acl { return () }
+1;
diff --git a/etc/upgrade/4.5.5/schema.Oracle b/etc/upgrade/4.5.5/schema.Oracle
new file mode 100644
index 0000000000..72a22c1461
--- /dev/null
+++ b/etc/upgrade/4.5.5/schema.Oracle
@@ -0,0 +1,15 @@
+CREATE SEQUENCE AuthTokens_seq;
+CREATE TABLE AuthTokens (
+ id NUMBER(11,0) CONSTRAINT AuthTokens_key PRIMARY KEY,
+ Owner NUMBER(11,0) DEFAULT 0 NOT NULL,
+ Token VARCHAR2(256),
+ Description varchar2(255) DEFAULT '',
+ LastUsed DATE,
+ Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
+ Created DATE,
+ LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
+ LastUpdated DATE
+);
+
+CREATE INDEX AuthTokensOwner ON AuthTokens (Owner);
+
diff --git a/etc/upgrade/4.5.5/schema.Pg b/etc/upgrade/4.5.5/schema.Pg
new file mode 100644
index 0000000000..29a5372a89
--- /dev/null
+++ b/etc/upgrade/4.5.5/schema.Pg
@@ -0,0 +1,15 @@
+CREATE SEQUENCE authtokens_id_seq;
+CREATE TABLE AuthTokens (
+ id integer DEFAULT nextval('authtokens_id_seq'),
+ Owner integer NOT NULL DEFAULT 0,
+ Token varchar(256) NULL,
+ Description varchar(255) NOT NULL DEFAULT '',
+ LastUsed timestamp DEFAULT NULL,
+ Creator integer NOT NULL DEFAULT 0,
+ Created timestamp DEFAULT NULL,
+ LastUpdatedBy integer NOT NULL DEFAULT 0,
+ LastUpdated timestamp DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+
+CREATE INDEX AuthTokensOwner ON AuthTokens (Owner);
diff --git a/etc/upgrade/4.5.5/schema.SQLite b/etc/upgrade/4.5.5/schema.SQLite
new file mode 100644
index 0000000000..cccb19b949
--- /dev/null
+++ b/etc/upgrade/4.5.5/schema.SQLite
@@ -0,0 +1,14 @@
+CREATE TABLE AuthTokens (
+ id INTEGER PRIMARY KEY,
+ Owner int(11) NOT NULL DEFAULT 0,
+ Token varchar(256) collate NOCASE NULL ,
+ Description varchar(255) NOT NULL DEFAULT '',
+ LastUsed timestamp DEFAULT NULL,
+ Creator int(11) NOT NULL DEFAULT 0,
+ Created timestamp DEFAULT NULL,
+ LastUpdatedBy int(11) NOT NULL DEFAULT 0,
+ LastUpdated timestamp DEFAULT NULL
+);
+
+CREATE INDEX AuthTokensOwner on AuthTokens (Owner);
+
diff --git a/etc/upgrade/4.5.5/schema.mysql b/etc/upgrade/4.5.5/schema.mysql
new file mode 100644
index 0000000000..ca5334c888
--- /dev/null
+++ b/etc/upgrade/4.5.5/schema.mysql
@@ -0,0 +1,15 @@
+CREATE TABLE AuthTokens (
+ id int(11) NOT NULL AUTO_INCREMENT,
+ Owner int(11) NOT NULL DEFAULT 0,
+ Token varchar(256) NULL,
+ Description varchar(255) NOT NULL DEFAULT '',
+ LastUsed datetime DEFAULT NULL,
+ Creator int(11) NOT NULL DEFAULT 0,
+ Created datetime DEFAULT NULL,
+ LastUpdatedBy int(11) NOT NULL DEFAULT 0,
+ LastUpdated datetime DEFAULT NULL,
+ PRIMARY KEY (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE INDEX AuthTokensOwner ON AuthTokens (Owner);
+
diff --git a/etc/upgrade/4.5.6/content b/etc/upgrade/4.5.7/content
similarity index 100%
rename from etc/upgrade/4.5.6/content
rename to etc/upgrade/4.5.7/content
diff --git a/lib/RT.pm b/lib/RT.pm
index 21f54c46c1..9f9a0e194f 100644
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -506,6 +506,7 @@ sub InitClasses {
require RT::CustomFieldValues::Canonicalizer;
require RT::Configuration;
require RT::Configurations;
+ require RT::Authen::Token;
_BuildTableAttributes();
diff --git a/lib/RT/Authen/Token.pm b/lib/RT/Authen/Token.pm
new file mode 100644
index 0000000000..4670f711f6
--- /dev/null
+++ b/lib/RT/Authen/Token.pm
@@ -0,0 +1,128 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+# <sales at bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+
+package RT::Authen::Token;
+
+use strict;
+use warnings;
+
+use RT::System;
+
+'RT::System'->AddRight(Staff => ManageAuthTokens => 'Manage authentication tokens'); # loc
+
+use RT::Authen::Token::AuthToken;
+use RT::Authen::Token::AuthTokens;
+
+sub UserForAuthString {
+ my $self = shift;
+ my $authstring = shift;
+ my $user = shift;
+
+ my ($user_id, $cleartext_token) = RT::Authen::Token::AuthToken->ParseAuthString($authstring);
+ return unless $user_id;
+
+ my $user_obj = RT::CurrentUser->new;
+ $user_obj->Load($user_id);
+ return if !$user_obj->Id || $user_obj->Disabled;
+
+ if (length $user) {
+ my $check_user = RT::CurrentUser->new;
+ $check_user->Load($user);
+ return unless $check_user->Id && $user_obj->Id == $check_user->Id;
+ }
+
+ my $tokens = RT::Authen::Token::AuthTokens->new(RT->SystemUser);
+ $tokens->LimitOwner(VALUE => $user_id);
+ while (my $token = $tokens->Next) {
+ if ($token->IsToken($cleartext_token)) {
+ $token->UpdateLastUsed;
+ return ($user_obj, $token);
+ }
+ }
+
+ return;
+}
+
+=head1 NAME
+
+RT-Authen-Token - token-based authentication
+
+=head1 DESCRIPTION
+
+This module adds the ability for users to generate and login with
+authentication tokens. Users with the C<ManageAuthTokens> permission
+will see a new "Auth Tokens" menu item under "Logged in as ____" ->
+Settings. On that page they will be able to generate new tokens and
+modify or revoke existing tokens.
+
+Once you have an authentication token, you may use it in place of a
+password to log into RT. (Additionally, L<RT::Extension::REST2> allows
+for using auth tokens with the C<Authorization: token> HTTP header.) One
+common use case is to use an authentication token as an
+application-specific password, so that you may revoke that application's
+access without disturbing other applications. You also need not change
+your password, since the application never received it.
+
+If you have the C<AdminUsers> permission, along with
+C<ManageAuthTokens>, you may generate, modify, and revoke tokens for
+other users as well by visiting Admin -> Users -> Select -> (user) ->
+Auth Tokens.
+
+Authentication tokens are stored securely (hashed and salted) in the
+database just like passwords, and so cannot be recovered after they are
+generated.
+
+=item Update your Apache configuration
+
+If you are running RT under Apache, add the following directive to your RT
+Apache configuration to allow RT to access the Authorization header.
+
+ SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
+=cut
+
+1;
diff --git a/lib/RT/Authen/Token/AuthToken.pm b/lib/RT/Authen/Token/AuthToken.pm
new file mode 100644
index 0000000000..93720aa42e
--- /dev/null
+++ b/lib/RT/Authen/Token/AuthToken.pm
@@ -0,0 +1,371 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+# <sales at bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+use strict;
+use warnings;
+use 5.10.1;
+
+package RT::Authen::Token::AuthToken;
+use base 'RT::Record';
+
+require RT::User;
+require RT::Util;
+use Digest::SHA 'sha512_hex';
+
+=head1 NAME
+
+RT::Authen::Token::AuthToken - Represents an authentication token for a user
+
+=cut
+
+=head1 METHODS
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database. Available
+keys are:
+
+=over 4
+
+=item Owner
+
+The user ID for whom this token will authenticate. If it's not the AuthToken
+object's CurrentUser, then the AdminUsers permission is required.
+
+=item Description
+
+A human-readable description of what this token will be used for.
+
+=back
+
+Returns a tuple of (status, msg) on failure and (id, msg, authstring) on
+success. Note that this is the only time the authstring will be directly
+readable (as it is stored in the database hashed like a password, so use
+this opportunity to capture it.
+
+=cut
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Owner => undef,
+ Description => '',
+ @_,
+ );
+
+ return (0, $self->loc("Permission Denied"))
+ unless $self->CurrentUserHasRight('ManageAuthTokens');
+
+ return (0, $self->loc("Owner required"))
+ unless $args{Owner};
+
+ return (0, $self->loc("Permission Denied"))
+ unless $args{Owner} == $self->CurrentUser->Id
+ || $self->CurrentUserHasRight('AdminUsers');
+
+ my $token = $self->_GenerateToken;
+
+ my ( $id, $msg ) = $self->SUPER::Create(
+ Token => $self->_CryptToken($token),
+ map { $_ => $args{$_} } grep {exists $args{$_}}
+ qw(Owner Description),
+ );
+ unless ($id) {
+ return (0, $self->loc("Authentication token create failed: [_1]", $msg));
+ }
+
+ my $authstring = $self->_BuildAuthString($self->Owner, $token);
+
+ return ($id, $self->loc('Authentication token created'), $authstring);
+}
+
+=head2 CurrentUserCanSee
+
+Returns true if the current user can see the AuthToken
+
+=cut
+
+sub CurrentUserCanSee {
+ my $self = shift;
+
+ return 0 unless $self->CurrentUserHasRight('ManageAuthTokens');
+
+ return 0 unless $self->__Value('Owner') == $self->CurrentUser->Id
+ || $self->CurrentUserHasRight('AdminUsers');
+
+ return 1;
+}
+
+=head2 SetOwner
+
+Not permitted
+
+=cut
+
+sub SetOwner {
+ my $self = shift;
+ return (0, $self->loc("Permission Denied"));
+}
+
+=head2 SetToken
+
+Not permitted
+
+=cut
+
+sub SetToken {
+ my $self = shift;
+ return (0, $self->loc("Permission Denied"));
+}
+
+=head2 Delete
+
+Checks ACL
+
+=cut
+
+sub Delete {
+ my $self = shift;
+ return (0, $self->loc("Permission Denied")) unless $self->CurrentUserCanSee;
+ my ($ok, $msg) = $self->SUPER::Delete(@_);
+ return ($ok, $self->loc("Authentication token revoked.")) if $ok;
+ return ($ok, $msg);
+}
+
+=head2 UpdateLastUsed
+
+Sets the "last used" time, without touching "last updated"
+
+=cut
+
+sub UpdateLastUsed {
+ my $self = shift;
+
+ my $now = RT::Date->new( $self->CurrentUser );
+ $now->SetToNow;
+
+ return $self->__Set(
+ Field => 'LastUsed',
+ Value => $now->ISO,
+ );
+}
+
+=head2 ParseAuthString AUTHSTRING
+
+Class method that takes as input an authstring and provides a tuple
+of (user id, token) on success, or the empty list on failure.
+
+=cut
+
+sub ParseAuthString {
+ my $class = shift;
+ my $input = shift;
+
+ my ($version) = $input =~ s/^([0-9]+)-//
+ or return;
+
+ if ($version == 1) {
+ my ($user_id, $token) = $input =~ /^([0-9]+)-([0-9a-f]{32})$/i
+ or return;
+ return ($user_id, $token);
+ }
+
+ return;
+}
+
+=head2 IsToken
+
+Analogous to L<RT::User/IsPassword>, without all of the legacy password
+forms.
+
+=cut
+
+sub IsToken {
+ my $self = shift;
+ my $value = shift;
+
+ my $stored = $self->__Value('Token');
+
+ # If it's a new-style (>= RT 4.0) password, it starts with a '!'
+ my (undef, $method, @rest) = split /!/, $stored;
+ if ($method eq "bcrypt") {
+ if (RT::Util->can('constant_time_eq')) {
+ return 0 unless RT::Util::constant_time_eq(
+ $self->_CryptToken_bcrypt($value, @rest),
+ $stored,
+ );
+ } else {
+ return 0 unless $self->_CryptToken_bcrypt($value, @rest) eq $stored;
+ }
+ # Upgrade to a larger number of rounds if necessary
+ return 1 unless $rest[0] < RT->Config->Get('BcryptCost');
+ }
+ else {
+ $RT::Logger->warn("Unknown hash method $method");
+ return 0;
+ }
+
+ # We got here by validating successfully, but with a legacy
+ # password form. Update to the most recent form.
+ $self->_Set(Field => 'Token', Value => $self->_CryptToken($value));
+ return 1;
+}
+
+=head2 LastUsedObj
+
+L</LastUsed> as an L<RT::Date> object.
+
+=cut
+
+sub LastUsedObj {
+ my $self = shift;
+ my $date = RT::Date->new($self->CurrentUser);
+ $date->Set(Format => 'sql', Value => $self->LastUsed);
+ return $date;
+}
+
+=head1 PRIVATE METHODS
+
+Documented for internal use only, do not call these from outside
+RT::Authen::Token::AuthToken itself.
+
+=head2 _Set
+
+Checks if the current user can I<ManageAuthTokens> before calling
+C<SUPER::_Set>.
+
+=cut
+
+sub _Set {
+ my $self = shift;
+ my %args = (
+ Field => undef,
+ Value => undef,
+ @_
+ );
+
+ return (0, $self->loc("Permission Denied"))
+ unless $self->CurrentUserCanSee;
+
+ return $self->SUPER::_Set(@_);
+}
+
+=head2 _Value
+
+Checks L</CurrentUserCanSee> before calling C<SUPER::_Value>.
+
+=cut
+
+sub _Value {
+ my $self = shift;
+ return unless $self->CurrentUserCanSee;
+ return $self->SUPER::_Value(@_);
+}
+
+=head2 _GenerateToken
+
+Generates an unpredictable auth token
+
+=cut
+
+sub _GenerateToken {
+ my $class = shift;
+ require Time::HiRes;
+
+ my $input = join '',
+ Time::HiRes::time(), # subsecond-precision time
+ {}, # unpredictable memory address
+ rand(); # RNG
+
+ my $digest = sha512_hex($input);
+
+ return substr($digest, 0, 32);
+}
+
+=head2 _BuildAuthString
+
+Takes a user id and token and provides an authstring for use in place of
+a (username, password) combo.
+
+=cut
+
+sub _BuildAuthString {
+ my $self = shift;
+ my $version = 1;
+ my $userid = shift;
+ my $token = shift;
+
+ return $version . '-' . $userid . '-' . $token;
+}
+
+sub _CryptToken_bcrypt {
+ my $self = shift;
+ return $self->CurrentUser->UserObj->_GeneratePassword_bcrypt(@_);
+}
+
+sub _CryptToken {
+ my $self = shift;
+ return $self->_CryptToken_bcrypt(@_);
+}
+
+sub Table { "AuthTokens" }
+
+sub _CoreAccessible {
+ {
+ id => { read => 1, type => 'int(11)', default => '' },
+ Owner => { read => 1, type => 'int(11)', default => '0' },
+ Token => { read => 1, sql_type => 12, length => 256, is_blob => 0, is_numeric => 0, type => 'varchar(256)', default => ''},
+ Description => { read => 1, type => 'varchar(255)', default => '', write => 1 },
+ LastUsed => { read => 1, type => 'datetime', default => '', write => 1 },
+ Creator => { read => 1, type => 'int(11)', default => '0', auto => 1 },
+ Created => { read => 1, type => 'datetime', default => '', auto => 1 },
+ LastUpdatedBy => { read => 1, type => 'int(11)', default => '0', auto => 1 },
+ LastUpdated => { read => 1, type => 'datetime', default => '', auto => 1 },
+ }
+}
+
+1;
diff --git a/lib/RT/Authen/Token/AuthTokens.pm b/lib/RT/Authen/Token/AuthTokens.pm
new file mode 100644
index 0000000000..4136aebf11
--- /dev/null
+++ b/lib/RT/Authen/Token/AuthTokens.pm
@@ -0,0 +1,98 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+# <sales at bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+use strict;
+use warnings;
+
+package RT::Authen::Token::AuthTokens;
+use base 'RT::SearchBuilder';
+
+=head1 NAME
+
+RT::Authen::Token::AuthTokens - a collection of L<RT::Authen::Token::AuthToken> objects
+
+=cut
+
+=head2 LimitOwner
+
+Limit Owner
+
+=cut
+
+sub LimitOwner {
+ my $self = shift;
+ my %args = (
+ FIELD => 'Owner',
+ OPERATOR => '=',
+ @_
+ );
+
+ $self->SUPER::Limit(%args);
+}
+
+sub NewItem {
+ my $self = shift;
+ return RT::Authen::Token::AuthToken->new( $self->CurrentUser );
+}
+
+=head2 _Init
+
+Sets default ordering by id ascending.
+
+=cut
+
+sub _Init {
+ my $self = shift;
+
+ $self->OrderBy( FIELD => 'id', ORDER => 'ASC' );
+ return $self->SUPER::_Init( @_ );
+}
+
+sub Table { "AuthTokens" }
+
+1;
+
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 322f70f269..a6fa51e5ed 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -144,6 +144,7 @@ sub JSFiles {
Chart.min.js
chartjs-plugin-colorschemes.min.js
jquery.jgrowl.min.js
+ rt-authen-token.js
}, RT->Config->Get('JSFiles');
}
@@ -349,6 +350,47 @@ sub HandleRequest {
# Authenticate if the user is trying to login via user/pass query args
my ($authed, $msg) = AttemptPasswordAuthentication($ARGS);
+ unless ($authed) {
+ my $get_env = sub {
+ my $key = shift;
+ if (RT::Interface::Web->can('RequestENV')) {
+ return RT::Interface::Web::RequestENV($key)
+ }
+ return $ENV{$key};
+ };
+
+ my ($pass, $user) = ('', '');
+ if (($get_env->('HTTP_AUTHORIZATION')||'') =~ /^token (.*)$/i) {
+ $pass ||= $1;
+ }
+ unless ( defined $pass ) {
+ my ($user_obj, $token) = RT::Authen::Token->UserForAuthString($pass, $user);
+ if ( $user_obj ) {
+ # log in
+ my $remote_addr = $get_env->('REMOTE_ADDR');
+ $RT::Logger->info("Successful login for @{[$user_obj->Name]} from $remote_addr using authentication token #@{[$token->Id]} (\"@{[$token->Description]}\")");
+
+ # It's important to nab the next page from the session before we blow
+ # the session away
+ my $next = RT::Interface::Web::RemoveNextPage($ARGS->{'next'});
+ $next = $next->{'url'} if ref $next;
+
+ RT::Interface::Web::InstantiateNewSession();
+ $HTML::Mason::Commands::session{'CurrentUser'} = $user_obj;
+
+ # Really the only time we don't want to redirect here is if we were
+ # passed user and pass as query params in the URL.
+ if ($next) {
+ RT::Interface::Web::Redirect($next);
+ }
+ elsif ($ARGS->{'next'}) {
+ # Invalid hash, but still wants to go somewhere, take them to /
+ RT::Interface::Web::Redirect(RT->Config->Get('WebURL'));
+ }
+ }
+ }
+ }
+
unless ($authed) {
my $m = $HTML::Mason::Commands::m;
diff --git a/lib/RT/Interface/Web/MenuBuilder.pm b/lib/RT/Interface/Web/MenuBuilder.pm
index c8e58daeed..42740c32cc 100644
--- a/lib/RT/Interface/Web/MenuBuilder.pm
+++ b/lib/RT/Interface/Web/MenuBuilder.pm
@@ -304,6 +304,10 @@ sub BuildMainNav {
);
$settings->child( queue_list => title => loc('Queue list'), path => '/Prefs/QueueList.html' );
+ if ( $current_user->HasRight( Right => 'ManageAuthTokens', Object => RT->System ) ) {
+ $settings->child('about_me')->add_after(auth_tokens => title => loc('Auth Tokens'), path => '/Prefs/AuthTokens.html');
+ }
+
my $search_menu = $settings->child( 'saved-searches' => title => loc('Saved Searches') );
my $searches = [ $HTML::Mason::Commands::m->comp( "/Search/Elements/SearchesForObject",
Object => RT::System->new( $current_user )) ];
@@ -1531,6 +1535,20 @@ sub _BuildAdminMenu {
$page->child( create => title => loc('Create'), path => "/Admin/Articles/Classes/Modify.html?Create=1" );
}
}
+
+ my $request_path_token = $request_path =~ s!/{2,}!/!g;
+ if ( $request_path_token =~ m{^(/Admin/Users|/User/(Summary|History)\.html)} and $admin->child("users") ) {
+ if ( $HTML::Mason::Commands::DECODED_ARGS->{'id'} && $HTML::Mason::Commands::DECODED_ARGS->{'id'} =~ /^\d+$/ ) {
+ my $id = $HTML::Mason::Commands::DECODED_ARGS->{'id'};
+ my $obj = RT::User->new( $HTML::Mason::Commands::session{'CurrentUser'} );
+ $obj->Load($id);
+
+ if ( $obj and $obj->id ) {
+ my $tabs = PageMenu();
+ $tabs->child(auth_tokens => title => loc('Auth Tokens'), path => '/Admin/Users/AuthTokens.html?id=' . $id);
+ }
+ }
+ }
}
sub BuildSelfServiceNav {
diff --git a/share/html/Admin/Users/AuthTokens.html b/share/html/Admin/Users/AuthTokens.html
new file mode 100644
index 0000000000..34de303304
--- /dev/null
+++ b/share/html/Admin/Users/AuthTokens.html
@@ -0,0 +1,78 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<& /Admin/Elements/Header, Title => loc("[_1]'s authentication tokens",$UserObj->Name) &>
+<& /Elements/Tabs &>
+<& /Elements/ListActions, actions => \@results &>
+
+<div class="form-row">
+ <div class="auth-tokens col-12">
+ <p><&|/l&>Authentication tokens allow other applications to use your user
+ account without having to share your password, while allowing you to
+ revoke access on an application-specific basis. Changing your password
+ <em>does not</em> invalidate your auth tokens; you must revoke them here.</&>
+ </p>
+ </div>
+</div>
+
+<& /Elements/AuthToken/CreateButton, %ARGS, Owner => $UserObj->Id &>
+<& /Elements/AuthToken/List, %ARGS, Owner => $UserObj->Id &>
+</div>
+
+<%ARGS>
+$id => undef
+</%ARGS>
+<%INIT>
+my @results;
+
+my $UserObj = RT::User->new( $session{'CurrentUser'} );
+$UserObj->Load( $id );
+unless ( $UserObj->id ) {
+ Abort( loc("Couldn't load user #[_1]", $id) );
+}
+$id = $ARGS{'id'} = $UserObj->id;
+</%INIT>
diff --git a/share/html/Elements/AuthToken/CreateButton b/share/html/Elements/AuthToken/CreateButton
new file mode 100644
index 0000000000..58c386e302
--- /dev/null
+++ b/share/html/Elements/AuthToken/CreateButton
@@ -0,0 +1,72 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$Owner
+$ShowCreateForm => 0
+$CreateToken => 0
+</%ARGS>
+<%INIT>
+</%INIT>
+% if ($CreateToken) {
+ <&| /Widgets/TitleBox, title => loc("Create Auth Token") &>
+ <& /Elements/AuthToken/CreateResults, %ARGS &>
+ </&>
+% } elsif ($ShowCreateForm) {
+ <&| /Widgets/TitleBox, title => loc("Create Auth Token") &>
+ <& /Elements/AuthToken/CreateForm, Owner => $Owner &>
+ </&>
+% } else {
+<div class="authtoken-form-container">
+ <& /Elements/AuthToken/CreateForm, Owner => $Owner &>
+</div>
+<form method="GET">
+ <input type="hidden" name="ShowCreateForm" value="1">
+ <input type="hidden" name="id" value="<% $Owner %>">
+ <button type="submit" class="authtoken-create">Create Auth Token</button>
+</form>
+% }
diff --git a/share/html/Elements/AuthToken/CreateForm b/share/html/Elements/AuthToken/CreateForm
new file mode 100644
index 0000000000..8a73fb9b5d
--- /dev/null
+++ b/share/html/Elements/AuthToken/CreateForm
@@ -0,0 +1,103 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$Owner
+$Description => ''
+</%ARGS>
+<%INIT>
+# Don't require password for systems with some form of federated auth
+my %res = $session{'CurrentUser'}->CurrentUserRequireToSetPassword();
+</%INIT>
+
+<div class="modal" id="create-auth-token">
+ <div class="modal-dialog modal-dialog-centered" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h5 class="modal-title"><&|/l&>Create auth token</&></h5>
+ <a href="javascript:void(0)" class="close" data-dismiss="modal" aria-label="Close">
+ <span aria-hidden="true">×</span>
+ </a>
+ </div>
+ <div class="modal-body">
+ <form class="authtoken-form" method="post" data-ajax-url="<% RT->Config->Get('WebPath') %>/Helpers/AuthToken/Create">
+ <div class="form-row">
+ <input type="hidden" name="Owner" value="<% $Owner %>">
+% if ( $res{'CanSet'} ){
+ <div class="label col-3">
+ <&|/l, $session{'CurrentUser'}->Name()&>[_1]'s current password</&>:
+ </div>
+ <div class="value col-9">
+ <input class="form-control" type="password" name="Password" size="16" autocomplete="off" /></td>
+ </div>
+% }
+ <div class="col-3 label">
+ <&|/l&>Description</&>:<br><em><&|/l&>What's this token for?</&></em>
+ </div>
+ <div class="value col-9">
+ <input class="form-control" type="text" name="Description" value="<% $Description %>" size="16" />
+ </div>
+ </div>
+
+ <div class="form-row">
+ <div class="col-12">
+ <& /Elements/Submit, Label => loc("Create"), Name => 'CreateToken' &>
+ </div>
+ </div>
+
+ <span class="loading"><img src="<%RT->Config->Get('WebPath')%>/static/images/loading.gif" alt="<%loc('Loading')%>" title="<%loc('Loading')%>" /></span>
+ </form>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="form-row">
+ <div class="col-12">
+ <a class="button btn btn-primary" href="#create-auth-token" data-toggle="modal" rel="modal:open" name="create_auth_token"><&|/l&>Create Auth Token</&></a>
+ </div>
+</div>
diff --git a/share/html/Elements/AuthToken/CreateResults b/share/html/Elements/AuthToken/CreateResults
new file mode 100644
index 0000000000..58bb9da202
--- /dev/null
+++ b/share/html/Elements/AuthToken/CreateResults
@@ -0,0 +1,96 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$Owner => undef
+$Password => ''
+$Description => ''
+</%ARGS>
+<%INIT>
+my $token = RT::AuthToken->new($session{CurrentUser});
+# Don't require password for systems with some form of federated auth
+my %res = $session{'CurrentUser'}->CurrentUserRequireToSetPassword();
+my ($error, $authstring);
+
+if (!$Owner) {
+ $error = loc("Owner required. Please refresh the page and try again.");
+}
+elsif (!length($Description)) {
+ $error = loc("Description cannot be blank.");
+}
+elsif ($res{'CanSet'} && !length($Password)) {
+ $error = loc("Please enter your current password.");
+}
+elsif ($res{'CanSet'} && !$session{CurrentUser}->IsPassword($Password) ) {
+ $error = loc("Please enter your current password correctly.");
+}
+else {
+ ((my $ok), (my $msg), $authstring) = $token->Create(
+ Owner => $Owner,
+ Description => $Description,
+ );
+}
+</%INIT>
+<div class="form-row">
+% if ($error) {
+ <& /Elements/AuthToken/CreateForm, Owner => $Owner, Error => $error, Description => $Description &>
+% } else {
+ <div class="authtoken-success">
+ <div class="col-12">
+ <p><&|/l, $Description&>This is your new authentication token. Treat
+ it carefully like a password. Please save it now because you cannot
+ access it again.
+ </&></p>
+ </div>
+ <div class="col-12 text-center">
+ <span class="authstring"><% $authstring %></span>
+ </div>
+ </div>
+ </div>
+</div>
+% }
+
diff --git a/share/html/Elements/AuthToken/List b/share/html/Elements/AuthToken/List
new file mode 100644
index 0000000000..c6f729c295
--- /dev/null
+++ b/share/html/Elements/AuthToken/List
@@ -0,0 +1,138 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$Owner
+</%ARGS>
+<%INIT>
+my $tokens = RT::Authen::Token::AuthTokens->new($session{CurrentUser});
+$tokens->LimitOwner(VALUE => $Owner);
+</%INIT>
+<div class="authtoken-list" data-owner="<% $Owner %>">
+ <span class="loading"><img src="<%RT->Config->Get('WebPath')%>/static/images/loading.gif" alt="<%loc('Loading')%>" title="<%loc('Loading')%>" /></span>
+% if ($tokens->Count == 0) {
+ <em><&|/l&>No authentication tokens.</&></em>
+% } else {
+ <ul class="list-group">
+% while (my $token = $tokens->Next) {
+ <div class="form-row">
+ <div class="col-6">
+ <li class="list-group" id="auth-token-<% $token->Id %>">
+
+% my $used = $token->LastUsedObj;
+% my $last_updated = '';
+% if ($used->IsSet) {
+% $last_updated = loc( $used->AgeAsString, "used [_1]" );
+% } else {
+% $last_updated = loc( "never used" );
+% }
+ <&| /Widgets/TitleBox,
+ title => $token->Description,
+ title_href => "#auth-token-".$token->Id,
+ titleright_raw => $last_updated,
+ rolledup => 0
+ &>
+% if ($ARGS{ShowModifyForm} && $ARGS{Token} == $token->Id) {
+% if ($ARGS{Update} || $ARGS{Revoke}) {
+ <& /Elements/AuthToken/ModifyResults, %ARGS, Token => $token->Id, Owner => $Owner &>
+% }
+% }
+ <form class="authtoken-form" method="post" data-ajax-url="<% RT->Config->Get('WebPath') %>/Helpers/AuthToken/Modify" action="<% RT->Config->Get('WebPath') %><% $r->uri %>">
+ <div class="form-row">
+% if ($ARGS{id}) {
+ <input type="hidden" name="id" value="<% $ARGS{id} %>">
+% }
+ <input type="hidden" name="Token" value="<% $token->id %>">
+ <input type="hidden" name="Owner" value="<% $Owner %>">
+
+ <div class="col-4">
+ <label class="label"><&|/l&>Description</&>:
+ <span class="far fa-question-circle icon-helper" data-toggle="tooltip" data-placement="top" data-original-title="<&|/l&>What's this token for?</&>"></span>
+ </label>
+ </div>
+ <div class="col-8">
+ <span class="value"><input class="form-control" type="text" name="Description" value="<% $ARGS{Description} // $token->Description %>" size="16" /></span>
+ </div>
+
+ <div class="col-4">
+ <label class="label"><&|/l&>Last Used</&>:</label>
+ </div>
+ <div class="col-8">
+% my $used = $token->LastUsedObj;
+ <span class="value">
+% if ($used->IsSet) {
+ <% $used->AgeAsString %>
+% } else {
+ <&|/l&>never</&>
+% }
+ </span>
+ </div>
+
+ <div class="col-4">
+ <label class="label"><&|/l&>Created</&>:</label>
+ </div>
+ <div class="col-8">
+ <span class="value"><% $token->CreatedObj->AgeAsString %></span>
+ </div>
+
+ <div class="col-6">
+ <input class="button btn btn-primary" type="submit" name="Update" value="<&|/l&>Save</&>"></input>
+ </div>
+ <div class="col-6">
+ <input class="button btn btn-primary" type="submit" name="Revoke" value="<&|/l&>Revoke</&>"></input>
+ </div>
+ <span class="loading"><img src="<%RT->Config->Get('WebPath')%>/static/images/loading.gif" alt="<%loc('Loading')%>" title="<%loc('Loading')%>" /></span>
+ </form>
+ </div>
+ </&>
+ </li>
+ </div>
+ </div>
+% }
+ </ul>
+% }
+</div>
diff --git a/share/html/Elements/AuthToken/ModifyForm b/share/html/Elements/AuthToken/ModifyForm
new file mode 100644
index 0000000000..c57dbc10f1
--- /dev/null
+++ b/share/html/Elements/AuthToken/ModifyForm
@@ -0,0 +1,123 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$Token => undef
+$TokenObj => undef
+$Error => ''
+</%ARGS>
+<%INIT>
+if (!$TokenObj) {
+ $TokenObj = RT::Authen::Token::AuthToken->new($session{CurrentUser});
+ $TokenObj->Load($Token);
+}
+Abort("Unable to load authentication token") if !$TokenObj->Id;
+Abort("Permission Denied") if !$TokenObj->CurrentUserCanSee;
+</%INIT>
+
+<div class="modal" id="edit-auth-token">
+ <div class="modal-dialog modal-dialog-centered" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h5 class="modal-title"><&|/l&>Edit auth token</&></h5>
+ <a href="javascript:void(0)" class="close" data-dismiss="modal" aria-label="Close">
+ <span aria-hidden="true">×</span>
+ </a>
+ </div>
+ <div class="modal-body">
+ <form class="authtoken-form" method="post" data-ajax-url="<% RT->Config->Get('WebPath') %>/Helpers/AuthToken/Modify" action="<% RT->Config->Get('WebPath') %><% $r->uri %>">
+ <div class="form-row">
+% if ($Error) {
+ <div class="col-12">
+ <p class="error"><% $Error %></p>
+ </div>
+% }
+% if ($ARGS{id}) {
+ <input type="hidden" name="id" value="<% $ARGS{id} %>">
+% }
+ <input type="hidden" name="Token" value="<% $TokenObj->id %>">
+
+ <div class="form-row">
+ <div class="col-4 label">
+ <&|/l&>Description</&>:<br><em><&|/l&>What's this token for?</&></em>
+ </div>
+ <div class="col-8 value">
+ <input class="form-control" type="text" name="Description" value="<% $ARGS{Description} // $TokenObj->Description %>" size="16" />
+ </div>
+
+ <div class="col-4 label">
+ <&|/l&>Last Used</&>:
+ </div>
+ <div class="col-8 value">
+% my $used = $TokenObj->LastUsedObj;
+% if ($used->IsSet) {
+ <% $used->AgeAsString %>
+% } else {
+ <&|/l&>never</&>
+% }
+ </div>
+
+ <div class="col-4 label">
+ <&|/l&>Created</&>:
+ </div>
+ <div class="col-8 value">
+ <% $TokenObj->CreatedObj->AgeAsString %>
+ </div>
+ </div>
+
+ <div class="buttons">
+ <input type="submit" name="Update" value="<&|/l&>Save</&>"></input>
+ <input type="submit" name="Revoke" value="<&|/l&>Revoke</&>"></input>
+ </div>
+
+ <span class="loading"><img src="<%RT->Config->Get('WebPath')%>/static/images/loading.gif" alt="<%loc('Loading')%>" title="<%loc('Loading')%>" /></span>
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/share/html/Elements/AuthToken/ModifyResults b/share/html/Elements/AuthToken/ModifyResults
new file mode 100644
index 0000000000..0bb6ac7a72
--- /dev/null
+++ b/share/html/Elements/AuthToken/ModifyResults
@@ -0,0 +1,83 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$Token
+$Description => ''
+$Update => 0
+$Revoke => 0
+</%ARGS>
+<%INIT>
+my $TokenObj = RT::Authen::Token::AuthToken->new($session{CurrentUser});
+$TokenObj->Load($Token);
+my ($error, $ok, $msg);
+
+if ($Update) {
+ if (!length($Description)) {
+ $error = loc("Description cannot be blank.");
+ }
+
+ if ($Description ne $TokenObj->Description) {
+ ($ok, $msg) = $TokenObj->SetDescription($Description);
+ $error = $msg if !$ok;
+ }
+}
+elsif ($Revoke) {
+ ($ok, $msg) = $TokenObj->Delete;
+}
+</%INIT>
+<div id="messages" class="auth-token-messages col-6">
+% if ( $error ) {
+ <div class="alert alert-danger" role="alert">
+ <% $error %>
+ </div>
+% } if ( $msg ) {
+ <div class="alert alert-success" role="alert">
+ <% $msg %>
+ </div>
+% }
+</div>
diff --git a/share/html/Helpers/AuthToken/Create b/share/html/Helpers/AuthToken/Create
new file mode 100644
index 0000000000..1ff6d3ac0d
--- /dev/null
+++ b/share/html/Helpers/AuthToken/Create
@@ -0,0 +1,49 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<& /Elements/AuthToken/CreateResults, %ARGS &>
+% $m->abort;
diff --git a/share/html/Helpers/AuthToken/List b/share/html/Helpers/AuthToken/List
new file mode 100644
index 0000000000..041fdf13c8
--- /dev/null
+++ b/share/html/Helpers/AuthToken/List
@@ -0,0 +1,49 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<& /Elements/AuthToken/List, Owner => $ARGS{owner} &>
+% $m->abort;
diff --git a/share/html/Helpers/AuthToken/Modify b/share/html/Helpers/AuthToken/Modify
new file mode 100644
index 0000000000..73a88f2a9d
--- /dev/null
+++ b/share/html/Helpers/AuthToken/Modify
@@ -0,0 +1,49 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<& /Elements/AuthToken/ModifyResults, %ARGS &>
+% $m->abort;
diff --git a/share/html/Prefs/AuthTokens.html b/share/html/Prefs/AuthTokens.html
new file mode 100644
index 0000000000..9d9e8b4d47
--- /dev/null
+++ b/share/html/Prefs/AuthTokens.html
@@ -0,0 +1,76 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<& /Elements/Header, Title => loc('My authentication tokens') &>
+<& /Elements/Tabs &>
+<& /Elements/ListActions, actions => \@results &>
+
+<div class="form-row">
+ <div class="col-12">
+ <p><&|/l&>Authentication tokens allow other applications to use your user
+ account without having to share your password, while allowing you to
+ revoke access on an application-specific basis. Changing your password
+ <em>does not</em> invalidate your auth tokens; you must revoke them here.
+ </&></p>
+ </div>
+
+ <div class="col-12">
+ <& /Elements/AuthToken/CreateForm, %ARGS, Owner => $UserObj->Id &>
+ </div>
+
+ <div id="auth-token-messages" class="auth-token-messages col-12">
+ </div>
+
+ <div class="col-12">
+ <& /Elements/AuthToken/List, %ARGS, Owner => $UserObj->Id &>
+ </div>
+</div>
+
+<%INIT>
+my @results;
+my $UserObj = $session{'CurrentUser'}->UserObj;
+</%INIT>
diff --git a/share/static/css/elevator-light/main.css b/share/static/css/elevator-light/main.css
index dec0b5a144..fb32f7792d 100644
--- a/share/static/css/elevator-light/main.css
+++ b/share/static/css/elevator-light/main.css
@@ -47,3 +47,4 @@
@import "jquery.jgrowl.min.css";
@import "inline-edit.css";
@import "lifecycleui.css";
+ at import "rt-authen-token.css";
diff --git a/share/static/css/elevator-light/rt-authen-token.css b/share/static/css/elevator-light/rt-authen-token.css
new file mode 100644
index 0000000000..80f8ccadb1
--- /dev/null
+++ b/share/static/css/elevator-light/rt-authen-token.css
@@ -0,0 +1,73 @@
+.authtoken-form-container {
+ display: none;
+}
+
+.authtoken-form .loading {
+ float: right;
+ display: none;
+}
+
+.authtoken-form.submitting .buttons {
+ display: none;
+}
+
+.authtoken-form.submitting .loading {
+ display: inline;
+}
+
+.authtoken-form .error {
+ color: red;
+}
+
+.authstring {
+ font-size: 1.2em;
+ font-family: monospace;
+ padding: .3em;
+ border: 1px dashed black;
+ background-color: #f9f9f9;
+}
+
+.authtoken-success {
+ margin-bottom: 15px;
+}
+
+.authtoken-list ul {
+ list-style-type: none;
+ padding-left: 0;
+}
+
+.authtoken-list ul li + li {
+ margin-top: 1em;
+}
+
+.authtoken-list .description {
+ font-weight: bold;
+}
+
+.authtoken-list .lastused {
+ font-style: italic;
+ color: #666;
+}
+
+.authtoken-list .loading {
+ display: none;
+}
+
+.authtoken-list.refreshing {
+ opacity: 0.3;
+}
+.authtoken-list.refreshing .loading {
+ display: inline;
+}
+
+.authtoken-form input[name=Update] {
+ float: right;
+}
+
+.authtoken-form input[name=Revoke] {
+ float: left;
+}
+
+.auth-token-messages {
+ margin: 1rem 0rem 0rem 0px;
+}
diff --git a/share/static/images/loading.gif b/share/static/images/loading.gif
new file mode 100644
index 0000000000..3288d1035d
Binary files /dev/null and b/share/static/images/loading.gif differ
diff --git a/share/static/js/rt-authen-token.js b/share/static/js/rt-authen-token.js
new file mode 100644
index 0000000000..e989851335
--- /dev/null
+++ b/share/static/js/rt-authen-token.js
@@ -0,0 +1,67 @@
+jQuery(function() {
+ var refreshTokenList = function () {
+ var list = jQuery('.authtoken-list');
+ jQuery.post(
+ RT.Config.WebHomePath + "/Helpers/AuthToken/List",
+ list.data(),
+ function (data) {
+ list.replaceWith(data);
+ }
+ );
+ };
+
+ var submitForm = function (form, extraParams) {
+ var payload = form.serializeArray();
+ var name = extraParams[0].name;
+
+ if (extraParams) {
+ Array.prototype.push.apply(payload, extraParams);
+ }
+
+ form.addClass('submitting');
+ form.find('input').attr('disabled', true);
+
+ var renderResult = function(name, html) {
+ if ( name === 'CreateToken' ) {
+ var form = jQuery('.modal .authtoken-form');
+ if (form.length) {
+ form.replaceWith(html);
+ }
+ else {
+ jQuery('#body').append(html);
+ }
+ }
+ else {
+ jQuery('#auth-token-messages').replaceWith(html);
+ }
+ refreshTokenList();
+ };
+
+ jQuery.ajax({
+ method: 'POST',
+ url: form.data('ajax-url'),
+ data: payload,
+ timeout: 30000, /* 30 seconds */
+ success: function (data, status) {
+ renderResult(name, data);
+ },
+ error: function (xhr, status, error) {
+ renderResult("<p>An error has occurred. Please refresh the page and try again.<p>");
+ }
+ });
+ };
+
+ jQuery('body').on('click', '.authtoken-form button, .authtoken-form input[type=submit]', function (e) {
+ e.preventDefault();
+ var button = jQuery(this);
+
+ var params = [{ name: button.attr('name'), value: button.attr('value') }];
+ submitForm(button.closest('form'), params);
+ });
+
+ jQuery('body').on('submit', '.authtoken-form', function (e) {
+ e.preventDefault();
+ submitForm(jQuery(this));
+ });
+});
+
commit 983b36aea8e3b0f0c19a405dea699fdebd272254
Author: Craig <craig at bestpractical.com>
Date: Thu May 7 10:43:41 2020 -0400
Migrate Authen-Token code to work like other RT pages
diff --git a/lib/RT/Authen/Token.pm b/lib/RT/Authen/Token.pm
index 4670f711f6..cf9c824d89 100644
--- a/lib/RT/Authen/Token.pm
+++ b/lib/RT/Authen/Token.pm
@@ -94,14 +94,13 @@ RT-Authen-Token - token-based authentication
=head1 DESCRIPTION
-This module adds the ability for users to generate and login with
-authentication tokens. Users with the C<ManageAuthTokens> permission
-will see a new "Auth Tokens" menu item under "Logged in as ____" ->
-Settings. On that page they will be able to generate new tokens and
-modify or revoke existing tokens.
+Allow for users to generate and login with authentication tokens.
+Users with the C<ManageAuthTokens> permission will see a new "Auth Tokens"
+menu item under "Logged in as ____" -> Settings. On that page they will
+be able to generate new tokens and modify or revoke existing tokens.
Once you have an authentication token, you may use it in place of a
-password to log into RT. (Additionally, L<RT::Extension::REST2> allows
+password to log into RT. (Additionally, L<REST2> allows
for using auth tokens with the C<Authorization: token> HTTP header.) One
common use case is to use an authentication token as an
application-specific password, so that you may revoke that application's
@@ -123,6 +122,7 @@ If you are running RT under Apache, add the following directive to your RT
Apache configuration to allow RT to access the Authorization header.
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
+
=cut
1;
diff --git a/lib/RT/Authen/Token/AuthToken.pm b/lib/RT/Authen/Token/AuthToken.pm
index 93720aa42e..111f5d132c 100644
--- a/lib/RT/Authen/Token/AuthToken.pm
+++ b/lib/RT/Authen/Token/AuthToken.pm
@@ -45,6 +45,7 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
+
use strict;
use warnings;
use 5.10.1;
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index a6fa51e5ed..03ec19c6c9 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -349,47 +349,7 @@ sub HandleRequest {
# Authenticate if the user is trying to login via user/pass query args
my ($authed, $msg) = AttemptPasswordAuthentication($ARGS);
-
- unless ($authed) {
- my $get_env = sub {
- my $key = shift;
- if (RT::Interface::Web->can('RequestENV')) {
- return RT::Interface::Web::RequestENV($key)
- }
- return $ENV{$key};
- };
-
- my ($pass, $user) = ('', '');
- if (($get_env->('HTTP_AUTHORIZATION')||'') =~ /^token (.*)$/i) {
- $pass ||= $1;
- }
- unless ( defined $pass ) {
- my ($user_obj, $token) = RT::Authen::Token->UserForAuthString($pass, $user);
- if ( $user_obj ) {
- # log in
- my $remote_addr = $get_env->('REMOTE_ADDR');
- $RT::Logger->info("Successful login for @{[$user_obj->Name]} from $remote_addr using authentication token #@{[$token->Id]} (\"@{[$token->Description]}\")");
-
- # It's important to nab the next page from the session before we blow
- # the session away
- my $next = RT::Interface::Web::RemoveNextPage($ARGS->{'next'});
- $next = $next->{'url'} if ref $next;
-
- RT::Interface::Web::InstantiateNewSession();
- $HTML::Mason::Commands::session{'CurrentUser'} = $user_obj;
-
- # Really the only time we don't want to redirect here is if we were
- # passed user and pass as query params in the URL.
- if ($next) {
- RT::Interface::Web::Redirect($next);
- }
- elsif ($ARGS->{'next'}) {
- # Invalid hash, but still wants to go somewhere, take them to /
- RT::Interface::Web::Redirect(RT->Config->Get('WebURL'));
- }
- }
- }
- }
+ AttemptTokenAuthentification($ARGS);
unless ($authed) {
my $m = $HTML::Mason::Commands::m;
@@ -909,6 +869,50 @@ sub AttemptPasswordAuthentication {
}
}
+sub AttemptTokenAuthentification {
+ my $ARGS = shift;
+ return if RT::Interface::Web::_UserLoggedIn();
+
+ my $get_env = sub {
+ my $key = shift;
+ if (RT::Interface::Web->can('RequestENV')) {
+ return RT::Interface::Web::RequestENV($key)
+ }
+ return $ENV{$key};
+ };
+
+ my ($pass, $user) = ('', '');
+ if (($get_env->('HTTP_AUTHORIZATION')||'') =~ /^token (.*)$/i) {
+ $pass ||= $1;
+ }
+ return unless defined $pass;
+
+ my ($user_obj, $token) = RT::Authen::Token->UserForAuthString($pass, $user);
+ if ( $user_obj ) {
+ # log in
+ my $remote_addr = $get_env->('REMOTE_ADDR');
+ $RT::Logger->info("Successful login for @{[$user_obj->Name]} from $remote_addr using authentication token #@{[$token->Id]} (\"@{[$token->Description]}\")");
+
+ # It's important to nab the next page from the session before we blow
+ # the session away
+ my $next = RT::Interface::Web::RemoveNextPage($ARGS->{'next'});
+ $next = $next->{'url'} if ref $next;
+
+ RT::Interface::Web::InstantiateNewSession();
+ $HTML::Mason::Commands::session{'CurrentUser'} = $user_obj;
+
+ # Really the only time we don't want to redirect here is if we were
+ # passed user and pass as query params in the URL.
+ if ($next) {
+ RT::Interface::Web::Redirect($next);
+ }
+ elsif ($ARGS->{'next'}) {
+ # Invalid hash, but still wants to go somewhere, take them to /
+ RT::Interface::Web::Redirect(RT->Config->Get('WebURL'));
+ }
+ }
+}
+
=head2 LoadSessionFromCookie
Load or setup a session cookie for the current user.
diff --git a/lib/RT/Interface/Web/MenuBuilder.pm b/lib/RT/Interface/Web/MenuBuilder.pm
index 42740c32cc..4cf0fd111b 100644
--- a/lib/RT/Interface/Web/MenuBuilder.pm
+++ b/lib/RT/Interface/Web/MenuBuilder.pm
@@ -917,6 +917,13 @@ sub BuildMainNav {
$page->child( history => title => loc('History'), path => "/Admin/Tools/ConfigHistory.html" );
}
+ if ( $request_path =~ m{^/Prefs/AuthTokens} ) {
+ $page->child( select_auth_token => title => loc('Select'), path => '/Prefs/AuthTokens.html');
+ $page->child( create_auth_token => title => loc('Create'),
+ raw_html => q[<a class="btn menu-item" href="#create-auth-token" data-toggle="modal" rel="modal:open">].loc("Create")."</a>"
+ );
+ }
+
# due to historical reasons of always having been in /Elements/Tabs
$HTML::Mason::Commands::m->callback( CallbackName => 'Privileged', Path => $request_path, Search_Args => $args, Has_Query => $has_query, ARGSRef => \%args, CallbackPage => '/Elements/Tabs' );
}
@@ -1346,6 +1353,16 @@ sub _BuildAdminMenu {
$page->child( keys => title => loc('Private keys'), path => "/Admin/Users/Keys.html?id=" . $id );
}
$page->child( 'summary' => title => loc('User Summary'), path => "/User/Summary.html?id=" . $id );
+
+ my $auth_tokens = $page->child(auth_tokens => title => loc('Auth Tokens'), path => '/Admin/Users/AuthTokens.html?id=' . $id);
+
+ # Only show the create option on the auth token select page because we use a modal
+ if ( $request_path =~ m{^(/Admin/Users/AuthTokens.html)} and $admin->child("users") ) {
+ $auth_tokens->child( select_auth_token => title => loc('Select'), path => '/Admin/Users/AuthTokens.html?id='.$id);
+ $auth_tokens->child( create_auth_token => title => loc('Create'),
+ raw_html => q[<a class="btn menu-item" href="#create-auth-token" data-toggle="modal" rel="modal:open">].loc("Create")."</a>"
+ );
+ }
}
}
@@ -1535,20 +1552,6 @@ sub _BuildAdminMenu {
$page->child( create => title => loc('Create'), path => "/Admin/Articles/Classes/Modify.html?Create=1" );
}
}
-
- my $request_path_token = $request_path =~ s!/{2,}!/!g;
- if ( $request_path_token =~ m{^(/Admin/Users|/User/(Summary|History)\.html)} and $admin->child("users") ) {
- if ( $HTML::Mason::Commands::DECODED_ARGS->{'id'} && $HTML::Mason::Commands::DECODED_ARGS->{'id'} =~ /^\d+$/ ) {
- my $id = $HTML::Mason::Commands::DECODED_ARGS->{'id'};
- my $obj = RT::User->new( $HTML::Mason::Commands::session{'CurrentUser'} );
- $obj->Load($id);
-
- if ( $obj and $obj->id ) {
- my $tabs = PageMenu();
- $tabs->child(auth_tokens => title => loc('Auth Tokens'), path => '/Admin/Users/AuthTokens.html?id=' . $id);
- }
- }
- }
}
sub BuildSelfServiceNav {
diff --git a/share/html/Helpers/AuthToken/Create b/share/html/Admin/Users/AuthToken/Create.html
similarity index 86%
rename from share/html/Helpers/AuthToken/Create
rename to share/html/Admin/Users/AuthToken/Create.html
index 1ff6d3ac0d..97317312c3 100644
--- a/share/html/Helpers/AuthToken/Create
+++ b/share/html/Admin/Users/AuthToken/Create.html
@@ -45,5 +45,19 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<& /Elements/AuthToken/CreateResults, %ARGS &>
-% $m->abort;
+
+<& /Elements/AuthToken/Create, Owner => $UserObj->Id, %ARGS &>
+
+<%ARGS>
+$id => undef
+</%ARGS>
+<%INIT>
+my @results;
+
+my $UserObj = RT::User->new( $session{'CurrentUser'} );
+$UserObj->Load( $id );
+unless ( $UserObj->id ) {
+ Abort( loc("Couldn't load user #[_1]", $id) );
+}
+$id = $ARGS{'id'} = $UserObj->id;
+</%INIT>
diff --git a/share/html/Admin/Users/AuthTokens.html b/share/html/Admin/Users/AuthTokens.html
index 34de303304..e7368099cd 100644
--- a/share/html/Admin/Users/AuthTokens.html
+++ b/share/html/Admin/Users/AuthTokens.html
@@ -45,30 +45,13 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<& /Admin/Elements/Header, Title => loc("[_1]'s authentication tokens",$UserObj->Name) &>
-<& /Elements/Tabs &>
-<& /Elements/ListActions, actions => \@results &>
-<div class="form-row">
- <div class="auth-tokens col-12">
- <p><&|/l&>Authentication tokens allow other applications to use your user
- account without having to share your password, while allowing you to
- revoke access on an application-specific basis. Changing your password
- <em>does not</em> invalidate your auth tokens; you must revoke them here.</&>
- </p>
- </div>
-</div>
+<& /Elements/Header, Title => loc('My authentication tokens') &>
+<& /Elements/Tabs &>
-<& /Elements/AuthToken/CreateButton, %ARGS, Owner => $UserObj->Id &>
-<& /Elements/AuthToken/List, %ARGS, Owner => $UserObj->Id &>
-</div>
+<& /Elements/AuthToken/AuthTokens, Owner => $id, Path => '/Prefs/AuthTokens.html', %ARGS &>
-<%ARGS>
-$id => undef
-</%ARGS>
<%INIT>
-my @results;
-
my $UserObj = RT::User->new( $session{'CurrentUser'} );
$UserObj->Load( $id );
unless ( $UserObj->id ) {
@@ -76,3 +59,7 @@ unless ( $UserObj->id ) {
}
$id = $ARGS{'id'} = $UserObj->id;
</%INIT>
+
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/share/html/Elements/AuthToken/AuthTokens b/share/html/Elements/AuthToken/AuthTokens
new file mode 100644
index 0000000000..0a031ff856
--- /dev/null
+++ b/share/html/Elements/AuthToken/AuthTokens
@@ -0,0 +1,146 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<& /Elements/ListActions, actions => \@results &>
+
+<div class="form-row">
+ <div class="col-12">
+ <p><&|/l&>Authentication tokens allow other applications to use your user account without having to share your password, while allowing you to revoke access on an application-specific basis. Changing your password <em>does not</em> invalidate your auth tokens; you must revoke them here.</&></p>
+ </div>
+
+% if ( $Authstring ) {
+ <div class="modal authtoken-success" id="auth-token-auth-string">
+ <div class="modal-dialog modal-dialog-centered" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <&|/l, $Description &>This is your new authentication token. Treat it carefully like a password. Please save it now because you cannot access it again.</&>
+ <a id="auth-token-close-modal" href="javascript:void(0)" class="close" data-dismiss="modal" aria-label="Close">
+ <span aria-hidden="true">×</span>
+ </a>
+ </div>
+ <div class="col-12 authstring text-center">
+ <span><% $Authstring %></span>
+ </div>
+ </div>
+ </div>
+ </div>
+% }
+
+ <div class="col-12">
+ <& /Elements/AuthToken/Create, Path => $Path, Owner => $Owner &>
+ </div>
+
+ <div class="col-12">
+ <& /Elements/AuthToken/List, Path => $Path, Owner => $Owner &>
+ </div>
+</div>
+
+<%INIT>
+my @results;
+
+if ( $Update || $Revoke ) {
+ my $error = '';
+
+ my $token = RT::Authen::Token::AuthToken->new( $session{CurrentUser} );
+ $token->Load( $ARGS{'Token'} );
+ my ($ok, $msg);
+ if ( $Update ) {
+ if ( !length( $Description ) ) {
+ push @results, loc( "Description cannot be blank." );
+ }
+
+ if ( $Description ne $token->Description ) {
+ ($ok, $msg) = $token->SetDescription( $Description );
+ push @results, $msg;
+ }
+ }
+ elsif ($Revoke) {
+ ($ok, $msg) = $token->Delete;
+ push @results, $msg;
+ }
+}
+
+my ($authstring);
+if ( $CreateToken ) {
+ my $token = RT::Authen::Token::AuthToken->new( $session{CurrentUser} );
+
+ # Don't require password for systems with some form of federated auth
+ my %res = $session{'CurrentUser'}->CurrentUserRequireToSetPassword();
+
+ if ( !length( $Description ) ) {
+ push @results, loc("Description cannot be blank.");
+ }
+ elsif ( $res{'CanSet'} && !length( $ARGS{'Password'} ) ) {
+ push @results, loc("Please enter your current password.");
+ }
+ elsif ( $res{'CanSet'} && !$session{CurrentUser}->IsPassword($ARGS{'Password'} ) ) {
+ push @results, loc("Please enter your current password correctly.");
+ }
+ else {
+ ((my $ok), (my $msg), $Authstring) = $token->Create(
+ Owner => $Owner,
+ Description => $Description,
+ );
+ if ( $ok ) {
+ push @results, loc( "New token successfully created" );
+ }
+ else {
+ push @results, loc( "Something went wrong" );
+ }
+ }
+}
+</%INIT>
+
+<%ARGS>
+$Path
+$Owner
+$Update => 0
+$Revoke => 0
+$CreateToken => 0
+$Authstring => ''
+$Description => ''
+</%ARGS>
diff --git a/share/html/Elements/AuthToken/CreateForm b/share/html/Elements/AuthToken/Create
similarity index 84%
rename from share/html/Elements/AuthToken/CreateForm
rename to share/html/Elements/AuthToken/Create
index 8a73fb9b5d..bc4409657a 100644
--- a/share/html/Elements/AuthToken/CreateForm
+++ b/share/html/Elements/AuthToken/Create
@@ -45,26 +45,17 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<%ARGS>
-$Owner
-$Description => ''
-</%ARGS>
-<%INIT>
-# Don't require password for systems with some form of federated auth
-my %res = $session{'CurrentUser'}->CurrentUserRequireToSetPassword();
-</%INIT>
-
<div class="modal" id="create-auth-token">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><&|/l&>Create auth token</&></h5>
- <a href="javascript:void(0)" class="close" data-dismiss="modal" aria-label="Close">
- <span aria-hidden="true">×</span>
+ <a id="auth-token-close-modal" href="javascript:void(0)" class="close" data-dismiss="modal" aria-label="Close">
+ <span aria-hidden="true">×</span>
</a>
</div>
<div class="modal-body">
- <form class="authtoken-form" method="post" data-ajax-url="<% RT->Config->Get('WebPath') %>/Helpers/AuthToken/Create">
+ <form class="authtoken-form" method="POST" action="<% RT->Config->Get('WebPath') . $Path %>">
<div class="form-row">
<input type="hidden" name="Owner" value="<% $Owner %>">
% if ( $res{'CanSet'} ){
@@ -88,16 +79,19 @@ my %res = $session{'CurrentUser'}->CurrentUserRequireToSetPassword();
<& /Elements/Submit, Label => loc("Create"), Name => 'CreateToken' &>
</div>
</div>
-
- <span class="loading"><img src="<%RT->Config->Get('WebPath')%>/static/images/loading.gif" alt="<%loc('Loading')%>" title="<%loc('Loading')%>" /></span>
</form>
</div>
</div>
</div>
</div>
-<div class="form-row">
- <div class="col-12">
- <a class="button btn btn-primary" href="#create-auth-token" data-toggle="modal" rel="modal:open" name="create_auth_token"><&|/l&>Create Auth Token</&></a>
- </div>
-</div>
+<%INIT>
+# Don't require password for systems with some form of federated auth
+my %res = $session{'CurrentUser'}->CurrentUserRequireToSetPassword();
+</%INIT>
+
+<%ARGS>
+$Path
+$Owner
+$Description => ''
+</%ARGS>
diff --git a/share/html/Elements/AuthToken/CreateButton b/share/html/Elements/AuthToken/CreateButton
deleted file mode 100644
index 58c386e302..0000000000
--- a/share/html/Elements/AuthToken/CreateButton
+++ /dev/null
@@ -1,72 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
-%# <sales at bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-<%ARGS>
-$Owner
-$ShowCreateForm => 0
-$CreateToken => 0
-</%ARGS>
-<%INIT>
-</%INIT>
-% if ($CreateToken) {
- <&| /Widgets/TitleBox, title => loc("Create Auth Token") &>
- <& /Elements/AuthToken/CreateResults, %ARGS &>
- </&>
-% } elsif ($ShowCreateForm) {
- <&| /Widgets/TitleBox, title => loc("Create Auth Token") &>
- <& /Elements/AuthToken/CreateForm, Owner => $Owner &>
- </&>
-% } else {
-<div class="authtoken-form-container">
- <& /Elements/AuthToken/CreateForm, Owner => $Owner &>
-</div>
-<form method="GET">
- <input type="hidden" name="ShowCreateForm" value="1">
- <input type="hidden" name="id" value="<% $Owner %>">
- <button type="submit" class="authtoken-create">Create Auth Token</button>
-</form>
-% }
diff --git a/share/html/Elements/AuthToken/CreateResults b/share/html/Elements/AuthToken/CreateResults
deleted file mode 100644
index 58bb9da202..0000000000
--- a/share/html/Elements/AuthToken/CreateResults
+++ /dev/null
@@ -1,96 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
-%# <sales at bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-<%ARGS>
-$Owner => undef
-$Password => ''
-$Description => ''
-</%ARGS>
-<%INIT>
-my $token = RT::AuthToken->new($session{CurrentUser});
-# Don't require password for systems with some form of federated auth
-my %res = $session{'CurrentUser'}->CurrentUserRequireToSetPassword();
-my ($error, $authstring);
-
-if (!$Owner) {
- $error = loc("Owner required. Please refresh the page and try again.");
-}
-elsif (!length($Description)) {
- $error = loc("Description cannot be blank.");
-}
-elsif ($res{'CanSet'} && !length($Password)) {
- $error = loc("Please enter your current password.");
-}
-elsif ($res{'CanSet'} && !$session{CurrentUser}->IsPassword($Password) ) {
- $error = loc("Please enter your current password correctly.");
-}
-else {
- ((my $ok), (my $msg), $authstring) = $token->Create(
- Owner => $Owner,
- Description => $Description,
- );
-}
-</%INIT>
-<div class="form-row">
-% if ($error) {
- <& /Elements/AuthToken/CreateForm, Owner => $Owner, Error => $error, Description => $Description &>
-% } else {
- <div class="authtoken-success">
- <div class="col-12">
- <p><&|/l, $Description&>This is your new authentication token. Treat
- it carefully like a password. Please save it now because you cannot
- access it again.
- </&></p>
- </div>
- <div class="col-12 text-center">
- <span class="authstring"><% $authstring %></span>
- </div>
- </div>
- </div>
-</div>
-% }
-
diff --git a/share/html/Elements/AuthToken/List b/share/html/Elements/AuthToken/List
index c6f729c295..774e089bc0 100644
--- a/share/html/Elements/AuthToken/List
+++ b/share/html/Elements/AuthToken/List
@@ -45,13 +45,7 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<%ARGS>
-$Owner
-</%ARGS>
-<%INIT>
-my $tokens = RT::Authen::Token::AuthTokens->new($session{CurrentUser});
-$tokens->LimitOwner(VALUE => $Owner);
-</%INIT>
+
<div class="authtoken-list" data-owner="<% $Owner %>">
<span class="loading"><img src="<%RT->Config->Get('WebPath')%>/static/images/loading.gif" alt="<%loc('Loading')%>" title="<%loc('Loading')%>" /></span>
% if ($tokens->Count == 0) {
@@ -81,7 +75,7 @@ $tokens->LimitOwner(VALUE => $Owner);
<& /Elements/AuthToken/ModifyResults, %ARGS, Token => $token->Id, Owner => $Owner &>
% }
% }
- <form class="authtoken-form" method="post" data-ajax-url="<% RT->Config->Get('WebPath') %>/Helpers/AuthToken/Modify" action="<% RT->Config->Get('WebPath') %><% $r->uri %>">
+ <form class="authtoken-form" method="post" action="<% RT->Config->Get('WebPath') . $Path %>">
<div class="form-row">
% if ($ARGS{id}) {
<input type="hidden" name="id" value="<% $ARGS{id} %>">
@@ -136,3 +130,13 @@ $tokens->LimitOwner(VALUE => $Owner);
</ul>
% }
</div>
+
+<%INIT>
+my $tokens = RT::Authen::Token::AuthTokens->new($session{CurrentUser});
+$tokens->LimitOwner(VALUE => $Owner);
+</%INIT>
+
+<%ARGS>
+$Owner
+$Path
+</%ARGS>
diff --git a/share/html/Elements/AuthToken/ModifyForm b/share/html/Elements/AuthToken/ModifyForm
deleted file mode 100644
index c57dbc10f1..0000000000
--- a/share/html/Elements/AuthToken/ModifyForm
+++ /dev/null
@@ -1,123 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
-%# <sales at bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-<%ARGS>
-$Token => undef
-$TokenObj => undef
-$Error => ''
-</%ARGS>
-<%INIT>
-if (!$TokenObj) {
- $TokenObj = RT::Authen::Token::AuthToken->new($session{CurrentUser});
- $TokenObj->Load($Token);
-}
-Abort("Unable to load authentication token") if !$TokenObj->Id;
-Abort("Permission Denied") if !$TokenObj->CurrentUserCanSee;
-</%INIT>
-
-<div class="modal" id="edit-auth-token">
- <div class="modal-dialog modal-dialog-centered" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <h5 class="modal-title"><&|/l&>Edit auth token</&></h5>
- <a href="javascript:void(0)" class="close" data-dismiss="modal" aria-label="Close">
- <span aria-hidden="true">×</span>
- </a>
- </div>
- <div class="modal-body">
- <form class="authtoken-form" method="post" data-ajax-url="<% RT->Config->Get('WebPath') %>/Helpers/AuthToken/Modify" action="<% RT->Config->Get('WebPath') %><% $r->uri %>">
- <div class="form-row">
-% if ($Error) {
- <div class="col-12">
- <p class="error"><% $Error %></p>
- </div>
-% }
-% if ($ARGS{id}) {
- <input type="hidden" name="id" value="<% $ARGS{id} %>">
-% }
- <input type="hidden" name="Token" value="<% $TokenObj->id %>">
-
- <div class="form-row">
- <div class="col-4 label">
- <&|/l&>Description</&>:<br><em><&|/l&>What's this token for?</&></em>
- </div>
- <div class="col-8 value">
- <input class="form-control" type="text" name="Description" value="<% $ARGS{Description} // $TokenObj->Description %>" size="16" />
- </div>
-
- <div class="col-4 label">
- <&|/l&>Last Used</&>:
- </div>
- <div class="col-8 value">
-% my $used = $TokenObj->LastUsedObj;
-% if ($used->IsSet) {
- <% $used->AgeAsString %>
-% } else {
- <&|/l&>never</&>
-% }
- </div>
-
- <div class="col-4 label">
- <&|/l&>Created</&>:
- </div>
- <div class="col-8 value">
- <% $TokenObj->CreatedObj->AgeAsString %>
- </div>
- </div>
-
- <div class="buttons">
- <input type="submit" name="Update" value="<&|/l&>Save</&>"></input>
- <input type="submit" name="Revoke" value="<&|/l&>Revoke</&>"></input>
- </div>
-
- <span class="loading"><img src="<%RT->Config->Get('WebPath')%>/static/images/loading.gif" alt="<%loc('Loading')%>" title="<%loc('Loading')%>" /></span>
- </div>
- </form>
- </div>
- </div>
- </div>
-</div>
diff --git a/share/html/Elements/AuthToken/ModifyResults b/share/html/Elements/AuthToken/ModifyResults
deleted file mode 100644
index 0bb6ac7a72..0000000000
--- a/share/html/Elements/AuthToken/ModifyResults
+++ /dev/null
@@ -1,83 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
-%# <sales at bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-<%ARGS>
-$Token
-$Description => ''
-$Update => 0
-$Revoke => 0
-</%ARGS>
-<%INIT>
-my $TokenObj = RT::Authen::Token::AuthToken->new($session{CurrentUser});
-$TokenObj->Load($Token);
-my ($error, $ok, $msg);
-
-if ($Update) {
- if (!length($Description)) {
- $error = loc("Description cannot be blank.");
- }
-
- if ($Description ne $TokenObj->Description) {
- ($ok, $msg) = $TokenObj->SetDescription($Description);
- $error = $msg if !$ok;
- }
-}
-elsif ($Revoke) {
- ($ok, $msg) = $TokenObj->Delete;
-}
-</%INIT>
-<div id="messages" class="auth-token-messages col-6">
-% if ( $error ) {
- <div class="alert alert-danger" role="alert">
- <% $error %>
- </div>
-% } if ( $msg ) {
- <div class="alert alert-success" role="alert">
- <% $msg %>
- </div>
-% }
-</div>
diff --git a/share/html/Helpers/AuthToken/List b/share/html/Helpers/AuthToken/List
deleted file mode 100644
index 041fdf13c8..0000000000
--- a/share/html/Helpers/AuthToken/List
+++ /dev/null
@@ -1,49 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
-%# <sales at bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-<& /Elements/AuthToken/List, Owner => $ARGS{owner} &>
-% $m->abort;
diff --git a/share/html/Helpers/AuthToken/Modify b/share/html/Helpers/AuthToken/Modify
deleted file mode 100644
index 73a88f2a9d..0000000000
--- a/share/html/Helpers/AuthToken/Modify
+++ /dev/null
@@ -1,49 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
-%# <sales at bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-<& /Elements/AuthToken/ModifyResults, %ARGS &>
-% $m->abort;
diff --git a/share/html/Prefs/AuthTokens.html b/share/html/Prefs/AuthTokens.html
index 9d9e8b4d47..9bac418470 100644
--- a/share/html/Prefs/AuthTokens.html
+++ b/share/html/Prefs/AuthTokens.html
@@ -45,32 +45,12 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
+
<& /Elements/Header, Title => loc('My authentication tokens') &>
<& /Elements/Tabs &>
-<& /Elements/ListActions, actions => \@results &>
-
-<div class="form-row">
- <div class="col-12">
- <p><&|/l&>Authentication tokens allow other applications to use your user
- account without having to share your password, while allowing you to
- revoke access on an application-specific basis. Changing your password
- <em>does not</em> invalidate your auth tokens; you must revoke them here.
- </&></p>
- </div>
-
- <div class="col-12">
- <& /Elements/AuthToken/CreateForm, %ARGS, Owner => $UserObj->Id &>
- </div>
-
- <div id="auth-token-messages" class="auth-token-messages col-12">
- </div>
- <div class="col-12">
- <& /Elements/AuthToken/List, %ARGS, Owner => $UserObj->Id &>
- </div>
-</div>
+<& /Elements/AuthToken/AuthTokens, Owner => $owner, Path => '/Prefs/AuthTokens.html', %ARGS &>
<%INIT>
-my @results;
-my $UserObj = $session{'CurrentUser'}->UserObj;
+my $owner = $session{'CurrentUser'}->UserObj->Id;
</%INIT>
diff --git a/share/static/css/elevator-light/rt-authen-token.css b/share/static/css/elevator-light/rt-authen-token.css
index 80f8ccadb1..4c4163201e 100644
--- a/share/static/css/elevator-light/rt-authen-token.css
+++ b/share/static/css/elevator-light/rt-authen-token.css
@@ -68,6 +68,3 @@
float: left;
}
-.auth-token-messages {
- margin: 1rem 0rem 0rem 0px;
-}
diff --git a/share/static/images/loading.gif b/share/static/images/loading.gif
deleted file mode 100644
index 3288d1035d..0000000000
Binary files a/share/static/images/loading.gif and /dev/null differ
diff --git a/share/static/js/rt-authen-token.js b/share/static/js/rt-authen-token.js
index e989851335..a1fbb6a2a9 100644
--- a/share/static/js/rt-authen-token.js
+++ b/share/static/js/rt-authen-token.js
@@ -1,67 +1,3 @@
jQuery(function() {
- var refreshTokenList = function () {
- var list = jQuery('.authtoken-list');
- jQuery.post(
- RT.Config.WebHomePath + "/Helpers/AuthToken/List",
- list.data(),
- function (data) {
- list.replaceWith(data);
- }
- );
- };
-
- var submitForm = function (form, extraParams) {
- var payload = form.serializeArray();
- var name = extraParams[0].name;
-
- if (extraParams) {
- Array.prototype.push.apply(payload, extraParams);
- }
-
- form.addClass('submitting');
- form.find('input').attr('disabled', true);
-
- var renderResult = function(name, html) {
- if ( name === 'CreateToken' ) {
- var form = jQuery('.modal .authtoken-form');
- if (form.length) {
- form.replaceWith(html);
- }
- else {
- jQuery('#body').append(html);
- }
- }
- else {
- jQuery('#auth-token-messages').replaceWith(html);
- }
- refreshTokenList();
- };
-
- jQuery.ajax({
- method: 'POST',
- url: form.data('ajax-url'),
- data: payload,
- timeout: 30000, /* 30 seconds */
- success: function (data, status) {
- renderResult(name, data);
- },
- error: function (xhr, status, error) {
- renderResult("<p>An error has occurred. Please refresh the page and try again.<p>");
- }
- });
- };
-
- jQuery('body').on('click', '.authtoken-form button, .authtoken-form input[type=submit]', function (e) {
- e.preventDefault();
- var button = jQuery(this);
-
- var params = [{ name: button.attr('name'), value: button.attr('value') }];
- submitForm(button.closest('form'), params);
- });
-
- jQuery('body').on('submit', '.authtoken-form', function (e) {
- e.preventDefault();
- submitForm(jQuery(this));
- });
+ jQuery('#auth-token-auth-string').modal('show');
});
-
commit 8600ff25d85d8d96139a77438621a794eb7e426c
Author: Craig <craig at bestpractical.com>
Date: Thu May 7 14:18:35 2020 -0400
Add documentation for using token auth
diff --git a/docs/authentication.pod b/docs/authentication.pod
index 882805ed21..4552f856fd 100644
--- a/docs/authentication.pod
+++ b/docs/authentication.pod
@@ -86,6 +86,11 @@ An example of using LDAP authentication and HTTP Basic auth:
</Location>
+If you using token authorization then add the following directive to your RT
+Apache configuration to allow RT to access the Authorization header.
+
+ SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
+
=head3 RT Configuration Options
All of the following options control the behavior of RT's built-in external
diff --git a/docs/web_deployment.pod b/docs/web_deployment.pod
index 779401f985..c46933971f 100644
--- a/docs/web_deployment.pod
+++ b/docs/web_deployment.pod
@@ -32,6 +32,11 @@ spontaneously logged in as other users in the system.
See also L<authentication/Apache configuration>, in case you intend to
use Apache to provide authentication.
+If you using token authorization then add the following directive to your RT
+Apache configuration to allow RT to access the Authorization header.
+
+ SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
+
=head3 mod_fcgid
B<WARNING>: Before mod_fcgid 2.3.6, the maximum request size was 1GB.
diff --git a/lib/RT/Authen/Token.pm b/lib/RT/Authen/Token.pm
index cf9c824d89..e7358b47c8 100644
--- a/lib/RT/Authen/Token.pm
+++ b/lib/RT/Authen/Token.pm
@@ -92,6 +92,8 @@ sub UserForAuthString {
RT-Authen-Token - token-based authentication
+=cut
+
=head1 DESCRIPTION
Allow for users to generate and login with authentication tokens.
@@ -116,7 +118,7 @@ Authentication tokens are stored securely (hashed and salted) in the
database just like passwords, and so cannot be recovered after they are
generated.
-=item Update your Apache configuration
+=head2 Update your Apache configuration
If you are running RT under Apache, add the following directive to your RT
Apache configuration to allow RT to access the Authorization header.
-----------------------------------------------------------------------
More information about the rt-commit
mailing list