Skip to content

Commit 4464211

Browse files
committed
use jwt and not eas key for NodeRed credentials
1 parent 5b56a8f commit 4464211

13 files changed

Lines changed: 105 additions & 105 deletions

File tree

OpenFlow/src/Config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export class Config {
1313
public static auto_create_domains: string[] = Config.parseArray(Config.getEnv("auto_create_domains", ""));
1414
public static allow_user_registration: boolean = Config.parseBoolean(Config.getEnv("allow_user_registration", "false"));
1515
public static allow_personal_nodered: boolean = Config.parseBoolean(Config.getEnv("allow_personal_nodered", "false"));
16+
public static force_queue_prefix: boolean = Config.parseBoolean(Config.getEnv("force_queue_prefix", "true"));
1617
public static saml_federation_metadata: string = Config.getEnv("saml_federation_metadata", "");
1718
public static api_ws_url: string = Config.getEnv("api_ws_url", "ws://localhost:3000");
1819

OpenFlow/src/Crypt.ts

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,39 @@
11
import * as crypto from "crypto";
2-
import * as bcrypt from "bcryptjs";
2+
import * as bcrypt from "bcryptjs";
33
import * as jsonwebtoken from "jsonwebtoken";
44
import { Base } from "./base";
55
import { TokenUser } from "./TokenUser";
66
import { User } from "./User";
77
import { Config } from "./Config";
88

99
export class Crypt {
10-
static encryption_key:string = Config.aes_secret.substr(0,32); // must be 256 bytes (32 characters)
11-
static iv_length:number = 16; // for AES, this is always 16
12-
static bcrypt_salt_rounds:number = 12;
10+
static encryption_key: string = Config.aes_secret.substr(0, 32); // must be 256 bytes (32 characters)
11+
static iv_length: number = 16; // for AES, this is always 16
12+
static bcrypt_salt_rounds: number = 12;
1313

14-
static encrypt(text:string):string {
15-
let iv:Buffer = crypto.randomBytes(Crypt.iv_length);
16-
let cipher:crypto.Cipher = crypto.createCipheriv("aes-256-cbc", Buffer.from(Crypt.encryption_key), iv);
17-
let encrypted:Buffer = cipher.update((text as any));
14+
static encrypt(text: string): string {
15+
let iv: Buffer = crypto.randomBytes(Crypt.iv_length);
16+
let cipher: crypto.Cipher = crypto.createCipheriv("aes-256-cbc", Buffer.from(Crypt.encryption_key), iv);
17+
let encrypted: Buffer = cipher.update((text as any));
1818
encrypted = Buffer.concat([encrypted, cipher.final()]);
1919
return iv.toString("hex") + ":" + encrypted.toString("hex");
2020
}
2121

22-
static decrypt(text:string):string {
23-
let textParts:string[] = text.split(":");
24-
let iv:Buffer = Buffer.from(textParts.shift(), "hex");
25-
let encryptedText:Buffer = Buffer.from(textParts.join(":"), "hex");
26-
let decipher:crypto.Decipher = crypto.createDecipheriv("aes-256-cbc", Buffer.from(Crypt.encryption_key), iv);
27-
let decrypted:Buffer = decipher.update(encryptedText);
22+
static decrypt(text: string): string {
23+
let textParts: string[] = text.split(":");
24+
let iv: Buffer = Buffer.from(textParts.shift(), "hex");
25+
let encryptedText: Buffer = Buffer.from(textParts.join(":"), "hex");
26+
let decipher: crypto.Decipher = crypto.createDecipheriv("aes-256-cbc", Buffer.from(Crypt.encryption_key), iv);
27+
let decrypted: Buffer = decipher.update(encryptedText);
2828
decrypted = Buffer.concat([decrypted, decipher.final()]);
2929
return decrypted.toString();
3030
}
3131

32-
static async hash(password: string):Promise<string> {
32+
static async hash(password: string): Promise<string> {
3333
return new Promise<string>(async (resolve, reject) => {
3434
try {
35-
bcrypt.hash(password, Crypt.bcrypt_salt_rounds, async (error, hash)=> {
36-
if(error) { return reject(error); }
35+
bcrypt.hash(password, Crypt.bcrypt_salt_rounds, async (error, hash) => {
36+
if (error) { return reject(error); }
3737
resolve(hash);
3838
});
3939
} catch (error) {
@@ -42,11 +42,11 @@ export class Crypt {
4242
});
4343
}
4444

45-
static async compare(password: string, passwordhash:string):Promise<boolean> {
45+
static async compare(password: string, passwordhash: string): Promise<boolean> {
4646
return new Promise<boolean>(async (resolve, reject) => {
4747
try {
48-
bcrypt.compare(password, passwordhash, async (error, res)=> {
49-
if(error) { return reject(error); }
48+
bcrypt.compare(password, passwordhash, async (error, res) => {
49+
if (error) { return reject(error); }
5050
resolve(res);
5151
});
5252
} catch (error) {
@@ -55,20 +55,20 @@ export class Crypt {
5555
});
5656
}
5757

58-
static createToken(item: User | TokenUser): string {
59-
var user:TokenUser = new TokenUser(item);
60-
var token:string = jsonwebtoken.sign({ data: user }, Crypt.encryption_key,
58+
static createToken(item: User | TokenUser, expiresIn: string): string {
59+
var user: TokenUser = new TokenUser(item);
60+
var token: string = jsonwebtoken.sign({ data: user }, Crypt.encryption_key,
6161
{ expiresIn: "1h" }); // 60 (seconds), "2 days", "10h", "7d"
6262
return token;
6363
}
6464

6565
static verityToken(token: string): TokenUser {
66-
var o:any = jsonwebtoken.verify(token, Crypt.encryption_key);
66+
var o: any = jsonwebtoken.verify(token, Crypt.encryption_key);
6767
o.data = TokenUser.assign(o.data);
6868
return o.data;
6969
}
7070
static decryptToken(token: string): any {
71-
var o:any = jsonwebtoken.verify(token, Crypt.encryption_key);
71+
var o: any = jsonwebtoken.verify(token, Crypt.encryption_key);
7272
return o;
7373
}
7474
}

OpenFlow/src/LoginProvider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export class LoginProvider {
114114
res.setHeader("Content-Type", "application/json");
115115
if (req.user) {
116116
var user: TokenUser = new TokenUser(req.user);
117-
res.end(JSON.stringify({ jwt: Crypt.createToken(user) }));
117+
res.end(JSON.stringify({ jwt: Crypt.createToken(user, "1h") }));
118118
} else {
119119
res.end(JSON.stringify({ jwt: "" }));
120120
}
@@ -126,7 +126,7 @@ export class LoginProvider {
126126
var user: User = await LoginProvider.validateToken(rawAssertion);
127127
var tuser: TokenUser = new TokenUser(user);
128128
res.setHeader("Content-Type", "application/json");
129-
res.end(JSON.stringify({ jwt: Crypt.createToken(tuser) }));
129+
res.end(JSON.stringify({ jwt: Crypt.createToken(tuser, "1h") }));
130130
} catch (error) {
131131
res.end(error);
132132
console.error(error);

OpenFlow/src/Messages/Message.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ export class Message {
452452
user.device = msg.device;
453453
}
454454
Audit.LoginSuccess(tuser, type, "websocket", cli.remoteip);
455-
msg.jwt = Crypt.createToken(user);
455+
msg.jwt = Crypt.createToken(user, "1h");
456456
msg.user = tuser;
457457
if (msg.validate_only !== true) {
458458
cli._logger.debug(tuser.username + " signed in using " + type);
@@ -518,6 +518,13 @@ export class Message {
518518
name = name.toLowerCase();
519519
var namespace = Config.namespace;
520520
var hostname = Config.nodered_domain_schema.replace("$nodered_id$", name);
521+
var queue_prefix: string = "";
522+
if (Config.force_queue_prefix) {
523+
queue_prefix = cli.user.username;
524+
}
525+
526+
var tuser: TokenUser = new TokenUser(cli.user);
527+
var nodered_jwt: string = Crypt.createToken(tuser, "365d");
521528

522529
// var noderedusers = await User.ensureRole(cli.jwt, name + "noderedusers", null);
523530
// noderedusers.addRight(cli.user._id, cli.user.username, [Rights.full_control]);
@@ -560,12 +567,13 @@ export class Message {
560567
{ name: "saml_issuer", value: Config.saml_issuer },
561568
{ name: "nodered_id", value: name },
562569
{ name: "nodered_sa", value: cli.user.username },
570+
{ name: "jwt", value: nodered_jwt },
571+
{ name: "queue_prefix", value: queue_prefix },
563572
{ name: "api_ws_url", value: Config.api_ws_url },
564573
{ name: "amqp_url", value: Config.amqp_url },
565574
{ name: "nodered_domain_schema", value: hostname },
566575
{ name: "protocol", value: Config.protocol },
567576
{ name: "port", value: Config.port.toString() },
568-
{ name: "aes_secret", value: Config.aes_secret },
569577
{ name: "noderedusers", value: (name + "noderedusers") },
570578
{ name: "noderedadmins", value: (name + "noderedadmins") },
571579
]

OpenFlow/src/TokenUser.ts

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,39 @@ import { WellknownIds } from "./base";
44
import { Crypt } from "./Crypt";
55

66
export class TokenUser {
7-
_type:string;
8-
_id:string;
9-
name:string;
10-
username:string;
11-
roles:Rolemember[] = [];
12-
constructor(user:User | TokenUser) {
13-
if(user===null || user===undefined) { return; }
7+
_type: string;
8+
_id: string;
9+
name: string;
10+
username: string;
11+
roles: Rolemember[] = [];
12+
constructor(user: User | TokenUser) {
13+
if (user === null || user === undefined) { return; }
1414
this._type = user._type;
1515
this._id = user._id;
1616
this.name = user.name;
1717
this.username = user.username;
1818
this.roles = user.roles;
1919
}
20-
static assign<T>(o:T): T {
21-
var newo:TokenUser = new TokenUser(null);
20+
static assign<T>(o: T): T {
21+
var newo: TokenUser = new TokenUser(null);
2222
return Object.assign(newo, o);
2323
}
24-
static rootUser():User {
25-
var result:User = new User();
24+
static rootUser(): User {
25+
var result: User = new User();
2626
result._type = "user"; result.name = "root"; result.username = "root"; result._id = WellknownIds.root;
2727
result.roles = []; result.roles.push(new Rolemember("admins", WellknownIds.admins));
2828
return result;
2929
}
30-
static rootToken():string {
31-
return Crypt.createToken(TokenUser.rootUser());
30+
static rootToken(): string {
31+
return Crypt.createToken(TokenUser.rootUser(), "1h");
3232
}
33-
hasrolename(name:string):Boolean {
34-
var hits:Rolemember[] = this.roles.filter(member=>member.name===name);
35-
return (hits.length===1);
33+
hasrolename(name: string): Boolean {
34+
var hits: Rolemember[] = this.roles.filter(member => member.name === name);
35+
return (hits.length === 1);
3636
}
37-
hasroleid(id:string):boolean {
38-
var hits:Rolemember[] = this.roles.filter(member=>member._id===id);
39-
return (hits.length===1);
37+
hasroleid(id: string): boolean {
38+
var hits: Rolemember[] = this.roles.filter(member => member._id === id);
39+
return (hits.length === 1);
4040
}
4141

4242
}

OpenFlow/src/WebSocketServer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class WebSocketServer {
4545
if ((clockTimestamp - payload.iat) > 180) {
4646
WebSocketServer._logger.silly("Send new jwt to client");
4747
var l: SigninMessage = new SigninMessage();
48-
cli.jwt = Crypt.createToken(tuser);
48+
cli.jwt = Crypt.createToken(tuser, "1h");
4949
l.jwt = cli.jwt;
5050
l.user = tuser;
5151
var m: Message = new Message(); m.command = "refreshtoken";

OpenFlowNodeRED/src/Config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { fetch, toPassportConfig } from "passport-saml-metadata";
44
export class Config {
55
public static nodered_id: string = Config.getEnv("nodered_id", "1");
66
public static nodered_sa: string = Config.getEnv("nodered_sa", "");
7+
public static queue_prefix: string = Config.getEnv("queue_prefix", "");
78

89
public static consumer_key: string = Config.getEnv("consumer_key", "");
910
public static consumer_secret: string = Config.getEnv("consumer_secret", "");
@@ -34,6 +35,8 @@ export class Config {
3435
public static api_allow_anonymous: boolean = Config.parseBoolean(Config.getEnv("api_allow_anonymous", "false"));
3536

3637
public static aes_secret: string = Config.getEnv("aes_secret", "");
38+
public static jwt: string = Config.getEnv("jwt", "");
39+
3740

3841
public static baseurl(): string {
3942
if (Config.nodered_sa === null || Config.nodered_sa === undefined || Config.nodered_sa === "") {

OpenFlowNodeRED/src/index.ts

Lines changed: 12 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,6 @@ const logger: winston.Logger = Logger.configure();
1818

1919
Config.DumpConfig();
2020

21-
22-
var con: amqp_consumer = new amqp_consumer(logger, Config.amqp_url, "hello1");
23-
var pub: amqp_publisher = new amqp_publisher(logger, Config.amqp_url, "");
24-
var excon1: amqp_exchange_consumer = new amqp_exchange_consumer(logger, Config.amqp_url, "hello2");
25-
var excon2: amqp_exchange_consumer = new amqp_exchange_consumer(logger, Config.amqp_url, "hello2");
26-
var expub: amqp_exchange_publisher = new amqp_exchange_publisher(logger, Config.amqp_url, "hello2");
27-
28-
var rpccon: amqp_rpc_consumer = new amqp_rpc_consumer(logger, Config.amqp_url, "rpchello", (msg: string): string => {
29-
return "server response! " + msg;
30-
});
31-
var rpcpub: amqp_rpc_publisher = new amqp_rpc_publisher(logger, Config.amqp_url);
32-
3321
process.on('unhandledRejection', up => {
3422
console.error(up);
3523
throw up
@@ -46,43 +34,30 @@ function isNumeric(num) {
4634
socket.events.on("onopen", async () => {
4735

4836
var q: SigninMessage = new SigninMessage();
49-
var user = new TokenUser();
50-
console.log("nodered_id: " + Config.nodered_id + " nodered_sa: " + Config.nodered_sa);
51-
if (NoderedUtil.IsNullEmpty(Config.nodered_sa)) {
52-
user.name = "nodered" + Config.nodered_id;
37+
if (Config.jwt !== "") {
38+
q.jwt = Config.jwt;
39+
} else if (Crypt.encryption_key !== "") {
40+
var user = new TokenUser();
41+
if (NoderedUtil.IsNullEmpty(Config.nodered_sa)) {
42+
user.name = "nodered" + Config.nodered_id;
43+
} else {
44+
user.name = Config.nodered_sa;
45+
}
46+
user.username = user.name;
47+
q.jwt = Crypt.createToken(user);
5348
} else {
54-
user.name = Config.nodered_sa;
49+
throw new Error("missing encryption_key and jwt, signin not possible!");
5550
}
56-
user.username = user.name;
57-
q.jwt = Crypt.createToken(user);
5851
var msg: Message = new Message(); msg.command = "signin"; msg.data = JSON.stringify(q);
5952
var result: SigninMessage = await socket.Send<SigninMessage>(msg);
6053
logger.info("signed in as " + result.user.name + " with id " + result.user._id);
6154

6255
const server: http.Server = await WebServer.configure(logger, socket);
6356
logger.info("listening on " + Config.baseurl());
6457
});
65-
66-
// console.log("************************");
67-
// await con.connect();
68-
// await pub.connect();
69-
// pub.SendMessage("pub/sub hi mom");
70-
// console.log("************************");
71-
// await excon1.connect();
72-
// await excon2.connect();
73-
// await expub.connect();
74-
// expub.SendMessage("exchange/hi mom");
75-
// console.log("************************");
76-
// await rpccon.connect();
77-
// await rpcpub.connect();
78-
// console.log("************************");
79-
// var rpcresult:string = await rpcpub.SendMessage("Client says hi!", "rpchello");
80-
// console.log("rpcresult: " + rpcresult);
81-
// console.log("************************");
8258
} catch (error) {
8359
logger.error(error.message);
8460
console.error(error);
8561
}
86-
8762
})();
8863

OpenFlowNodeRED/src/nodered/nodes/NoderedUtil.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,17 +144,23 @@ export class NoderedUtil {
144144
if (!NoderedUtil.IsNullEmpty(username) && !NoderedUtil.IsNullEmpty(password)) {
145145
q.username = username; q.password = password;
146146
} else {
147-
if (Crypt.encryption_key === "") { throw new Error("root signin not allowed"); }
148147
var user = new TokenUser();
149148
Logger.instanse.debug("GetToken::nodered_id: " + Config.nodered_id);
150149
Logger.instanse.debug("GetToken::isNumeric: " + this.isNumeric(Config.nodered_id));
151-
if (NoderedUtil.IsNullEmpty(Config.nodered_sa)) {
152-
user.name = "nodered" + Config.nodered_id;
150+
if (Config.jwt !== "") {
151+
q.jwt = Config.jwt;
152+
} else if (Crypt.encryption_key !== "") {
153+
var user = new TokenUser();
154+
if (NoderedUtil.IsNullEmpty(Config.nodered_sa)) {
155+
user.name = "nodered" + Config.nodered_id;
156+
} else {
157+
user.name = Config.nodered_sa;
158+
}
159+
user.username = user.name;
160+
q.jwt = Crypt.createToken(user);
153161
} else {
154-
user.name = Config.nodered_sa;
162+
throw new Error("root signin not allowed");
155163
}
156-
user.username = user.name;
157-
q.jwt = Crypt.createToken(user);
158164
}
159165
var _msg: Message = new Message();
160166
_msg.command = "signin"; _msg.data = JSON.stringify(q);

OpenFlowNodeRED/src/nodered/nodes/api_nodes.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,20 @@ export class api_get_jwt {
6565
if (!NoderedUtil.IsNullEmpty(username) && !NoderedUtil.IsNullEmpty(password)) {
6666
q.username = username; q.password = password;
6767
} else {
68-
if (Crypt.encryption_key === "") { return NoderedUtil.HandleError(this, "root signin not allowed"); }
69-
var user = new TokenUser();
70-
if (NoderedUtil.IsNullEmpty(Config.nodered_sa)) {
71-
user.name = "nodered" + Config.nodered_id;
68+
if (Config.jwt !== "") {
69+
q.jwt = Config.jwt;
70+
} else if (Crypt.encryption_key !== "") {
71+
var user = new TokenUser();
72+
if (NoderedUtil.IsNullEmpty(Config.nodered_sa)) {
73+
user.name = "nodered" + Config.nodered_id;
74+
} else {
75+
user.name = Config.nodered_sa;
76+
}
77+
user.username = user.name;
78+
q.jwt = Crypt.createToken(user);
7279
} else {
73-
user.name = Config.nodered_sa;
80+
return NoderedUtil.HandleError(this, "root signin not allowed");
7481
}
75-
user.username = user.name;
76-
q.jwt = Crypt.createToken(user);
7782
}
7883
this.node.status({ fill: "blue", shape: "dot", text: "Requesting token" });
7984
var _msg: Message = new Message();

0 commit comments

Comments
 (0)