From d03b432557e0422d5b0dbd32e82d36d3f9a5b68a Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Mon, 24 Aug 2015 14:04:19 -0400 Subject: Bug 1192687 - add the ability for users to view and revoke existing sessions --- Bugzilla/Auth/Login/Cookie.pm | 11 ++++- Bugzilla/User/Session.pm | 48 +++++++++++++++++++ template/en/default/account/prefs/prefs.html.tmpl | 6 +++ .../en/default/account/prefs/sessions.html.tmpl | 56 ++++++++++++++++++++++ userprefs.cgi | 53 ++++++++++++++++++++ 5 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 Bugzilla/User/Session.pm create mode 100644 template/en/default/account/prefs/sessions.html.tmpl diff --git a/Bugzilla/Auth/Login/Cookie.pm b/Bugzilla/Auth/Login/Cookie.pm index e1faa52d0..46024bca4 100644 --- a/Bugzilla/Auth/Login/Cookie.pm +++ b/Bugzilla/Auth/Login/Cookie.pm @@ -19,7 +19,7 @@ package Bugzilla::Auth::Login::Cookie; use strict; use base qw(Bugzilla::Auth::Login); -use fields qw(_login_token); +use fields qw(_login_token _cookie); use Bugzilla::Constants; use Bugzilla::Error; @@ -58,6 +58,8 @@ sub get_login_info { @{$cgi->{'Bugzilla_cookie_list'}}; $user_id = $cookie->value if $cookie; } + trick_taint($login_cookie) if $login_cookie; + $self->cookie($login_cookie); # If the call is for a web service, and an api token is provided, check # it is valid. @@ -155,4 +157,11 @@ sub login_token { }; } +sub cookie { + my ($self, $val) = @_; + $self->{_cookie} = $val if @_ > 1; + + return $self->{_cookie}; +} + 1; diff --git a/Bugzilla/User/Session.pm b/Bugzilla/User/Session.pm new file mode 100644 index 000000000..c547867d1 --- /dev/null +++ b/Bugzilla/User/Session.pm @@ -0,0 +1,48 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::User::Session; + +use 5.10.1; +use strict; + +use parent qw(Bugzilla::Object); + +##################################################################### +# Overriden Constants that are used as methods +##################################################################### + +use constant DB_TABLE => 'logincookies'; +use constant DB_COLUMNS => qw( + cookie + userid + lastused + ipaddr + id + restrict_ipaddr +); + +use constant UPDATE_COLUMNS => qw(); +use constant VALIDATORS => {}; +use constant LIST_ORDER => 'lastused DESC'; +use constant NAME_FIELD => 'cookie'; + +# turn off auditing and exclude these objects from memcached +use constant { AUDIT_CREATES => 0, + AUDIT_UPDATES => 0, + AUDIT_REMOVES => 0, + USE_MEMCACHED => 0 }; + +# Accessors +sub id { return $_[0]->{id} } +sub userid { return $_[0]->{userid} } +sub cookie { return $_[0]->{cookie} } +sub lastused { return $_[0]->{lastused} } +sub ipaddr { return $_[0]->{ipaddr} } +sub restrict_ipaddr { return $_[0]->{restrict_ipaddr} } + +1; diff --git a/template/en/default/account/prefs/prefs.html.tmpl b/template/en/default/account/prefs/prefs.html.tmpl index 9610752ed..679a3cb30 100644 --- a/template/en/default/account/prefs/prefs.html.tmpl +++ b/template/en/default/account/prefs/prefs.html.tmpl @@ -71,6 +71,12 @@ link => "userprefs.cgi?tab=saved-searches", saveable => "1" }, + { + name => "sessions", + label => "Sessions", + link => "userprefs.cgi?tab=sessions", + saveable => "1", + }, { name => "apikey", label => "API Keys", diff --git a/template/en/default/account/prefs/sessions.html.tmpl b/template/en/default/account/prefs/sessions.html.tmpl new file mode 100644 index 000000000..13257ef01 --- /dev/null +++ b/template/en/default/account/prefs/sessions.html.tmpl @@ -0,0 +1,56 @@ +[%# This Source Code Form is subject to the terms of the Mozilla Public + # License, v. 2.0. If a copy of the MPL was not distributed with this + # file, You can obtain one at http://mozilla.org/MPL/2.0/. + # + # This Source Code Form is "Incompatible With Secondary Licenses", as + # defined by the Mozilla Public License, v. 2.0. + #%] + +[%# INTERFACE: + # sessions: array. Array of sessions this user has. + # session_max: int. Number of sessions that can be displayed at once. + # session_count: int. Total of number of sessions for the user. + # too_many_sessions: boolean. True if there are more than 20 sessions. + #%] + +

