1
0
Fork 0
mirror of https://github.com/openstf/stf synced 2025-10-04 10:19:30 +02:00

allow user to create an access token to access stf api from user settings tab.

This commit is contained in:
Vishal Banthia 2015-11-19 19:50:33 +09:00
parent ddc24e5dc5
commit 62413b3780
21 changed files with 263 additions and 65 deletions

View file

@ -308,4 +308,22 @@ dbapi.loadDevice = function(serial) {
return db.run(r.table('devices').get(serial))
}
dbapi.saveUserAccessToken = function(email, token) {
return db.run(r.table('users').get(email).update({
accessTokens: r.row('accessTokens').default([]).append({
title: token.title
, tokenId: token.tokenId
, jwt: token.jwt
})
}))
}
dbapi.removeUserAccessToken = function(email, title) {
return db.run(r.table('users').get(email).update({
accessTokens: r.row('accessTokens').default([]).filter(function(token) {
return token('title').ne(title)
})
}))
}
module.exports = dbapi

View file

@ -8,6 +8,11 @@ module.exports = {
indexFunction: function(user) {
return user('adbKeys')('fingerprint')
}
, accessTokens: {
indexFunction: function(user) {
return user('accessTokens')('tokenId')
}
}
, options: {
multi: true
}

View file

@ -7,6 +7,7 @@ var Promise = require('bluebird')
var _ = require('lodash')
var request = Promise.promisifyAll(require('request'))
var adbkit = require('adbkit')
var uuid = require('node-uuid')
var logger = require('../../util/logger')
var wire = require('../../wire')
@ -20,6 +21,7 @@ var zmqutil = require('../../util/zmqutil')
var cookieSession = require('./middleware/cookie-session')
var ip = require('./middleware/remote-ip')
var auth = require('./middleware/auth')
var jwtutil = require('../../util/jwtutil')
module.exports = function(options) {
var log = logger.createLogger('websocket')
@ -334,6 +336,39 @@ module.exports = function(options) {
.on('user.settings.reset', function() {
dbapi.resetUserSettings(user.email)
})
.on('user.keys.accessToken.generate', function(data) {
var expiry = Date.now() + 100 * 365 * 24 * 3600
, jwt = jwtutil.encode({
payload: {
email: user.email
, name: user.name
}
, secret: options.secret
, expiry: expiry
})
var tokenId = uuid.v4()
, title = data.title
return dbapi.saveUserAccessToken(user.email, {
title: title
, tokenId: tokenId
, jwt: jwt
})
.then(function() {
socket.emit('user.keys.accessToken.generated', {
title: title
, tokenId: tokenId
, jwt: jwt
})
})
})
.on('user.keys.accessToken.remove', function(data) {
return dbapi.removeUserAccessToken(user.email, data.title)
.then(function() {
socket.emit('user.keys.accessToken.removed', data.title)
})
})
.on('user.keys.adb.add', function(data) {
return adbkit.util.parsePublicKey(data.key)
.then(function(key) {

View file

@ -5,10 +5,12 @@ module.exports.encode = function(options) {
assert.ok(options.payload, 'payload required')
assert.ok(options.secret, 'secret required')
var expiry = options.expiry || Date.now() + 24 * 3600
return jws.sign({
header: {
alg: 'HS256'
, exp: Date.now() + 24 * 3600
, exp: expiry
}
, payload: options.payload
, secret: options.secret

View file

@ -0,0 +1,26 @@
module.exports = function generateAccessTokenDirective() {
return {
restrict: 'EA',
replace: true,
scope: {
showGenerate: '=',
showClipboard: '=',
},
template: require('./generate-access-token.jade'),
controller: function($scope, UserService) {
$scope.generateForm = {
title: ''
}
$scope.generateToken = function () {
UserService.generateAccessToken($scope.generateForm.title)
$scope.closeGenerateToken()
}
$scope.closeGenerateToken = function () {
$scope.title = ''
$scope.showGenerate = false
}
}
}
}

View file

@ -0,0 +1,3 @@
.stf-generate-access-token {
}

View file

@ -0,0 +1,20 @@
.panel.panel-default.stf-generate-access-token(ng-show='showGenerate')
.panel-heading
h3.panel-title(translate) Generate Access Token
.panel-body
form.form-horizontal(name='generateAccessTokenForm', ng-submit='generateToken(title)')
.form-group
label.control-label
i.fa.fa-key.fa-fw
span(translate) Title
input(id='access-token-title', type='text', name='accessTokenTitle', ng-model='generateForm.title', ng-required='true',
text-focus-select).form-control
button.btn.btn-primary-outline.btn-sm.pull-right(type='submit')
i.fa.fa-plus.fa-fw
span(translate) Generate New Token
error-message(message='{{error}}')

View file

@ -0,0 +1,6 @@
require('./generate-access-token.css')
module.exports = angular.module('stf.tokens.generate-access-token', [
])
.directive('generateAccessToken', require('./generate-access-token-directive'))

View file

@ -0,0 +1,3 @@
module.exports = angular.module('stf.tokens', [
require('./generate-access-token').name,
])

View file

@ -8,6 +8,22 @@ module.exports = function UserServiceFactory(
var user = UserService.currentUser = AppState.user
UserService.getAccessTokens = function() {
return (user.accessTokens || (user.accessTokens = []))
}
UserService.generateAccessToken = function(title) {
socket.emit('user.keys.accessToken.generate', {
title: title
})
}
UserService.removeAccessToken = function(title) {
socket.emit('user.keys.accessToken.remove', {
title: title
})
}
UserService.getAdbKeys = function() {
return (user.adbKeys || (user.adbKeys = []))
}
@ -24,6 +40,26 @@ module.exports = function UserServiceFactory(
socket.emit('user.keys.adb.remove', key)
}
// socket.on('user.keys.accessToken.generated', function(token) {
// UserService.getAccessTokens().push(token)
// $rootScope.$broadcast('user.keys.accessTokens.updated', user.accessTokens)
// $rootScope.$apply()
// })
socket.on('user.keys.accessToken.generated', function(token) {
$rootScope.$broadcast('user.keys.accessTokens.generated', token)
$rootScope.$apply()
})
socket.on('user.keys.accessToken.removed', function(title) {
user.accessTokens = UserService.getAccessTokens().filter(function(token) {
return token.title !== title
})
$rootScope.$broadcast('user.keys.accessTokens.updated', user.accessTokens)
$rootScope.$apply()
})
socket.on('user.keys.adb.added', function(key) {
UserService.getAdbKeys().push(key)
$rootScope.$broadcast('user.keys.adb.updated', user.adbKeys)

View file

@ -1,3 +1,31 @@
module.exports = function AccessTokensCtrl() {
module.exports = function AccessTokensCtrl($scope, $http, UserService) {
$scope.accessTokens = []
$scope.newToken = null
function updateTokens() {
$scope.accessTokens = UserService.getAccessTokens()
}
$scope.removeToken = function (title) {
UserService.removeAccessToken(title)
}
$scope.tokenGenerated = function() {
$scope.accessToken = ''
$scope.showGenerated = false
UserService.getAccessTokens().push($scope.newToken)
$scope.newToken = null
updateTokens()
}
$scope.$on('user.keys.accessTokens.generated', function(event, token) {
$scope.showGenerated = true
$scope.accessTokenId = token.tokenId
$scope.newToken = token
})
$scope.$on('user.keys.accessTokens.updated', updateTokens)
updateTokens()
}

View file

@ -1,3 +1,11 @@
.stf-access-tokens {
.stf-access-tokens .access-token-generated-okay {
display: inline-block;
}
.stf-access-tokens .token-id-textarea {
resize: none;
cursor: text;
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
font-size: 12px;
width: 85%;
}

View file

@ -1,11 +1,11 @@
.widget-container.fluid-height.stf-access-tokens(ng-controller='AccessTokensCtrl')
.widget-container.fluid-height.stf-keys.stf-access-tokens(ng-controller='AccessTokensCtrl')
.heading
i.fa.fa-key
span(translate) Access Tokens
button.btn.pull-right.btn-sm(
ng-click='showAdd = !showAdd',
ng-class='{ "btn-primary-outline": !showAdd, "btn-primary": showAdd }')
ng-click='showGenerate = !showGenerate',
ng-class='{ "btn-primary-outline": !showGenerate, "btn-primary": showGenerate }')
i.fa.fa-plus.fa-fw
a(ng-href='/#!/docs/Access-Tokens').pull-right.btn.btn-sm
@ -14,16 +14,27 @@
.widget-content.padded
nothing-to-show(icon='fa-key', message='{{"No access tokens" | translate}}',
ng-if='!adbKeys.length && !showAdd')
ng-if='!accessTokens.length && !showGenerate && !showGenerated')
generate-access-token(show-clipboard='true', show-generate='showGenerate')
div(ng-show='showGenerated')
.alert.alert-info.selectable
strong(translate) Warning:
span  
span(translate) Make sure to copy your access token now. You won't be able to see it again!
br
button.btn.pull-right.btn-primary.btn-sm(ng-click='tokenGenerated()')
i.fa.fa-check.fa-fw
textarea(readonly, rows='1', text-focus-select, ng-model='accessTokenId').form-control.token-id-textarea
ul.list-group.key-list
li.list-group-item(ng-repeat='key in adbKeys').animate-repeat
li.list-group-item(ng-repeat='token in accessTokens').animate-repeat
a
i.fa.fa-key.fa-2x.fa-fw.key-list-icon
.key-list-details.selectable
.key-list-title(ng-bind='key.title')
.key-list-fingerprint(ng-bind='key.fingerprint')
.key-list-title(ng-bind='token.title')
button.btn.btn-xs.btn-danger-outline.pull-right.key-list-remove(ng-click='removeKey(key)')
button.btn.btn-xs.btn-danger-outline.pull-right.key-list-remove(ng-click='removeToken(token.title)')
i.fa.fa-trash-o
span(translate) Remove

View file

@ -1,7 +1,8 @@
require('./access-tokens.css')
module.exports = angular.module('stf.access-tokens', [
require('stf/common-ui').name
module.exports = angular.module('stf.settings.keys.access-tokens', [
require('stf/common-ui').name,
require('stf/tokens/generate-access-token').name
])
.run(["$templateCache", function ($templateCache) {
$templateCache.put(

View file

@ -1,23 +1,3 @@
.stf-adb-keys .key-list-icon {
}
.stf-adb-keys .key-list a {
padding: 10px;
border-bottom: 1px solid #dddddd;
}
.stf-adb-keys .key-list-details {
display: inline-block;
}
.stf-adb-keys .key-list-title {
color: #007aff;
font-size: 14px;
font-weight: 300;
margin: 2px 0 6px;
}
.stf-adb-keys .key-list-fingerprint {
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
font-size: 10px;
@ -25,28 +5,3 @@
color: #999999;
font-weight: 300;
}
.stf-adb-keys .key-list-remove {
display: inline-block;
}
.animate-repeat.ng-move,
.animate-repeat.ng-enter,
.animate-repeat.ng-leave {
-webkit-transition: all ease-out 150ms;
transition: all ease-out 150ms;
}
.animate-repeat.ng-leave.ng-leave-active,
.animate-repeat.ng-move,
.animate-repeat.ng-enter {
opacity: 0;
max-height: 0;
}
.animate-repeat.ng-leave,
.animate-repeat.ng-move.ng-move-active,
.animate-repeat.ng-enter.ng-enter-active {
opacity: 1;
max-height: 100px;
}

View file

@ -1,4 +1,4 @@
.widget-container.fluid-height.stf-adb-keys(ng-controller='AdbKeysCtrl')
.widget-container.fluid-height.stf-keys.stf-adb-keys(ng-controller='AdbKeysCtrl')
.heading
i.fa.fa-android
span(translate) ADB Keys

View file

@ -1,6 +1,6 @@
require('./adb-keys.css')
module.exports = angular.module('stf.settings.adb-keys', [
module.exports = angular.module('stf.settings.keys.adb-keys', [
require('stf/common-ui').name,
require('stf/keys/add-adb-key').name
])

View file

@ -1,6 +1,6 @@
require('./keys.css')
module.exports = angular.module('stf.keys', [
module.exports = angular.module('stf.settings.keys', [
require('./adb-keys').name,
require('./access-tokens').name
])

View file

@ -1,3 +1,44 @@
.stf-keys {
.stf-keys .key-list-icon {
}
.stf-keys .key-list a {
padding: 10px;
border-bottom: 1px solid #dddddd;
}
.stf-keys .key-list-details {
display: inline-block;
}
.stf-keys .key-list-title {
color: #007aff;
font-size: 14px;
font-weight: 300;
margin: 2px 0 6px;
}
.stf-keys .key-list-remove {
display: inline-block;
}
.animate-repeat.ng-move,
.animate-repeat.ng-enter,
.animate-repeat.ng-leave {
-webkit-transition: all ease-out 150ms;
transition: all ease-out 150ms;
}
.animate-repeat.ng-leave.ng-leave-active,
.animate-repeat.ng-move,
.animate-repeat.ng-enter {
opacity: 0;
max-height: 0;
}
.animate-repeat.ng-leave,
.animate-repeat.ng-move.ng-move-active,
.animate-repeat.ng-enter.ng-enter-active {
opacity: 1;
max-height: 100px;
}

View file

@ -1,5 +1,5 @@
.row
//.col-md-6
.col-md-6
div(ng-include='"settings/keys/access-tokens/access-tokens.jade"')
.col-md-6
div(ng-include='"settings/keys/adb-keys/adb-keys.jade"')