Skip to content

Commit c56a4ff

Browse files
committed
Add configurable OAuth instead of hardcoded
1 parent defc6cb commit c56a4ff

4 files changed

Lines changed: 79 additions & 53 deletions

File tree

OpenFlow/src/LoginProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { DBHelper } from "./DBHelper";
2727
const safeObjectID = (s: string | number | ObjectID) => ObjectID.isValid(s) ? new ObjectID(s) : null;
2828

2929
const BaseRateLimiter = new RateLimiterMemory({
30-
points: 10,
30+
points: 40,
3131
duration: 5,
3232
});
3333

OpenFlow/src/OAuthProvider.ts

Lines changed: 71 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
import * as OAuthServer from "oauth2-server";
22
import * as winston from "winston";
33
import * as express from "express";
4-
import { TokenUser } from "openflow-api";
4+
import { TokenUser, Base } from "openflow-api";
5+
import { Config } from "./Config";
6+
import { Crypt } from "./Crypt";
57
const Request = OAuthServer.Request;
68
const Response = OAuthServer.Response;
79
export class OAuthProvider {
810
private _logger: winston.Logger;
911
private app: express.Express;
1012
public static instance: OAuthProvider = null;
11-
private clients = [{
12-
id: 'application', // TODO: Needed by refresh_token grant, because there is a bug at line 103 in https://github.com/oauthjs/node-oauth2-server/blob/v3.0.1/lib/grant-types/refresh-token-grant-type.js (used client.id instead of client.clientId)
13-
clientId: 'application',
14-
clientSecret: 'secret',
15-
grants: [
16-
'password',
17-
'refresh_token',
18-
'authorization_code'
19-
],
20-
redirectUris: []
21-
}];
13+
// private clients = [{
14+
// id: 'application', // TODO: Needed by refresh_token grant, because there is a bug at line 103 in https://github.com/oauthjs/node-oauth2-server/blob/v3.0.1/lib/grant-types/refresh-token-grant-type.js (used client.id instead of client.clientId)
15+
// clientId: 'application',
16+
// clientSecret: 'secret',
17+
// grants: [
18+
// 'password',
19+
// 'refresh_token',
20+
// 'authorization_code'
21+
// ],
22+
// redirectUris: []
23+
// }];
24+
private clients = [];
2225
private tokens = [];
2326
private codes = {};
2427
public oauthServer: any = null;
@@ -39,25 +42,31 @@ export class OAuthProvider {
3942
});
4043
(app as any).oauth = instance.oauthServer;
4144
app.all('/oauth/token', instance.obtainToken.bind(instance));
42-
app.get('/oauth/login', (req, res) => {
45+
app.get('/oauth/login', async (req, res) => {
46+
instance.clients = await Config.db.query<Base>({ _type: "oauthclient" }, null, 10, 0, null, "config", Crypt.rootToken());
47+
if (instance.clients == null || instance.clients.length == 0) return res.status(500).json({ message: 'OAuth not configured' });
4348
let state = req.params.state;
4449
if (state == null) state = encodeURIComponent(req.query.state as any);
4550
const access_type = req.query.access_type;
4651
const client_id = req.query.client_id;
4752
const redirect_uri = req.query.redirect_uri;
4853
const response_type = req.query.response_type;
4954
const scope = req.query.scope;
55+
let client = instance.getClientById(client_id);
5056
if (req.user) {
51-
// TODO: Add logic for configurering redirect url's !!!!!!!!
52-
if (instance.clients[0].redirectUris.indexOf(redirect_uri) == -1) {
53-
instance.clients[0].redirectUris.push(redirect_uri);
57+
if (client.redirectUris.length > 0) {
58+
if (client.redirectUris.indexOf(redirect_uri) == -1) {
59+
return res.status(500).json({ message: 'illegal redirect_uri ' + redirect_uri });
60+
// client.redirectUris.push(redirect_uri);
61+
}
5462
}
63+
const code = Math.random().toString(36).substr(2, 9);
5564

5665
instance._logger.info("[OAuth][" + (req.user as any).username + "] /oauth/login " + state);
57-
instance.codes["cc536d98d27750394a87ab9d057016e636a8ac31"] = req.user;
58-
instance.codes["cc536d98d27750394a87ab9d057016e636a8ac31"].redirect_uri = redirect_uri;
59-
// res.redirect(`${GRAFANA_URI}/login/generic_oauth?state=${state}&code=cc536d98d27750394a87ab9d057016e636a8ac31`);
60-
res.redirect(`${redirect_uri}?state=${state}&code=cc536d98d27750394a87ab9d057016e636a8ac31`);
66+
instance.codes[code] = req.user;
67+
instance.codes[code].redirect_uri = redirect_uri;
68+
instance.codes[code].client_id = client_id;
69+
res.redirect(`${redirect_uri}?state=${state}&code=${code}`);
6170
} else {
6271
instance._logger.info("[OAuth][anon] /oauth/login " + state);
6372
res.cookie("originalUrl", req.originalUrl, { maxAge: 900000, httpOnly: true });
@@ -126,18 +135,14 @@ export class OAuthProvider {
126135
res.status(err.code || 500).json(err);
127136
});
128137
}
129-
public getAccessToken(bearerToken) {
138+
public async getAccessToken(bearerToken) {
130139
this._logger.info("[OAuth] getAccessToken " + bearerToken);
131-
const tokens = this.tokens.filter((token) => {
132-
return token.accessToken === bearerToken;
133-
});
140+
const tokens = await Config.db.query<Base>({ _type: "token", "accessToken": bearerToken }, null, 10, 0, null, "oauthtokens", Crypt.rootToken());
134141
return tokens.length ? tokens[0] : false;
135142
}
136-
public getRefreshToken(bearerToken) {
143+
public async getRefreshToken(bearerToken) {
137144
this._logger.info("[OAuth] getRefreshToken " + bearerToken);
138-
const tokens = this.tokens.filter((token) => {
139-
return token.refreshToken === bearerToken;
140-
});
145+
const tokens = await Config.db.query<Base>({ _type: "token", "refreshToken": bearerToken }, null, 10, 0, null, "oauthtokens", Crypt.rootToken());
141146
return tokens.length ? tokens[0] : false;
142147
}
143148
public getClient(clientId, clientSecret) {
@@ -147,9 +152,17 @@ export class OAuthProvider {
147152
});
148153
return clients.length ? clients[0] : false;
149154
}
150-
public saveToken(token, client, user) {
155+
public getClientById(clientId) {
156+
this._logger.info("[OAuth] getClientById " + clientId);
157+
const clients = this.clients.filter((client) => {
158+
return client.clientId === clientId;
159+
});
160+
return clients.length ? clients[0] : false;
161+
}
162+
163+
public async saveToken(token, client, user) {
151164
this._logger.info("[OAuth] saveToken " + token);
152-
const result = {
165+
const result: any = {
153166
accessToken: token.accessToken,
154167
access_token: token.accessToken,
155168
accessTokenExpiresAt: token.accessTokenExpiresAt,
@@ -158,43 +171,50 @@ export class OAuthProvider {
158171
refresh_token: token.refreshToken,
159172
refreshTokenExpiresAt: token.refreshTokenExpiresAt,
160173
userId: user.id,
161-
162174
user: user,
163-
client: this.clients[0]
175+
client: client,
176+
_type: "token"
164177
};
165178
this.tokens.push(result);
179+
await Config.db.InsertOne(result, "oauthtokens", 0, false, Crypt.rootToken());
166180
return result;
167181
}
168182
saveAuthorizationCode(code, client, user) {
169183
this._logger.info("[OAuth] saveAuthorizationCode " + code);
170-
// const codeToSave: any = this.codes[code];
171-
const codeToSave: any = {
172-
'authorizationCode': code.authorizationCode,
173-
'expiresAt': code.expiresAt,
174-
'redirectUri': code.redirectUri,
175-
'scope': code.scope,
176-
'client': client.id,
177-
'user': user.username
178-
};
179-
this.codes[code] = codeToSave;
180-
code = Object.assign({}, code, {
181-
'client': client.id,
182-
'user': user.username
183-
});
184+
// // const codeToSave: any = this.codes[code];
185+
// const codeToSave: any = {
186+
// 'authorizationCode': code.authorizationCode,
187+
// 'expiresAt': code.expiresAt,
188+
// 'redirectUri': code.redirectUri,
189+
// 'scope': code.scope,
190+
// 'client': client.id,
191+
// 'user': user.username
192+
// };
193+
// this.codes[code] = codeToSave;
194+
// this.revokeAuthorizationCode(code);
195+
// code = Object.assign({}, code, {
196+
// 'client': client.id,
197+
// 'user': user.username
198+
// });
184199
return code;
185200
}
186201
getAuthorizationCode(code) {
187202
this._logger.info("[OAuth] getAuthorizationCode " + code);
188203
let user: TokenUser = this.codes[code];
204+
const client_id: string = this.codes[code].client_id;
189205
if (user == null) return null;
206+
this.revokeAuthorizationCode(code);
190207
const redirect_uri = (user as any).redirect_uri;
191208
const expiresAt = new Date();
192209
expiresAt.setMonth(expiresAt.getMonth() + 1);
193-
let role = "Viewer";
194210
user = TokenUser.From(user);
195-
if (user.HasRoleName("admins")) role = "Admin";
196-
if (user.HasRoleName("grafana editors")) role = "Editor";
197-
if (user.HasRoleName("grafana admins")) role = "Admin";
211+
let client = this.getClientById(client_id);
212+
213+
let role = client.defaultrole;
214+
const keys: string[] = Object.keys(client.rolemappings);
215+
for (let i = 0; i < keys.length; i++) {
216+
if (user.HasRoleName(keys[i])) role = client.rolemappings[keys[i]];
217+
}
198218
const result = {
199219
code: code,
200220
client: this.clients[0],
@@ -209,12 +229,11 @@ export class OAuthProvider {
209229
expiresAt: expiresAt,
210230
redirectUri: redirect_uri
211231
}
212-
// Viewer, Editor, Admin
213-
// return result;
214232
return result;
215233
}
216234
revokeAuthorizationCode(code) {
217235
this._logger.info("[OAuth] revokeAuthorizationCode " + code);
236+
delete this.codes[code];
218237
return true;
219238
// const user: TokenUser = this.codes[code];
220239
// if (user != null) delete this.codes[code];

OpenFlow/src/public/Controllers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4043,6 +4043,7 @@ export class OAuthClientCtrl extends entityCtrl<Base> {
40434043
(this.model as any).clientSecret = 'secret';
40444044
(this.model as any).grants = ['password', 'refresh_token', 'authorization_code'];
40454045
(this.model as any).redirectUris = [];
4046+
(this.model as any).defaultrole = "Viewer";
40464047
(this.model as any).rolemappings = { "admins": "admin", "grafana editors": "Editor", "grafana admins": "Admin" };
40474048
}
40484049
});

OpenFlow/src/public/OAuthClient.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ <h1 class="pagetitle" translate lib="web">oauthclient</h1>
6969

7070

7171

72+
<div class="form-group">
73+
<label for="defaultrole" class="col-sm-2 control-label" translate lib="web">defaultrole</label>
74+
<div class="col-sm-4">
75+
<input ng-model="ctrl.model.defaultrole" class="form-control input-md" ng-disabled="ctrl.loading==true" />
76+
</div>
77+
</div>
7278

7379
<div class="form-group">
7480
<label for="rolemappings" class="col-sm-2 control-label" translate lib="web">rolemappings</label>

0 commit comments

Comments
 (0)