Here you can see your active [% terms.Bugzilla %] sessions. + You can logout these sessions, which means when you use [% terms.Bugzilla %] + from that location again you will have to log back in.

+ +

Note that you may not logout your current session from this page. + You can use the "Logout" link from the top right menu for that.

+ +

Active Sessions

+ +[% IF too_many_sessions %] +

You have [% session_count FILTER html %] sessions. Display limited to most + recent [% session_max FILTER html %].

+[% END %] + +

+ +

+ + + + + + + + + + [% FOREACH session IN sessions %] + + + + + [% END %] + + [% END %] +
Last usedIP AddressIP RestrictionLogout
[% session.lastused FILTER time %][% session.ipaddr OR "Unknown" FILTER html %][% session.restrict_ipaddr ? "Restricted" : "Unrestricted" FILTER html %] + + [% IF session.current %] + (current) + [% ELSE %] +
\ No newline at end of file diff --git a/userprefs.cgi b/userprefs.cgi index 8f18de8c4..72a8dfb69 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -35,9 +35,12 @@ use Bugzilla::Util; use Bugzilla::Error; use Bugzilla::User; use Bugzilla::User::Setting qw(clear_settings_cache); +use Bugzilla::User::Session; use Bugzilla::User::APIKey; use Bugzilla::Token; +use constant SESSION_MAX => 20; + my $template = Bugzilla->template; local our $vars = {}; @@ -539,6 +542,51 @@ sub SaveSavedSearches { Bugzilla->memcached->clear({ table => 'profiles', id => $user->id }); } +sub SaveSessions { + my $cgi = Bugzilla->cgi; + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; + + # Do it in a transaction. + $dbh->bz_start_transaction; + if ($cgi->param("session_logout_all")) { + my $info_getter = $user->authorizer && $user->authorizer->successful_info_getter(); + if ($info_getter->cookie) { + $dbh->do("DELETE FROM logincookies WHERE userid = ? AND cookie != ?", undef, + $user->id, $info_getter->cookie); + } + } + else { + my @logout_ids = $cgi->param('session_logout_id'); + my $sessions = Bugzilla::User::Session->new_from_list(\@logout_ids); + foreach my $session (@$sessions) { + $session->remove_from_db if $session->userid == $user->id; + } + } + + $dbh->bz_commit_transaction; +} + +sub DoSessions { + my $user = Bugzilla->user; + my $dbh = Bugzilla->dbh; + my $sessions = Bugzilla::User::Session->match({ userid => $user->id, LIMIT => SESSION_MAX + 1 }); + my $info_getter = $user->authorizer && $user->authorizer->successful_info_getter(); + + if ($info_getter) { + foreach my $session (@$sessions) { + $session->{current} = $info_getter->cookie eq $session->{cookie}; + } + } + my ($count) = $dbh->selectrow_array("SELECT count(*) FROM logincookies WHERE userid = ?", undef, + $user->id); + + $vars->{too_many_sessions} = @$sessions == SESSION_MAX + 1; + $vars->{sessions} = $sessions; + $vars->{session_count} = $count; + $vars->{session_max} = SESSION_MAX; + pop @$sessions if $vars->{too_many_sessions}; +} sub DoApiKey { my $user = Bugzilla->user; @@ -669,6 +717,11 @@ SWITCH: for ($current_tab_name) { DoApiKey(); last SWITCH; }; + /^sessions$/ && do { + SaveSessions() if $save_changes; + DoSessions(); + last SWITCH; + }; ThrowUserError("unknown_tab", { current_tab_name => $current_tab_name }); -- cgit v1.2.3-24-g4f1b