CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/hub/auth-token.coffee
Views: 687
1
#########################################################################
2
# This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
# License: MS-RSL – see LICENSE.md for details
4
#########################################################################
5
6
###
7
Temporary authentication token for user.
8
###
9
10
async = require('async')
11
12
random_key = require("random-key")
13
14
misc = require('@cocalc/util/misc')
15
{defaults, types, required} = misc
16
17
auth = require('./auth')
18
19
# map {account_id:{user_account_id:timestamp}}
20
ban = {}
21
22
BAN_TIME_MS = 1000*60
23
24
exports.get_user_auth_token = (opts) ->
25
opts = defaults opts, # temporary until types is more than just a WARNING
26
database : required
27
account_id : required
28
user_account_id : required
29
password : required # admin can get token by using password = ''.
30
lti : false # LTI auth mode
31
cb : required
32
types opts,
33
database : types.object.isRequired
34
account_id : types.string.isRequired
35
user_account_id : types.string.isRequired
36
password : types.string.isRequired
37
lti : types.bool # LTI auth mode
38
cb : types.func.isRequired # cb(err, auth_token)
39
40
auth_token = undefined
41
b = ban[opts.account_id]?[opts.user_account_id]
42
if b? and (new Date() - b < BAN_TIME_MS)
43
opts.cb("banned -- please wait at least #{BAN_TIME_MS/1000}s before trying again")
44
return
45
46
is_admin = false
47
is_lti = false
48
49
async.series([
50
(cb) ->
51
if opts.password != ''
52
is_admin = false
53
cb()
54
return
55
if not opts.lti
56
# must be an admin or NOPE.
57
opts.database.is_admin
58
account_id : opts.account_id
59
cb : (err, _is_admin) =>
60
is_admin = _is_admin
61
cb(err)
62
else
63
# must have an lti_id
64
opts.database.get_account
65
account_id : opts.account_id
66
columns : ["lti_id"]
67
cb : (err, lti_id) =>
68
is_lti = !!lti_id
69
cb(err)
70
(cb) ->
71
if (is_admin or is_lti) and opts.password == ''
72
# no need to do anything further
73
cb()
74
return
75
# confirm auth
76
auth.is_password_correct
77
database : opts.database
78
account_id : opts.user_account_id
79
password : opts.password
80
allow_empty_password : false # user must have a password
81
cb : (err, is_correct) ->
82
if err
83
cb(err)
84
else if not is_correct
85
# ban opts.account_id from attempting again for 1 minute (say)
86
b = ban[opts.account_id] ?= {}
87
b[opts.user_account_id] = new Date()
88
cb("incorrect password")
89
else
90
cb()
91
(cb) ->
92
# generate token
93
auth_token = random_key.generate(24)
94
# save in db
95
opts.database.save_auth_token
96
account_id : opts.user_account_id
97
auth_token : auth_token
98
ttl : 12*3600 # ttl in seconds (12 hours)
99
cb : cb
100
(cb) ->
101
# log that we created an auth_token for an account...
102
# just in case (this is entirely a security thing)
103
opts.database.log
104
event : 'get_user_auth_token'
105
value : {account_id : opts.account_id, user_account_id:opts.user_account_id, is_admin:is_admin}
106
cb : cb
107
108
], (err) ->
109
opts.cb(err, auth_token)
110
)
111
112
exports.revoke_user_auth_token = (opts) ->
113
opts = defaults opts,
114
database : required
115
auth_token : required
116
cb : required
117
types opts,
118
database : types.object.isRequired
119
auth_token : types.string.isRequired
120
cb : types.func.isRequired # cb(err, auth_token)
121
opts.database.delete_auth_token
122
auth_token : opts.auth_token
123
cb : cb
124