mirror of
https://github.com/openstf/stf
synced 2025-10-04 02:09:32 +02:00
allow user to create an access token to access stf api from user settings tab.
This commit is contained in:
parent
ddc24e5dc5
commit
62413b3780
21 changed files with 263 additions and 65 deletions
|
@ -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
|
||||
|
|
|
@ -8,6 +8,11 @@ module.exports = {
|
|||
indexFunction: function(user) {
|
||||
return user('adbKeys')('fingerprint')
|
||||
}
|
||||
, accessTokens: {
|
||||
indexFunction: function(user) {
|
||||
return user('accessTokens')('tokenId')
|
||||
}
|
||||
}
|
||||
, options: {
|
||||
multi: true
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.stf-generate-access-token {
|
||||
|
||||
}
|
|
@ -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}}')
|
|
@ -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'))
|
3
res/app/components/stf/tokens/index.js
Normal file
3
res/app/components/stf/tokens/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports = angular.module('stf.tokens', [
|
||||
require('./generate-access-token').name,
|
||||
])
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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%;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
])
|
||||
|
|
|
@ -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
|
||||
])
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"')
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue