Commit 40f95a95 by Shavonn Brown Committed by GitHub

16223 user auth token list and revoke (#17434)

* Start user auth token list on user profile (#16223)

* Now session found and first, better os regex (#16223)

* Revoke for user and admin user token manage (#16223)

* Tidying and styling (#16223)

* Tidying and styling (#16223)

* Update to use #16222 (#16223)

* Update for changes to 16222

* update per api issue
parent a20309d7
...@@ -126,7 +126,7 @@ export class SharedPreferences extends PureComponent<Props, State> { ...@@ -126,7 +126,7 @@ export class SharedPreferences extends PureComponent<Props, State> {
getOptionLabel={i => i.title} getOptionLabel={i => i.title}
onChange={(dashboard: DashboardSearchHit) => this.onHomeDashboardChanged(dashboard.id)} onChange={(dashboard: DashboardSearchHit) => this.onHomeDashboardChanged(dashboard.id)}
options={dashboards} options={dashboards}
placeholder="Chose default dashboard" placeholder="Choose default dashboard"
width={20} width={20}
/> />
</div> </div>
......
import _ from 'lodash'; import _ from 'lodash';
import { dateTime } from '@grafana/ui';
import { BackendSrv } from 'app/core/services/backend_srv'; import { BackendSrv } from 'app/core/services/backend_srv';
import { NavModelSrv } from 'app/core/core'; import { NavModelSrv } from 'app/core/core';
import { User } from 'app/core/services/context_srv'; import { User } from 'app/core/services/context_srv';
import { UserSession } from 'app/types';
export default class AdminEditUserCtrl { export default class AdminEditUserCtrl {
/** @ngInject */ /** @ngInject */
constructor($scope: any, $routeParams: any, backendSrv: BackendSrv, $location: any, navModelSrv: NavModelSrv) { constructor($scope: any, $routeParams: any, backendSrv: BackendSrv, $location: any, navModelSrv: NavModelSrv) {
$scope.user = {}; $scope.user = {};
$scope.sessions = [];
$scope.newOrg = { name: '', role: 'Editor' }; $scope.newOrg = { name: '', role: 'Editor' };
$scope.permissions = {}; $scope.permissions = {};
$scope.navModel = navModelSrv.getNav('admin', 'global-users', 0); $scope.navModel = navModelSrv.getNav('admin', 'global-users', 0);
...@@ -14,6 +17,7 @@ export default class AdminEditUserCtrl { ...@@ -14,6 +17,7 @@ export default class AdminEditUserCtrl {
$scope.init = () => { $scope.init = () => {
if ($routeParams.id) { if ($routeParams.id) {
$scope.getUser($routeParams.id); $scope.getUser($routeParams.id);
$scope.getUserSessions($routeParams.id);
$scope.getUserOrgs($routeParams.id); $scope.getUserOrgs($routeParams.id);
} }
}; };
...@@ -26,6 +30,48 @@ export default class AdminEditUserCtrl { ...@@ -26,6 +30,48 @@ export default class AdminEditUserCtrl {
}); });
}; };
$scope.getUserSessions = (id: number) => {
backendSrv.get('/api/admin/users/' + id + '/auth-tokens').then((sessions: UserSession[]) => {
sessions.reverse();
$scope.sessions = sessions.map((session: UserSession) => {
return {
id: session.id,
isActive: session.isActive,
seenAt: dateTime(session.seenAt).fromNow(),
createdAt: dateTime(session.createdAt).format('MMMM DD, YYYY'),
clientIp: session.clientIp,
browser: session.browser,
browserVersion: session.browserVersion,
os: session.os,
osVersion: session.osVersion,
device: session.device,
};
});
});
};
$scope.revokeUserSession = (tokenId: number) => {
backendSrv
.post('/api/admin/users/' + $scope.user_id + '/revoke-auth-token', {
authTokenId: tokenId,
})
.then(() => {
$scope.sessions = $scope.sessions.filter((session: UserSession) => {
if (session.id === tokenId) {
return false;
}
return true;
});
});
};
$scope.revokeAllUserSessions = (tokenId: number) => {
backendSrv.post('/api/admin/users/' + $scope.user_id + '/logout').then(() => {
$scope.sessions = [];
});
};
$scope.setPassword = () => { $scope.setPassword = () => {
if (!$scope.passwordForm.$valid) { if (!$scope.passwordForm.$valid) {
return; return;
......
...@@ -70,6 +70,7 @@ ...@@ -70,6 +70,7 @@
</div> </div>
</form> </form>
<div class="gf-form-group">
<table class="filter-table"> <table class="filter-table">
<thead> <thead>
<tr> <tr>
...@@ -97,5 +98,38 @@ ...@@ -97,5 +98,38 @@
</td> </td>
</tr> </tr>
</table> </table>
</div>
<h3 class="page-heading">Sessions</h3>
<div class="gf-form-group">
<table class="filter-table form-inline">
<thead>
<tr>
<th>Last seen</th>
<th>Logged on</th>
<th>IP address</th>
<th>Browser &amp; OS</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="session in sessions">
<td ng-if="session.isActive">Now</td>
<td ng-if="!session.isActive">{{session.seenAt}}</td>
<td>{{session.createdAt}}</td>
<td>{{session.clientIp}}</td>
<td>{{session.browser}} on {{session.os}} {{session.osVersion}}</td>
<td>
<button class="btn btn-danger btn-small" ng-click="revokeUserSession(session.id)">
<i class="fa fa-power-off"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
<button ng-if="sessions.length" class="btn btn-danger" ng-click="revokeAllUserSessions()">Logout user from all devices</button>
</div> </div>
...@@ -13,9 +13,7 @@ function convertToAlertRule(dto: AlertRuleDTO, state: string): AlertRule { ...@@ -13,9 +13,7 @@ function convertToAlertRule(dto: AlertRuleDTO, state: string): AlertRule {
stateText: stateModel.text, stateText: stateModel.text,
stateIcon: stateModel.iconClass, stateIcon: stateModel.iconClass,
stateClass: stateModel.stateClass, stateClass: stateModel.stateClass,
stateAge: dateTime(dto.newStateDate) stateAge: dateTime(dto.newStateDate).fromNow(true),
.fromNow()
.replace(' ago', ''),
}; };
if (rule.state !== 'paused') { if (rule.state !== 'paused') {
......
import config from 'app/core/config'; import config from 'app/core/config';
import { coreModule } from 'app/core/core'; import { coreModule } from 'app/core/core';
import { dateTime } from '@grafana/ui';
import { UserSession } from 'app/types';
export class ProfileCtrl { export class ProfileCtrl {
user: any; user: any;
oldTheme: any; oldTheme: any;
teams: any = []; teams: any = [];
orgs: any = []; orgs: any = [];
sessions: object[] = [];
userForm: any; userForm: any;
showTeamsList = false; showTeamsList = false;
showOrgsList = false; showOrgsList = false;
...@@ -15,6 +18,7 @@ export class ProfileCtrl { ...@@ -15,6 +18,7 @@ export class ProfileCtrl {
/** @ngInject */ /** @ngInject */
constructor(private backendSrv, private contextSrv, private $location, navModelSrv) { constructor(private backendSrv, private contextSrv, private $location, navModelSrv) {
this.getUser(); this.getUser();
this.getUserSessions();
this.getUserTeams(); this.getUserTeams();
this.getUserOrgs(); this.getUserOrgs();
this.navModel = navModelSrv.getNav('profile', 'profile-settings', 0); this.navModel = navModelSrv.getNav('profile', 'profile-settings', 0);
...@@ -27,6 +31,52 @@ export class ProfileCtrl { ...@@ -27,6 +31,52 @@ export class ProfileCtrl {
}); });
} }
getUserSessions() {
this.backendSrv.get('/api/user/auth-tokens').then((sessions: UserSession[]) => {
sessions.reverse();
const found = sessions.findIndex((session: UserSession) => {
return session.isActive;
});
if (found) {
const now = sessions[found];
sessions.splice(found, found);
sessions.unshift(now);
}
this.sessions = sessions.map((session: UserSession) => {
return {
id: session.id,
isActive: session.isActive,
seenAt: dateTime(session.seenAt).fromNow(),
createdAt: dateTime(session.createdAt).format('MMMM DD, YYYY'),
clientIp: session.clientIp,
browser: session.browser,
browserVersion: session.browserVersion,
os: session.os,
osVersion: session.osVersion,
device: session.device,
};
});
});
}
revokeUserSession(tokenId: number) {
this.backendSrv
.post('/api/user/revoke-auth-token', {
authTokenId: tokenId,
})
.then(() => {
this.sessions = this.sessions.filter((session: UserSession) => {
if (session.id === tokenId) {
return false;
}
return true;
});
});
}
getUserTeams() { getUserTeams() {
this.backendSrv.get('/api/user/teams').then(teams => { this.backendSrv.get('/api/user/teams').then(teams => {
this.teams = teams; this.teams = teams;
......
...@@ -74,3 +74,32 @@ ...@@ -74,3 +74,32 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<h3 class="page-heading">Sessions</h3>
<div class="gf-form-group">
<table class="filter-table form-inline">
<thead>
<tr>
<th>Last seen</th>
<th>Logged on</th>
<th>IP address</th>
<th>Browser &amp; OS</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="session in ctrl.sessions">
<td ng-if="session.isActive">Now</td>
<td ng-if="!session.isActive">{{session.seenAt}}</td>
<td>{{session.createdAt}}</td>
<td>{{session.clientIp}}</td>
<td>{{session.browser}} on {{session.os}} {{session.osVersion}}</td>
<td>
<button class="btn btn-danger btn-small" ng-click="ctrl.revokeUserSession(session.id)">
<i class="fa fa-power-off"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
...@@ -48,3 +48,16 @@ export interface UserState { ...@@ -48,3 +48,16 @@ export interface UserState {
orgId: number; orgId: number;
timeZone: string; timeZone: string;
} }
export interface UserSession {
id: number;
createdAt: string;
clientIp: string;
isActive: boolean;
seenAt: string;
browser: string;
browserVersion: string;
os: string;
osVersion: string;
device: string;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment