@cloudron/tegel 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +51 -27
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -6,8 +6,12 @@ import FileStoreFactory from 'session-file-store';
6
6
  import { lastMile, HttpError } from '@cloudron/connect-lastmile';
7
7
  import * as oidc from './src/oidc.js';
8
8
 
9
- export async function createExpressApp({ oidcConfig, jsonBodySizeLimit = '25mb', skipLastMile = false }) {
10
- if (oidcConfig) await oidc.initOIDC(oidcConfig);
9
+ let _testMode = null;
10
+
11
+ export async function createExpressApp({ oidcConfig, testMode, jsonBodySizeLimit = '25mb', skipLastMile = false }) {
12
+ _testMode = testMode || null;
13
+
14
+ if (!_testMode && oidcConfig) await oidc.initOIDC(oidcConfig);
11
15
 
12
16
  const app = express();
13
17
 
@@ -17,32 +21,41 @@ export async function createExpressApp({ oidcConfig, jsonBodySizeLimit = '25mb',
17
21
 
18
22
  app.use(express.json({ limit: jsonBodySizeLimit }));
19
23
 
20
- const FileStore = FileStoreFactory(session);
21
- const sessionStorePath = process.env.CLOUDRON ? '/app/data/.session.store' : '/tmp/session.store';
22
- const sessionSecretFile = process.env.CLOUDRON ? '/app/data/.session.secret' : '/tmp/.session.secret';
23
-
24
- let sessionSecret;
25
- if (!fs.existsSync(sessionSecretFile)) {
26
- sessionSecret = crypto.randomBytes(64).toString('hex');
27
- fs.writeFileSync(sessionSecretFile, sessionSecret, { mode: 0o600 });
24
+ if (_testMode) {
25
+ app.use(session({
26
+ secret: 'test-secret',
27
+ resave: false,
28
+ saveUninitialized: false,
29
+ cookie: { secure: false }
30
+ }));
28
31
  } else {
29
- sessionSecret = fs.readFileSync(sessionSecretFile, 'utf8').trim();
30
- }
31
-
32
- fs.mkdirSync(sessionStorePath, { recursive: true });
33
-
34
- app.use(session({
35
- secret: sessionSecret,
36
- store: new FileStore({ path: sessionStorePath }),
37
- resave: false,
38
- saveUninitialized: false,
39
- cookie: {
40
- secure: process.env.NODE_ENV === 'production',
41
- httpOnly: true,
42
- sameSite: 'lax',
43
- maxAge: 24 * 60 * 60 * 1000 // 24 hours
32
+ const FileStore = FileStoreFactory(session);
33
+ const sessionStorePath = process.env.CLOUDRON ? '/app/data/.session.store' : '/tmp/session.store';
34
+ const sessionSecretFile = process.env.CLOUDRON ? '/app/data/.session.secret' : '/tmp/.session.secret';
35
+
36
+ let sessionSecret;
37
+ if (!fs.existsSync(sessionSecretFile)) {
38
+ sessionSecret = crypto.randomBytes(64).toString('hex');
39
+ fs.writeFileSync(sessionSecretFile, sessionSecret, { mode: 0o600 });
40
+ } else {
41
+ sessionSecret = fs.readFileSync(sessionSecretFile, 'utf8').trim();
44
42
  }
45
- }));
43
+
44
+ fs.mkdirSync(sessionStorePath, { recursive: true });
45
+
46
+ app.use(session({
47
+ secret: sessionSecret,
48
+ store: new FileStore({ path: sessionStorePath }),
49
+ resave: false,
50
+ saveUninitialized: false,
51
+ cookie: {
52
+ secure: process.env.NODE_ENV === 'production',
53
+ httpOnly: true,
54
+ sameSite: 'lax',
55
+ maxAge: 24 * 60 * 60 * 1000 // 24 hours
56
+ }
57
+ }));
58
+ }
46
59
 
47
60
  const router = new express.Router();
48
61
  router.del = router.delete; // amend router.del for readability further on
@@ -58,6 +71,8 @@ export async function createExpressApp({ oidcConfig, jsonBodySizeLimit = '25mb',
58
71
  export const servePath = express.static;
59
72
 
60
73
  export async function oidcRedirectToLoginProvider(req, res, next) {
74
+ if (_testMode) return res.redirect('/');
75
+
61
76
  try {
62
77
  // Store the frontend origin for redirecting after login
63
78
  // This allows Vite dev server (port 5173) to work with OIDC callback (port 3000)
@@ -78,7 +93,7 @@ export async function oidcRedirectToLoginProvider(req, res, next) {
78
93
  } catch (error) {
79
94
  console.error('Login error:', error);
80
95
 
81
- next(new HttpError(500, 'Failed to initiate oidc flow.'))
96
+ next(new HttpError(500, 'Failed to initiate oidc flow.'));
82
97
  }
83
98
  }
84
99
 
@@ -88,6 +103,8 @@ export function oidcCallback(successRedirectTo, errorRedirectTo, successHook = a
88
103
  if (!successHook || typeof successHook !== 'function') throw new Error('oidcCallback needs a successHook path as a function');
89
104
 
90
105
  return async (req, res) => {
106
+ if (_testMode) return res.redirect(successRedirectTo);
107
+
91
108
  // Redirect to the frontend origin that initiated the login (supports Vite dev server)
92
109
  const frontendOrigin = req.session.frontendOrigin;
93
110
  delete req.session.frontendOrigin; // Clean up
@@ -113,6 +130,8 @@ export function logout(redirectTo) {
113
130
  if (!redirectTo || typeof redirectTo !== 'string') throw new Error('logout needs a redirectTo path as non-empty string');
114
131
 
115
132
  return (req, res) => {
133
+ if (_testMode) return res.redirect(redirectTo);
134
+
116
135
  let redirectUri = '';
117
136
 
118
137
  // Get the referer to redirect back to the same origin (supports Vite dev server)
@@ -145,6 +164,11 @@ export function requireAuth(redirectTo = '', { requiredScopes = [] } = {}) {
145
164
  if (typeof redirectTo !== 'string') throw new Error('requireAuth needs a redirectTo path as non-empty string');
146
165
 
147
166
  return async (req, res, next) => {
167
+ if (_testMode) {
168
+ req.user = { ..._testMode };
169
+ return next();
170
+ }
171
+
148
172
  const accessToken = extractAccessToken(req);
149
173
 
150
174
  if (accessToken) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudron/tegel",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "",
5
5
  "license": "GPL-2.0",
6
6
  "author": "Cloudron Developers",