UNPKG

6.8 kBJavaScriptView Raw
1
2const Base = require("./Base")
3const d = require("debug")("raptorjs:auth")
4const oauth2 = require("client-oauth2")
5
6const newExpires = () => Date.now() + (1000 * 60 * 5) // 5min
7
8class Auth extends Base {
9
10 constructor(container) {
11
12 super(container)
13
14 this.defaultState = {
15 token: null,
16 user: null,
17 expires: null,
18 oauth2: null
19 }
20 this.state = null
21
22 this.reset()
23 }
24
25 setUser(user) {
26 this.state.user = user
27 }
28
29 getUser() {
30 return this.state.user || null
31 }
32
33 setToken(token) {
34 this.state.token = token
35 }
36
37 getToken() {
38 return this.state.token || this.getConfig().token || null
39 }
40
41 setOAuth2Credentials(info) {
42 this.state.oauth2 = info
43 }
44
45 getOAuth2Credentials() {
46 return this.state.oauth2 || null
47 }
48
49 setExpires(expires) {
50 this.state.expires = expires
51 }
52
53 getExpires() {
54 return this.state.expires
55 }
56
57 reset() {
58 this.state = Object.assign({}, this.defaultState)
59 }
60
61 changePassword(req) {
62 if(!req.old) {
63 throw new Error("Old password is missing")
64 }
65 if(!req.new) {
66 throw new Error("New Password is missing")
67 }
68 return this.getClient().post(this.route("CHANGE_PASSWORD"), req)
69 }
70
71 loadUser(info) {
72 return this.login(info)
73 }
74
75 loginIsExpired() {
76 return !this.getConfig().token
77 && (this.state.expires !== null
78 && (Date.now() - this.state.expires) > 0)
79 }
80
81 login(config) {
82
83 if(config) {
84 const url = config.url || this.getConfig().url
85 if (config.token) {
86 this.setConfig({
87 url, token: config.token
88 })
89 } else if (config.domain) {
90 this.setConfig({
91 url,
92 domain: config.domain,
93 username: config.username,
94 password: config.password
95 })
96 } else {
97 this.setConfig({
98 url,
99 username: config.username,
100 password: config.password
101 })
102 }
103 }
104
105 // User available
106 if(this.getUser()) {
107
108 // try to refresh only if logged with username & password
109 if (this.loginIsExpired()) {
110 d("Refreshing session token [user=%s]", this.getConfig().username)
111 return this.refreshToken()
112 }
113
114 d("User avail [%s]", this.getUser().username)
115 return Promise.resolve(this.getUser())
116 }
117
118 const credentials = this.getConfig()
119 let promise = null
120
121 // clean up previous state
122 this.reset()
123
124 if(credentials.clientId) {
125 promise = this.getAccessToken(credentials)
126 .then(() => this.getContainer().Admin().User().read())
127 .then((user) => {
128 return Promise.resolve({ user, token: credentials.token })
129 })
130
131 } else if(credentials.token) {
132 this.setToken(credentials.token)
133 promise = this.getContainer().Admin().User().read()
134 .then((user) => {
135 return Promise.resolve({ user, token: credentials.token })
136 })
137 }
138 else {
139
140 if(!credentials.username || !credentials.password) {
141 return Promise.reject(new Error("Username and password are required to login"))
142 }
143
144 d("Login user %s", credentials.username)
145 promise = this.getClient().post(this.route("LOGIN"), credentials)
146 }
147
148 return promise
149 .then(({user, token}) => {
150 this.setToken(token)
151 this.setUser(user)
152 this.setExpires(newExpires())
153 return Promise.resolve(this.getUser())
154 })
155 }
156
157 getAccessToken(credentials) {
158
159 const client = new oauth2(credentials, (method, url, body, headers) => {
160 return this.getClient().baseRequest({
161 method, url, body, headers, baseUrl: null,
162 }).then((body) => {
163 // adapt response, wrap in { body: ... }
164 return Promise.resolve({ body: JSON.stringify(body) })
165 })
166 })
167
168 let p
169 switch (credentials.type) {
170 default:
171 case "client_credentials":
172 p = client.credentials.getToken()
173 break
174 }
175
176 return p.then((info) => {
177 d("Retrieved oauth2 token")
178 this.setOAuth2Credentials(info)
179 this.setToken(info.accessToken)
180 this.setExpires(info.expires.getTime())
181 return this.getContainer().Admin().User().read()
182 })
183 }
184
185 logout() {
186 return this.getClient().delete(this.route("LOGOUT"))
187 .then(() => {
188 this.reset()
189 return Promise.resolve()
190 })
191 }
192
193 refreshToken() {
194
195 if(this.getOAuth2Credentials()) {
196 return this.getOAuth2Credentials().refresh()
197 .then((info) => {
198 this.setOAuth2Credentials(info)
199 this.setToken(info.accessToken)
200 this.setExpires(info.expires.getTime())
201 return Promise.resolve()
202 })
203 }
204
205 return this.getClient().get(this.route("REFRESH_TOKEN"))
206 .then((res) => {
207 this.setToken(res.token)
208 this.setExpires(newExpires())
209 return Promise.resolve(this.getUser())
210 })
211 }
212
213 /**
214 * @deprecated use sync() instead
215 */
216 syncDevice(req) {
217 return this.getClient().post(this.route("DEVICE_SYNC"), req)
218 }
219
220 sync(req) {
221 const invalid = ["type","permission","userId","subjectId"].filter((k) => !req[k])
222 if(invalid.length) {
223 throw new Error("Sync request has missing properties: " + invalid.join(","))
224 }
225 return this.getClient().post(this.route("ACL_SYNC"), req)
226 }
227
228 can(type, permission, subjectId, userId, domain) {
229 if (typeof type === "object") {
230 subjectId = type.subjectId
231 permission = type.permission
232 userId = type.userId
233 domain = type.domain
234 type = type.type
235 }
236
237 userId = userId || this.getContainer().Auth().getUser().uuid
238 domain = domain || this.getContainer().getConfig().domain || null
239
240 return this.getContainer().Admin().User().can({
241 userId, type, permission, subjectId, domain
242 })
243 }
244}
245
246module.exports = Auth