Skip to content

Commit 8fbe170

Browse files
committed
Add cache to dashboardauth
1 parent 80e1d83 commit 8fbe170

2 files changed

Lines changed: 93 additions & 29 deletions

File tree

OpenFlow/src/Auth.ts

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { Crypt } from "./Crypt";
2-
import { User } from "@openiap/openflow-api";
2+
import { NoderedUtil, User } from "@openiap/openflow-api";
33
import { DBHelper } from "./DBHelper";
44
import { Span } from "@opentelemetry/api";
55
import { Logger } from "./Logger";
6+
import { Config } from "./Config";
67
export class Auth {
78
public static async ValidateByPassword(username: string, password: string, parent: Span): Promise<User> {
89
const span: Span = Logger.otel.startSubSpan("Auth.ValidateByPassword", parent);
@@ -21,4 +22,65 @@ export class Auth {
2122
Logger.otel.endSpan(span);
2223
}
2324
}
24-
}
25+
26+
public static authorizationCache: HashTable<CachedUser> = {};
27+
public static getUser(key: string): User {
28+
var res: CachedUser = this.authorizationCache[key];
29+
if (res === null || res === undefined) return null;
30+
var begin: number = res.firstsignin.getTime();
31+
var end: number = new Date().getTime();
32+
var seconds = Math.round((end - begin) / 1000);
33+
if (seconds < Config.api_credential_cache_seconds) {
34+
Logger.instanse.info("Return user " + res.user.username + " from cache");
35+
return res.user;
36+
}
37+
this.RemoveUser(key);
38+
return null;
39+
}
40+
public static async RemoveUser(key: string): Promise<void> {
41+
await semaphore.down();
42+
if (!NoderedUtil.IsNullUndefinded(this.authorizationCache[key])) {
43+
Logger.instanse.info("Delete user with key " + key + " from cache");
44+
delete this.authorizationCache[key];
45+
}
46+
semaphore.up();
47+
}
48+
public static async AddUser(user: User, key: string): Promise<void> {
49+
await semaphore.down();
50+
if (NoderedUtil.IsNullUndefinded(this.authorizationCache[key])) {
51+
Logger.instanse.info("Adding user " + user.username + " to cache with key " + key);
52+
var cuser: CachedUser = new CachedUser(user, user._id);
53+
this.authorizationCache[key] = cuser;
54+
}
55+
semaphore.up();
56+
}
57+
}
58+
export class CachedUser {
59+
public firstsignin: Date;
60+
constructor(
61+
public user: User,
62+
public _id: string
63+
) {
64+
this.firstsignin = new Date();
65+
}
66+
}
67+
interface HashTable<T> {
68+
[key: string]: T;
69+
}
70+
const Semaphore = (n) => ({
71+
n,
72+
async down() {
73+
while (this.n <= 0) await this.wait();
74+
this.n--;
75+
},
76+
up() {
77+
this.n++;
78+
},
79+
async wait() {
80+
if (this.n <= 0) return await new Promise((res, req) => {
81+
setImmediate(async () => res(await this.wait()))
82+
});
83+
return;
84+
},
85+
});
86+
const semaphore = Semaphore(1);

OpenFlow/src/LoginProvider.ts

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as crypto from "crypto";
22
import * as url from "url";
3-
import * as winston from "winston";
43
import * as express from "express";
54
import * as path from "path";
65

