@cloudron/tegel 1.2.0 → 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 +57 -27
  2. package/package.json +2 -2
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
- 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)
@@ -130,6 +149,12 @@ export function logout(redirectTo) {
130
149
 
131
150
  req.session.destroy((error) => {
132
151
  if (error) console.error('Logout error:', error);
152
+ res.clearCookie('connect.sid', {
153
+ path: '/',
154
+ httpOnly: true,
155
+ secure: process.env.NODE_ENV === 'production',
156
+ sameSite: 'lax'
157
+ });
133
158
  res.redirect(redirectUri + redirectTo);
134
159
  });
135
160
  };
@@ -139,6 +164,11 @@ export function requireAuth(redirectTo = '', { requiredScopes = [] } = {}) {
139
164
  if (typeof redirectTo !== 'string') throw new Error('requireAuth needs a redirectTo path as non-empty string');
140
165
 
141
166
  return async (req, res, next) => {
167
+ if (_testMode) {
168
+ req.user = { ..._testMode };
169
+ return next();
170
+ }
171
+
142
172
  const accessToken = extractAccessToken(req);
143
173
 
144
174
  if (accessToken) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudron/tegel",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "",
5
5
  "license": "GPL-2.0",
6
6
  "author": "Cloudron Developers",
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "devDependencies": {
22
22
  "@eslint/js": "^10.0.1",
23
- "eslint": "^10.0.2",
23
+ "eslint": "^10.0.3",
24
24
  "globals": "^17.4.0"
25
25
  }
26
26
  }