UNPKG

6.52 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 loadUser(info) {
62 return this.login(info)
63 }
64
65 loginIsExpired() {
66 return !this.getConfig().token
67 && (this.state.expires !== null
68 && (Date.now() - this.state.expires) > 0)
69 }
70
71 login(config) {
72
73 if(config) {
74 const url = config.url || this.getConfig().url
75 if (config.token) {
76 this.setConfig({
77 url, token: config.token
78 })
79 } else if (config.domain) {
80 this.setConfig({
81 url,
82 domain: config.domain,
83 username: config.username,
84 password: config.password
85 })
86 } else {
87 this.setConfig({
88 url,
89 username: config.username,
90 password: config.password
91 })
92 }
93 }
94
95 // User available
96 if(this.getUser()) {
97
98 // try to refresh only if logged with username & password
99 if (this.loginIsExpired()) {
100 d("Refreshing session token [user=%s]", this.getConfig().username)
101 return this.refreshToken()
102 }
103
104 d("User avail [%s]", this.getUser().username)
105 return Promise.resolve(this.getUser())
106 }
107
108 const credentials = this.getConfig()
109 let promise = null
110
111 // clean up previous state
112 this.reset()
113
114 if(credentials.clientId) {
115 promise = this.getAccessToken(credentials)
116 .then(() => this.getContainer().Admin().User().read())
117 .then((user) => {
118 return Promise.resolve({ user, token: credentials.token })
119 })
120
121 } else if(credentials.token) {
122 this.setToken(credentials.token)
123 promise = this.getContainer().Admin().User().read()
124 .then((user) => {
125 return Promise.resolve({ user, token: credentials.token })
126 })
127 }
128 else {
129
130 if(!credentials.username || !credentials.password) {
131 return Promise.reject(new Error("Username and password are required to login"))
132 }
133
134 d("Login user %s", credentials.username)
135 promise = this.getClient().post(this.route("LOGIN"), credentials)
136 }
137
138 return promise
139 .then(({user, token}) => {
140 this.setToken(token)
141 this.setUser(user)
142 this.setExpires(newExpires())
143 return Promise.resolve(this.getUser())
144 })
145 }
146
147 getAccessToken(credentials) {
148
149 const client = new oauth2(credentials, (method, url, body, headers) => {
150 return this.getClient().baseRequest({
151 method, url, body, headers, baseUrl: null,
152 }).then((body) => {
153 // adapt response, wrap in { body: ... }
154 return Promise.resolve({ body: JSON.stringify(body) })
155 })
156 })
157
158 let p
159 switch (credentials.type) {
160 default:
161 case "client_credentials":
162 p = client.credentials.getToken()
163 break
164 }
165
166 return p.then((info) => {
167 d("Retrieved oauth2 token")
168 this.setOAuth2Credentials(info)
169 this.setToken(info.accessToken)
170 this.setExpires(info.expires.getTime())
171 return this.getContainer().Admin().User().read()
172 })
173 }
174
175 logout() {
176 return this.getClient().delete(this.route("LOGOUT"))
177 .then(() => {
178 this.reset()
179 return Promise.resolve()
180 })
181 }
182
183 refreshToken() {
184
185 if(this.getOAuth2Credentials()) {
186 return this.getOAuth2Credentials().refresh()
187 .then((info) => {
188 this.setOAuth2Credentials(info)
189 this.setToken(info.accessToken)
190 this.setExpires(info.expires.getTime())
191 return Promise.resolve()
192 })
193 }
194
195 return this.getClient().get(this.route("REFRESH_TOKEN"))
196 .then((res) => {
197 this.setToken(res.token)
198 this.setExpires(newExpires())
199 return Promise.resolve(this.getUser())
200 })
201 }
202
203 /**
204 * @deprecated use sync() instead
205 */
206 syncDevice(req) {
207 return this.getClient().post(this.route("DEVICE_SYNC"), req)
208 }
209
210 sync(req) {
211 const invalid = ["type","permission","userId","subjectId"].filter((k) => !req[k])
212 if(invalid.length) {
213 throw new Error("Sync request has missing properties: " + invalid.join(","))
214 }
215 return this.getClient().post(this.route("ACL_SYNC"), req)
216 }
217
218 can(type, permission, subjectId, userId, domain) {
219 if (typeof type === "object") {
220 subjectId = type.subjectId
221 permission = type.permission
222 userId = type.userId
223 domain = type.domain
224 type = type.type
225 }
226
227 userId = userId || this.getContainer().Auth().getUser().uuid
228 domain = domain || this.getContainer().getConfig().domain || null
229
230 return this.getContainer().Admin().User().can({
231 userId, type, permission, subjectId, domain
232 })
233 }
234}
235
236module.exports = Auth