@@ -62,7 +61,6 @@ export class samlauthstrategyoptions {
6261
public verify: any;
6362
}
6463
export class LoginProvider {
65-
public static _logger: winston.Logger;
6664
public static _providers: any = {};
6765
public static login_providers: Provider[] = [];
6866

@@ -149,8 +147,7 @@ export class LoginProvider {
149147
Logger.otel.endSpan(span);
150148
}
151149
}
152-
static async configure(logger: winston.Logger, app: express.Express, baseurl: string): Promise<void> {
153-
LoginProvider._logger = logger;
150+
static async configure(app: express.Express, baseurl: string): Promise<void> {
154151
app.use(passport.initialize());
155152
app.use(passport.session());
156153
passport.serializeUser(async function (user: any, done: any): Promise<void> {
@@ -208,12 +205,14 @@ export class LoginProvider {
208205
if (!NoderedUtil.IsNullEmpty(authorization) && authorization.indexOf(" ") > 1 &&
209206
(authorization.toLocaleLowerCase().startsWith("bearer") || authorization.toLocaleLowerCase().startsWith("jwt"))) {
210207
const token = authorization.split(" ")[1];
211-
let user: User;
208+
let user: User = Auth.getUser(token);
212209
let tuser: TokenUser;
213-
try {
214-
user = await LoginProvider.validateToken(token, span);
215-
tuser = TokenUser.From(user);
216-
} catch (error) {
210+
if (user == null) {
211+
try {
212+
user = await LoginProvider.validateToken(token, span);
213+
tuser = TokenUser.From(user);
214+
} catch (error) {
215+
}
217216
}
218217
if (user == null) {
219218
try {
@@ -225,6 +224,7 @@ export class LoginProvider {
225224
if (user != null) {
226225
const allowed = user.roles.filter(x => x.name == "dashboardusers" || x.name == "admins");
227226
if (allowed.length > 0) {
227+
await Auth.AddUser(user, token);
228228
Logger.instanse.info("dashboardauth: Authorized " + user.username + " for " + req.url);
229229
return res.send({
230230
status: "success",
@@ -246,11 +246,13 @@ export class LoginProvider {
246246
const [login, password] = Buffer.from(b64auth, "base64").toString().split(':')
247247
if (login && password) {
248248
span.setAttribute("username", login);
249-
const user = await Auth.ValidateByPassword(login, password, span);
249+
let user: User = Auth.getUser(login + ":" + password);
250+
if (user == null) user = await Auth.ValidateByPassword(login, password, span);
250251
if (user != null) {
251252
const allowed = user.roles.filter(x => x.name == "dashboardusers" || x.name == "admins");
252253
if (allowed.length > 0) {
253254
Logger.instanse.info("dashboardauth: Authorized " + user.username + " for " + req.url);
255+
Auth.AddUser(user, login + ":" + password);
254256
return res.send({
255257
status: "success",
256258
display_status: "Success",
@@ -481,7 +483,7 @@ export class LoginProvider {
481483
Logger.otel.endSpan(span);
482484
return;
483485
}
484-
LoginProvider._logger.error("validate_user_form " + Config.validate_user_form + " does not exists!");
486+
Logger.instanse.error("validate_user_form " + Config.validate_user_form + " does not exists!");
485487
Config.validate_user_form = "";
486488
res.end(JSON.stringify({}));
487489
res.end();
@@ -852,7 +854,7 @@ export class LoginProvider {
852854
const strategy: passport.Strategy = new GoogleStrategy.Strategy(options, options.verify);
853855
passport.use(key, strategy);
854856
strategy.name = key;
855-
LoginProvider._logger.info(options.callbackURL);
857+
Logger.instanse.info(options.callbackURL);
856858
app.use("/" + key,
857859
express.urlencoded({ extended: false }),
858860
passport.authenticate(key, { failureRedirect: "/" + key, failureFlash: true }),
@@ -882,7 +884,7 @@ export class LoginProvider {
882884
const strategy: passport.Strategy = new SamlStrategy(options, options.verify);
883885
passport.use(key, strategy);
884886
strategy.name = key;
885-
LoginProvider._logger.info(options.callbackUrl);
887+
Logger.instanse.info(options.callbackUrl);
886888

887889
// app.get("/" + key + "/FederationMetadata/2007-06/FederationMetadata.xml",
888890
// wsfed.metadata({
@@ -1022,33 +1024,33 @@ export class LoginProvider {
10221024
app.use("/local",
10231025
express.urlencoded({ extended: false }),
10241026
function (req: any, res: any, next: any): void {
1025-
LoginProvider._logger.debug("passport.authenticate local");
1027+
Logger.instanse.debug("passport.authenticate local");
10261028
passport.authenticate("local", function (err, user, info) {
10271029
let originalUrl: any = req.cookies.originalUrl;
1028-
LoginProvider._logger.debug("originalUrl: " + originalUrl);
1030+
Logger.instanse.debug("originalUrl: " + originalUrl);
10291031
if (err) {
1030-
LoginProvider._logger.error(err);
1032+
Logger.instanse.error(err);
10311033
}
10321034
if (!err && user) {
1033-
LoginProvider._logger.info(user);
1035+
Logger.instanse.info(user);
10341036
req.logIn(user, function (err: any) {
10351037
if (err) {
1036-
LoginProvider._logger.info("req.logIn failed");
1037-
LoginProvider._logger.error(err);
1038+
Logger.instanse.info("req.logIn failed");
1039+
Logger.instanse.error(err);
10381040
return next(err);
10391041
}
1040-
LoginProvider._logger.info("req.logIn success");
1042+
Logger.instanse.info("req.logIn success");
10411043
if (!NoderedUtil.IsNullEmpty(originalUrl)) {
10421044
try {
10431045
res.cookie("originalUrl", "", { expires: new Date(0) });
10441046
LoginProvider.redirect(res, originalUrl);
1045-
LoginProvider._logger.debug("redirect: " + originalUrl);
1047+
Logger.instanse.debug("redirect: " + originalUrl);
10461048
return;
10471049
} catch (error) {
10481050
console.error(error.message ? error.message : error);
10491051
}
10501052
} else {
1051-
LoginProvider._logger.debug("redirect: to /");
1053+
Logger.instanse.debug("redirect: to /");
10521054
res.redirect("/");
10531055
return next();
10541056
}
@@ -1062,16 +1064,16 @@ export class LoginProvider {
10621064
originalUrl = originalUrl + "&error=1"
10631065
}
10641066
try {
1065-
LoginProvider._logger.debug("remove originalUrl");
1067+
Logger.instanse.debug("remove originalUrl");
10661068
res.cookie("originalUrl", "", { expires: new Date(0) });
1067-
LoginProvider._logger.debug("redirect: " + originalUrl);
1069+
Logger.instanse.debug("redirect: " + originalUrl);
10681070
LoginProvider.redirect(res, originalUrl);
10691071
} catch (error) {
10701072
console.error(error.message ? error.message : error);
10711073
}
10721074
} else {
10731075
try {
1074-
LoginProvider._logger.debug("redirect: to /");
1076+
Logger.instanse.debug("redirect: to /");
10751077
res.redirect("/");
10761078
return next();
10771079
} catch (error) {
@@ -1090,7 +1092,7 @@ export class LoginProvider {
10901092
let username: string = profile.username;
10911093
if (NoderedUtil.IsNullEmpty(username)) username = profile.nameID;
10921094
if (!NoderedUtil.IsNullEmpty(username)) { username = username.toLowerCase(); }
1093-
LoginProvider._logger.debug("verify: " + username);
1095+
Logger.instanse.debug("verify: " + username);
10941096
let _user: User = await DBHelper.FindByUsernameOrFederationid(username, span);
10951097

10961098
if (NoderedUtil.IsNullUndefinded(_user)) {
@@ -1168,7 +1170,7 @@ export class LoginProvider {
11681170
let username: string = profile.username;
11691171
if (NoderedUtil.IsNullEmpty(username)) username = profile.nameID;
11701172
if (!NoderedUtil.IsNullEmpty(username)) { username = username.toLowerCase(); }
1171-
LoginProvider._logger.debug("verify: " + username);
1173+
Logger.instanse.debug("verify: " + username);
11721174
let _user: User = await DBHelper.FindByUsernameOrFederationid(username, span);
11731175
if (NoderedUtil.IsNullUndefinded(_user)) {
11741176
let createUser: boolean = Config.auto_create_users;

0 commit comments

Comments
 (0)