-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
133 lines (118 loc) · 5.25 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
const express = require('express');
const expressSession = require('express-session');
const fs = require('fs');
const jsonwebtoken = require('jsonwebtoken');
const path = require('path');
const pbkdf2PasswordHasher = require('pbkdf2-password')();
// simulated database of users - replace this with a real DB and don't use weak passwords like 'admin'!
let users = {
'admin': {
username: 'admin',
// salt and hash are for the password 'admin' and were generated by running:
// pbkdf2PasswordHasher({ password: 'admin' }, (_err, _pass, salt, hash) => console.log(`salt: '${salt}', hash: '${hash}'`))
salt: 'yGkvJkw8kgR4paeTgcAYJroT/MLTO4gxoVCjlDBh+8GFMQ0Q6Wu74uC0m8wYaCAQ1guOnn1yMryIlxrffZUA6Q==',
hash: 'iZFXmotRAxwdfOm9T+a2iCKS4OzhYhiQWxEzykeWipJht6wqp6bS7qbh/6roFsV+luMmiYfAxbU789VMrHnVmyPPw4WC7kKxXzD5r/j3kEjJmtROG+GOMEJ8c/XJRkaIIW0dnGT69rIYZL8RQlNp7h6Y2K8H6hUGlsTZc7VntR4='
}
};
// utility for checking if a username and password is in the simulated database
const authenticate = (username, password, callback) => {
const user = users[username]; // lookup user from the simulated database
if (!user) return callback(null, null); // no match to username
pbkdf2PasswordHasher({ password, salt: user.salt }, (err, _pass, _salt, hash) => {
if (err) return callback(err, null); // error
if (hash === user.hash) return callback(null, user); // success
callback(null, null); // no match to password
});
}
// setup message of day
const messagePath = path.join(__dirname, 'message.html'); // path to message of the day
if (!fs.existsSync(messagePath)) { // when the message doesn't exist create a default one
fs.writeFileSync(messagePath, '<p>Have a lovely day!</p>', 'utf-8');
}
// make express app
const app = express();
// middleware to allow reading posted data
app.use(express.urlencoded({ extended: false }));
// middleware to allow creating sessions using cookies
app.use(expressSession({
resave: false,
saveUninitialized: false,
secret: 'REPLACE THIS SECRET WITH YOUR OWN!', // secret to encrypt session cookies
}));
// endpoint to serve the single page app
app.get('/', (_req, res) => res.sendFile(path.join(__dirname, 'index.html')));
app.get('/ai-request.js', (_req, res) => res.sendFile(path.join(__dirname, 'ai-request.js')));
// endpoint to check if the request contains an authenticated session
app.get('/authenticated', (req, res) => res.status(req.session.user ? 200 : 403).send());
// endpoint to produce a JWT for authenticating with the proxy [Ref-1.1]
app.get('/jsonwebtoken', (req, res) => {
if (req.session.user) {
const secret = process.env.EXAMPLE_APP_JWT_SECRET ?? "Default JWT secret"; // authentication with the proxy uses a shared secret
jsonwebtoken.sign({
sub: req.session.user.username, // sub[ject]
aud: ['openai-proxy-reference'], // aud[ience] must be an array for OPA to accept
allow_openai_chat_completions: true, // a claim for the proxy to verify
}, secret, { expiresIn: '2m', algorithm: 'HS256' }, (err, encoded) => {
if (err) { // encoding JWT failed, for example if the secret is empty
res.status(500).send();
} else { // reply with JWT
res.contentType('application/jwt').send(encoded);
}
});
} else { // not currently authenticated
res.status(403).send();
}
})
// endpoint to get the current message of the day from the message.html file
app.get('/message', (_req, res) => {
fs.readFile(messagePath, 'utf-8', (err, message) => {
if (err) {
console.error(err);
res.status(500).contentType('text/plain').send('Unable to read message.html file');
} else {
res.contentType('text/html').send(message);
}
});
});
// endpoint to allow authenticated sessions to update the current message of the day by writing a new mesage.html file
app.post('/message', (req, res) => {
if (req.session.user) { // check for an authenticated session
fs.writeFile(messagePath, req.body.message, (err) => {
if (err) { // server error
console.error(err);
res.status(500).contentType('text/plain').send('Unable to write message.html file');
} else { // file successfully updated
res.send();
}
});
} else { // not currently authenticated
res.status(403).send();
}
});
// endpoint to login to the server to allow actions requiring authentication
app.post('/login', (req, res) => {
authenticate(req.body.user, req.body.password, (err, user) => { // check the username and password
if (err) { // server error
console.error(err);
res.status(500).contentType('text/plain').send('Server error');
} else if (user) { // found matching user
req.session.regenerate(() => { // create or regenerate the session cookie
req.session.user = user; // associate the user with the session
res.status(200).send();
});
} else { // unknown username or incorrect password
res.status(403).send();
}
});
});
// endpoint to logout of the server to prevent any actions requiring authentication
app.post('/logout', (req, res) => {
req.session.destroy(() => { // remove the session cookie
res.send();
});
});
// start the server
const server = app.listen('3000');
// smooth server shutdown
process.on('SIGINT', () => server.close());
process.on('SIGTERM', () => server.close());