mirror of
https://github.com/sasjs/server.git
synced 2025-12-10 11:24:35 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e059bee7dc | ||
|
|
6f56aafab1 | ||
|
|
8734489cf0 | ||
|
|
7e6635f40f | ||
|
|
c0022a22f4 | ||
|
|
3fa2a7e2e3 | ||
| 8a617a73ae | |||
|
|
e7babb9f55 | ||
|
|
5ab35b02c4 | ||
|
|
ad82ee7106 | ||
|
|
d2e9456d81 | ||
|
|
e6d1989847 | ||
|
|
7a932383b4 | ||
|
|
576e18347e | ||
|
|
61815f8ae1 | ||
|
|
afff27fd21 | ||
|
|
a8ba378fd1 | ||
|
|
73c81a45dc | ||
|
|
12d424acce | ||
|
|
414fb19de3 | ||
|
|
cfddf1fb0c | ||
|
|
1f483b1afc | ||
|
|
0470239ef1 | ||
|
|
2c259fe1de |
57
CHANGELOG.md
57
CHANGELOG.md
@@ -1,3 +1,60 @@
|
||||
## [0.3.9](https://github.com/sasjs/server/compare/v0.3.8...v0.3.9) (2022-06-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* forcing utf 8 encoding. Closes [#76](https://github.com/sasjs/server/issues/76) ([8734489](https://github.com/sasjs/server/commit/8734489cf014aedaca3f325e689493e4fe0b71ca))
|
||||
|
||||
## [0.3.8](https://github.com/sasjs/server/compare/v0.3.7...v0.3.8) (2022-06-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* execution controller better error handling ([8a617a7](https://github.com/sasjs/server/commit/8a617a73ae63233332f5788c90f173d6cd5e1283))
|
||||
* execution controller error details ([3fa2a7e](https://github.com/sasjs/server/commit/3fa2a7e2e32f90050f6b09e30ce3ef725eb0b15f))
|
||||
|
||||
## [0.3.7](https://github.com/sasjs/server/compare/v0.3.6...v0.3.7) (2022-06-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **appstream:** redirect to relative + nested resource should be accessed ([5ab35b0](https://github.com/sasjs/server/commit/5ab35b02c4417132dddb5a800982f31d0d50ef66))
|
||||
|
||||
## [0.3.6](https://github.com/sasjs/server/compare/v0.3.5...v0.3.6) (2022-06-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **appstream:** should serve only new files for same app stream name with new deployment ([e6d1989](https://github.com/sasjs/server/commit/e6d1989847761fbe562d7861ffa0ee542839b125))
|
||||
|
||||
## [0.3.5](https://github.com/sasjs/server/compare/v0.3.4...v0.3.5) (2022-05-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* bumping sasjs/core library ([61815f8](https://github.com/sasjs/server/commit/61815f8ae18be132e17c199cd8e3afbcc2fa0b60))
|
||||
|
||||
## [0.3.4](https://github.com/sasjs/server/compare/v0.3.3...v0.3.4) (2022-05-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **web:** system username for DESKTOP mode ([a8ba378](https://github.com/sasjs/server/commit/a8ba378fd1ff374ba025a96fdfae5c6c36954465))
|
||||
|
||||
## [0.3.3](https://github.com/sasjs/server/compare/v0.3.2...v0.3.3) (2022-05-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* usage of autoexec API in DESKTOP mode ([12d424a](https://github.com/sasjs/server/commit/12d424acce8108a6f53aefbac01fddcdc5efb48f))
|
||||
|
||||
## [0.3.2](https://github.com/sasjs/server/compare/v0.3.1...v0.3.2) (2022-05-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **web:** ability to use get/patch User API in desktop mode. ([2c259fe](https://github.com/sasjs/server/commit/2c259fe1de95d84e6929e311aaa6b895e66b42a3))
|
||||
|
||||
## [0.3.1](https://github.com/sasjs/server/compare/v0.3.0...v0.3.1) (2022-05-26)
|
||||
|
||||
|
||||
|
||||
82
api/package-lock.json
generated
82
api/package-lock.json
generated
@@ -8,7 +8,7 @@
|
||||
"name": "api",
|
||||
"version": "0.0.2",
|
||||
"dependencies": {
|
||||
"@sasjs/core": "^4.23.1",
|
||||
"@sasjs/core": "^4.27.3",
|
||||
"@sasjs/utils": "2.42.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"connect-mongo": "^4.6.0",
|
||||
@@ -24,7 +24,8 @@
|
||||
"mongoose-sequence": "^5.3.1",
|
||||
"morgan": "^1.10.0",
|
||||
"multer": "^1.4.3",
|
||||
"swagger-ui-express": "4.3.0"
|
||||
"swagger-ui-express": "4.3.0",
|
||||
"url": "^0.10.3"
|
||||
},
|
||||
"bin": {
|
||||
"api": "build/src/server.js"
|
||||
@@ -1385,9 +1386,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/core": {
|
||||
"version": "4.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.23.1.tgz",
|
||||
"integrity": "sha512-9d6yEPJRRvPLMUkpyaiQ62SXNMMyt2l815jxWgFjnVOxKeUQv9TPyZqZ0FpmWdVe6EY8dv8GLlyaBpOLDnY6Vg=="
|
||||
"version": "4.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.27.3.tgz",
|
||||
"integrity": "sha512-8AaPPRGMwhmjw244CDSnTqHXdp/77ZBjIJMgwqw4wTrCf8Vzs2Y5hVihbvAniIGQctZHLMR6X5a3X4ccn9gRjg=="
|
||||
},
|
||||
"node_modules/@sasjs/utils": {
|
||||
"version": "2.42.1",
|
||||
@@ -2883,7 +2884,7 @@
|
||||
"node_modules/busboy": {
|
||||
"version": "0.2.14",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
|
||||
"integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
|
||||
"integrity": "sha512-InWFDomvlkEj+xWLBfU3AvnbVYqeTWmQopiW0tWWEy5yehYm2YkGEc59sUmw/4ty5Zj/b0WHGs1LgecuBSBGrg==",
|
||||
"dependencies": {
|
||||
"dicer": "0.2.5",
|
||||
"readable-stream": "1.1.x"
|
||||
@@ -3677,7 +3678,7 @@
|
||||
"node_modules/dicer": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
|
||||
"integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
|
||||
"integrity": "sha512-FDvbtnq7dzlPz0wyYlOExifDEZcu8h+rErEXgfxqmLfRfC/kJidEFh4+effJRO3P0xmfqyPbSMG0LveNRfTKVg==",
|
||||
"dependencies": {
|
||||
"readable-stream": "1.1.x",
|
||||
"streamsearch": "0.1.2"
|
||||
@@ -7590,9 +7591,10 @@
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"node_modules/multer": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.3.tgz",
|
||||
"integrity": "sha512-np0YLKncuZoTzufbkM6wEKp68EhWJXcU6fq6QqrSwkckd2LlMgd1UqhUJLj6NS/5sZ8dE8LYDWslsltJznnXlg==",
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz",
|
||||
"integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==",
|
||||
"deprecated": "Multer 1.x is affected by CVE-2022-24434. This is fixed in v1.4.4-lts.1 which drops support for versions of Node.js before 6. Please upgrade to at least Node.js 6 and version 1.4.4-lts.1 of Multer. If you need support for older versions of Node.js, we are open to accepting patches that would fix the CVE on the main 1.x release line, whilst maintaining compatibility with Node.js 0.10.",
|
||||
"dependencies": {
|
||||
"append-field": "^1.0.0",
|
||||
"busboy": "^0.2.11",
|
||||
@@ -8552,6 +8554,15 @@
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/querystring": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
|
||||
"deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
|
||||
"engines": {
|
||||
"node": ">=0.4.x"
|
||||
}
|
||||
},
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
@@ -9942,6 +9953,15 @@
|
||||
"url": "https://github.com/yeoman/update-notifier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/url": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
|
||||
"integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
|
||||
"dependencies": {
|
||||
"punycode": "1.3.2",
|
||||
"querystring": "0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url-parse-lax": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
|
||||
@@ -9954,6 +9974,11 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/url/node_modules/punycode": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
||||
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
@@ -11364,9 +11389,9 @@
|
||||
}
|
||||
},
|
||||
"@sasjs/core": {
|
||||
"version": "4.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.23.1.tgz",
|
||||
"integrity": "sha512-9d6yEPJRRvPLMUkpyaiQ62SXNMMyt2l815jxWgFjnVOxKeUQv9TPyZqZ0FpmWdVe6EY8dv8GLlyaBpOLDnY6Vg=="
|
||||
"version": "4.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.27.3.tgz",
|
||||
"integrity": "sha512-8AaPPRGMwhmjw244CDSnTqHXdp/77ZBjIJMgwqw4wTrCf8Vzs2Y5hVihbvAniIGQctZHLMR6X5a3X4ccn9gRjg=="
|
||||
},
|
||||
"@sasjs/utils": {
|
||||
"version": "2.42.1",
|
||||
@@ -12629,7 +12654,7 @@
|
||||
"busboy": {
|
||||
"version": "0.2.14",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
|
||||
"integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
|
||||
"integrity": "sha512-InWFDomvlkEj+xWLBfU3AvnbVYqeTWmQopiW0tWWEy5yehYm2YkGEc59sUmw/4ty5Zj/b0WHGs1LgecuBSBGrg==",
|
||||
"requires": {
|
||||
"dicer": "0.2.5",
|
||||
"readable-stream": "1.1.x"
|
||||
@@ -13267,7 +13292,7 @@
|
||||
"dicer": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
|
||||
"integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
|
||||
"integrity": "sha512-FDvbtnq7dzlPz0wyYlOExifDEZcu8h+rErEXgfxqmLfRfC/kJidEFh4+effJRO3P0xmfqyPbSMG0LveNRfTKVg==",
|
||||
"requires": {
|
||||
"readable-stream": "1.1.x",
|
||||
"streamsearch": "0.1.2"
|
||||
@@ -16220,9 +16245,9 @@
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"multer": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.3.tgz",
|
||||
"integrity": "sha512-np0YLKncuZoTzufbkM6wEKp68EhWJXcU6fq6QqrSwkckd2LlMgd1UqhUJLj6NS/5sZ8dE8LYDWslsltJznnXlg==",
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz",
|
||||
"integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==",
|
||||
"requires": {
|
||||
"append-field": "^1.0.0",
|
||||
"busboy": "^0.2.11",
|
||||
@@ -16933,6 +16958,11 @@
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
|
||||
},
|
||||
"querystring": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
|
||||
},
|
||||
"queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
@@ -17963,6 +17993,22 @@
|
||||
"xdg-basedir": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
|
||||
"integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
|
||||
"requires": {
|
||||
"punycode": "1.3.2",
|
||||
"querystring": "0.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"punycode": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
||||
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
|
||||
}
|
||||
}
|
||||
},
|
||||
"url-parse-lax": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
},
|
||||
"author": "4GL Ltd",
|
||||
"dependencies": {
|
||||
"@sasjs/core": "^4.23.1",
|
||||
"@sasjs/core": "^4.27.3",
|
||||
"@sasjs/utils": "2.42.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"connect-mongo": "^4.6.0",
|
||||
@@ -63,7 +63,8 @@
|
||||
"mongoose-sequence": "^5.3.1",
|
||||
"morgan": "^1.10.0",
|
||||
"multer": "^1.4.3",
|
||||
"swagger-ui-express": "4.3.0"
|
||||
"swagger-ui-express": "4.3.0",
|
||||
"url": "^0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
@@ -96,7 +97,7 @@
|
||||
},
|
||||
"nodemonConfig": {
|
||||
"ignore": [
|
||||
"tmp/**/*"
|
||||
"sasjs_root/**/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,11 @@ import { Request, Security, Route, Tags, Post, Body } from 'tsoa'
|
||||
import { ExecuteReturnJson, ExecutionController } from './internal'
|
||||
import { ExecuteReturnJsonResponse } from '.'
|
||||
import {
|
||||
getDesktopUserAutoExecPath,
|
||||
getPreProgramVariables,
|
||||
getUserAutoExec,
|
||||
ModeType,
|
||||
parseLogToArray
|
||||
} from '../utils'
|
||||
import { readFile } from '@sasjs/utils'
|
||||
|
||||
interface ExecuteSASCodePayload {
|
||||
/**
|
||||
@@ -43,7 +42,7 @@ const executeSASCode = async (
|
||||
const userAutoExec =
|
||||
process.env.MODE === ModeType.Server
|
||||
? user?.autoExec
|
||||
: await readFile(getDesktopUserAutoExecPath())
|
||||
: await getUserAutoExec()
|
||||
|
||||
try {
|
||||
const { webout, log, httpHeaders } =
|
||||
|
||||
@@ -43,7 +43,7 @@ export class ExecutionController {
|
||||
session?: Session
|
||||
) {
|
||||
if (!(await fileExists(programPath)))
|
||||
throw 'ExecutionController: SAS file does not exist.'
|
||||
throw `The Stored Program at (${vars._program}) does not exist, or you do not have permission to view it.`
|
||||
|
||||
const program = await readFile(programPath)
|
||||
|
||||
|
||||
@@ -93,6 +93,7 @@ ${autoExecContent}`
|
||||
session.path,
|
||||
'-AUTOEXEC',
|
||||
autoExecPath,
|
||||
'-ENCODING UTF-8',
|
||||
process.platform === 'win32' ? '-nosplash' : ''
|
||||
])
|
||||
.then(() => {
|
||||
|
||||
@@ -14,8 +14,10 @@ import {
|
||||
Hidden,
|
||||
Request
|
||||
} from 'tsoa'
|
||||
import { desktopUser } from '../middlewares'
|
||||
|
||||
import User, { UserPayload } from '../model/User'
|
||||
import { getUserAutoExec, updateUserAutoExec, ModeType } from '../utils'
|
||||
|
||||
export interface UserResponse {
|
||||
id: number
|
||||
@@ -86,6 +88,10 @@ export class UserController {
|
||||
@Request() req: express.Request,
|
||||
@Path() userId: number
|
||||
): Promise<UserDetailsResponse> {
|
||||
const { MODE } = process.env
|
||||
|
||||
if (MODE === ModeType.Desktop) return getDesktopAutoExec()
|
||||
|
||||
const { user } = req
|
||||
const getAutoExec = user!.isAdmin || user!.userId == userId
|
||||
return getUser(userId, getAutoExec)
|
||||
@@ -108,6 +114,11 @@ export class UserController {
|
||||
@Path() userId: number,
|
||||
@Body() body: UserPayload
|
||||
): Promise<UserDetailsResponse> {
|
||||
const { MODE } = process.env
|
||||
|
||||
if (MODE === ModeType.Desktop)
|
||||
return updateDesktopAutoExec(body.autoExec ?? '')
|
||||
|
||||
return updateUser(userId, body)
|
||||
}
|
||||
|
||||
@@ -181,6 +192,14 @@ const getUser = async (
|
||||
}
|
||||
}
|
||||
|
||||
const getDesktopAutoExec = async () => {
|
||||
return {
|
||||
...desktopUser,
|
||||
id: desktopUser.userId,
|
||||
autoExec: await getUserAutoExec()
|
||||
}
|
||||
}
|
||||
|
||||
const updateUser = async (
|
||||
id: number,
|
||||
data: Partial<UserPayload>
|
||||
@@ -216,6 +235,15 @@ const updateUser = async (
|
||||
}
|
||||
}
|
||||
|
||||
const updateDesktopAutoExec = async (autoExec: string) => {
|
||||
await updateUserAutoExec(autoExec)
|
||||
return {
|
||||
...desktopUser,
|
||||
id: desktopUser.userId,
|
||||
autoExec
|
||||
}
|
||||
}
|
||||
|
||||
const deleteUser = async (
|
||||
id: number,
|
||||
isAdmin: boolean,
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
import { RequestHandler, Request, Response, NextFunction } from 'express'
|
||||
import jwt from 'jsonwebtoken'
|
||||
import { csrfProtection } from '../app'
|
||||
import { fetchLatestAutoExec, verifyTokenInDB } from '../utils'
|
||||
import { fetchLatestAutoExec, ModeType, verifyTokenInDB } from '../utils'
|
||||
import { desktopUser } from './desktop'
|
||||
|
||||
export const authenticateAccessToken: RequestHandler = async (
|
||||
req,
|
||||
res,
|
||||
next
|
||||
) => {
|
||||
const { MODE } = process.env
|
||||
if (MODE === ModeType.Desktop) {
|
||||
req.user = desktopUser
|
||||
return next()
|
||||
}
|
||||
|
||||
// if request is coming from web and has valid session
|
||||
// we can validate the request and check for CSRF Token
|
||||
// it can be validated.
|
||||
if (req.session?.loggedIn) {
|
||||
if (req.session.user) {
|
||||
const user = await fetchLatestAutoExec(req.session.user)
|
||||
|
||||
@@ -1,20 +1,37 @@
|
||||
import { RequestHandler } from 'express'
|
||||
import { RequestHandler, Request } from 'express'
|
||||
import { userInfo } from 'os'
|
||||
import { RequestUser } from '../types'
|
||||
import { ModeType } from '../utils'
|
||||
|
||||
const regexUser = /^\/SASjsApi\/user\/[0-9]*$/ // /SASjsApi/user/1
|
||||
|
||||
const allowedInDesktopMode: { [key: string]: RegExp[] } = {
|
||||
GET: [regexUser],
|
||||
PATCH: [regexUser]
|
||||
}
|
||||
|
||||
const reqAllowedInDesktopMode = (request: Request): boolean => {
|
||||
const { method, originalUrl: url } = request
|
||||
|
||||
return !!allowedInDesktopMode[method]?.find((urlRegex) => urlRegex.test(url))
|
||||
}
|
||||
|
||||
export const desktopRestrict: RequestHandler = (req, res, next) => {
|
||||
const { MODE } = process.env
|
||||
if (MODE?.trim() !== 'server')
|
||||
return res.status(403).send('Not Allowed while in Desktop Mode.')
|
||||
|
||||
if (MODE === ModeType.Desktop) {
|
||||
if (!reqAllowedInDesktopMode(req))
|
||||
return res.status(403).send('Not Allowed while in Desktop Mode.')
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
export const desktopUsername: RequestHandler = (req, res, next) => {
|
||||
const { MODE } = process.env
|
||||
if (MODE?.trim() !== 'server')
|
||||
return res.status(200).send({
|
||||
userId: 12345,
|
||||
username: 'DESKTOPusername',
|
||||
displayName: 'DESKTOP User'
|
||||
})
|
||||
|
||||
next()
|
||||
export const desktopUser: RequestUser = {
|
||||
userId: 12345,
|
||||
clientId: 'desktop_app',
|
||||
username: userInfo().username,
|
||||
displayName: userInfo().username,
|
||||
isAdmin: true,
|
||||
isActive: true
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import swaggerUi from 'swagger-ui-express'
|
||||
import {
|
||||
authenticateAccessToken,
|
||||
desktopRestrict,
|
||||
desktopUsername,
|
||||
verifyAdmin
|
||||
} from '../../middlewares'
|
||||
|
||||
@@ -22,7 +21,7 @@ import sessionRouter from './session'
|
||||
const router = express.Router()
|
||||
|
||||
router.use('/info', infoRouter)
|
||||
router.use('/session', desktopUsername, authenticateAccessToken, sessionRouter)
|
||||
router.use('/session', authenticateAccessToken, sessionRouter)
|
||||
router.use('/auth', desktopRestrict, authRouter)
|
||||
router.use(
|
||||
'/client',
|
||||
|
||||
@@ -47,10 +47,11 @@ stpRouter.post(
|
||||
query?._program
|
||||
)
|
||||
|
||||
if (response instanceof Buffer) {
|
||||
res.writeHead(200, (req as any).sasHeaders)
|
||||
return res.end(response)
|
||||
}
|
||||
// TODO: investigate if this code is required
|
||||
// if (response instanceof Buffer) {
|
||||
// res.writeHead(200, (req as any).sasHeaders)
|
||||
// return res.end(response)
|
||||
// }
|
||||
|
||||
res.send(response)
|
||||
} catch (err: any) {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import path from 'path'
|
||||
import express from 'express'
|
||||
import express, { Request } from 'express'
|
||||
import { folderExists } from '@sasjs/utils'
|
||||
|
||||
import { addEntryToAppStreamConfig, getFilesFolder } from '../../utils'
|
||||
import { appStreamHtml } from './appStreamHtml'
|
||||
|
||||
const appStreams: { [key: string]: string } = {}
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
router.get('/', async (req, res) => {
|
||||
@@ -44,7 +46,7 @@ export const publishAppStream = async (
|
||||
streamServiceName = `AppStreamName${appCount + 1}`
|
||||
}
|
||||
|
||||
router.use(`/${streamServiceName}`, express.static(pathToDeployment))
|
||||
appStreams[streamServiceName] = pathToDeployment
|
||||
|
||||
addEntryToAppStreamConfig(
|
||||
streamServiceName,
|
||||
@@ -64,4 +66,26 @@ export const publishAppStream = async (
|
||||
return {}
|
||||
}
|
||||
|
||||
router.get(`/*`, function (req: Request, res, next) {
|
||||
const reqPath = req.path.replace(/^\//, '')
|
||||
|
||||
// Redirecting to url with trailing slash for appStream base URL only
|
||||
if (reqPath.split('/').length === 1 && !reqPath.endsWith('/'))
|
||||
// navigating to same url with slash at start
|
||||
return res.redirect(301, `${reqPath}/`)
|
||||
|
||||
const appStream = reqPath.split('/')[0]
|
||||
const appStreamFilesPath = appStreams[appStream]
|
||||
if (appStreamFilesPath) {
|
||||
// resourcePath is without appStream base path
|
||||
const resourcePath = reqPath.split('/').slice(1).join('/') || 'index.html'
|
||||
|
||||
req.url = resourcePath
|
||||
|
||||
return express.static(appStreamFilesPath)(req, res, next)
|
||||
}
|
||||
|
||||
return res.send("There's no App Stream available here.")
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import express from 'express'
|
||||
import { WebController } from '../../controllers/web'
|
||||
import { authenticateAccessToken } from '../../middlewares'
|
||||
import { authenticateAccessToken, desktopRestrict } from '../../middlewares'
|
||||
import { authorizeValidation, loginWebValidation } from '../../utils'
|
||||
|
||||
const webRouter = express.Router()
|
||||
@@ -19,7 +19,7 @@ webRouter.get('/', async (req, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
webRouter.post('/SASLogon/login', async (req, res) => {
|
||||
webRouter.post('/SASLogon/login', desktopRestrict, async (req, res) => {
|
||||
const { error, value: body } = loginWebValidation(req.body)
|
||||
if (error) return res.status(400).send(error.details[0].message)
|
||||
|
||||
@@ -33,6 +33,7 @@ webRouter.post('/SASLogon/login', async (req, res) => {
|
||||
|
||||
webRouter.post(
|
||||
'/SASLogon/authorize',
|
||||
desktopRestrict,
|
||||
authenticateAccessToken,
|
||||
async (req, res) => {
|
||||
const { error, value: body } = authorizeValidation(req.body)
|
||||
@@ -47,7 +48,7 @@ webRouter.post(
|
||||
}
|
||||
)
|
||||
|
||||
webRouter.get('/logout', async (req, res) => {
|
||||
webRouter.get('/logout', desktopRestrict, async (req, res) => {
|
||||
try {
|
||||
await controller.logout(req)
|
||||
res.status(200).send('OK!')
|
||||
|
||||
8
api/src/utils/desktopAutoExec.ts
Normal file
8
api/src/utils/desktopAutoExec.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { createFile, readFile } from '@sasjs/utils'
|
||||
import { getDesktopUserAutoExecPath } from './file'
|
||||
|
||||
export const getUserAutoExec = async (): Promise<string> =>
|
||||
readFile(getDesktopUserAutoExecPath())
|
||||
|
||||
export const updateUserAutoExec = async (autoExecContent: string) =>
|
||||
createFile(getDesktopUserAutoExecPath(), autoExecContent)
|
||||
15
api/src/utils/getServerUrl.ts
Normal file
15
api/src/utils/getServerUrl.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import express from 'express'
|
||||
import url from 'url'
|
||||
|
||||
export const getFullUrl = (req: express.Request) =>
|
||||
url.format({
|
||||
protocol: req.protocol,
|
||||
host: req.get('host'),
|
||||
pathname: req.originalUrl
|
||||
})
|
||||
|
||||
export const getServerUrl = (req: express.Request) =>
|
||||
url.format({
|
||||
protocol: req.protocol,
|
||||
host: req.get('x-forwarded-host') || req.get('host')
|
||||
})
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './appStreamConfig'
|
||||
export * from './connectDB'
|
||||
export * from './copySASjsCore'
|
||||
export * from './desktopAutoExec'
|
||||
export * from './extractHeaders'
|
||||
export * from './file'
|
||||
export * from './generateAccessToken'
|
||||
@@ -9,6 +10,7 @@ export * from './generateRefreshToken'
|
||||
export * from './getCertificates'
|
||||
export * from './getDesktopFields'
|
||||
export * from './getPreProgramVariables'
|
||||
export * from './getServerUrl'
|
||||
export * from './instantiateLogger'
|
||||
export * from './isDebugOn'
|
||||
export * from './parseLogToArray'
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
} from '@mui/material'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
import { AppContext } from '../../context/appContext'
|
||||
import { AppContext, ModeType } from '../../context/appContext'
|
||||
|
||||
const Profile = () => {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
@@ -89,6 +89,7 @@ const Profile = () => {
|
||||
required
|
||||
value={user.displayName}
|
||||
variant="outlined"
|
||||
disabled={appContext.mode === ModeType.Desktop}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
@@ -103,6 +104,7 @@ const Profile = () => {
|
||||
required
|
||||
value={user.username}
|
||||
variant="outlined"
|
||||
disabled={appContext.mode === ModeType.Desktop}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -9,6 +9,11 @@ import React, {
|
||||
} from 'react'
|
||||
import axios from 'axios'
|
||||
|
||||
export enum ModeType {
|
||||
Server = 'server',
|
||||
Desktop = 'desktop'
|
||||
}
|
||||
|
||||
interface AppContextProps {
|
||||
checkingSession: boolean
|
||||
loggedIn: boolean
|
||||
@@ -19,6 +24,7 @@ interface AppContextProps {
|
||||
setUsername: Dispatch<SetStateAction<string>> | null
|
||||
displayName: string
|
||||
setDisplayName: Dispatch<SetStateAction<string>> | null
|
||||
mode: ModeType
|
||||
logout: (() => void) | null
|
||||
}
|
||||
|
||||
@@ -32,6 +38,7 @@ export const AppContext = createContext<AppContextProps>({
|
||||
setUsername: null,
|
||||
displayName: '',
|
||||
setDisplayName: null,
|
||||
mode: ModeType.Server,
|
||||
logout: null
|
||||
})
|
||||
|
||||
@@ -42,6 +49,7 @@ const AppContextProvider = (props: { children: ReactNode }) => {
|
||||
const [userId, setUserId] = useState(0)
|
||||
const [username, setUsername] = useState('')
|
||||
const [displayName, setDisplayName] = useState('')
|
||||
const [mode, setMode] = useState(ModeType.Server)
|
||||
|
||||
useEffect(() => {
|
||||
setCheckingSession(true)
|
||||
@@ -60,6 +68,14 @@ const AppContextProvider = (props: { children: ReactNode }) => {
|
||||
setLoggedIn(false)
|
||||
axios.get('/') // get CSRF TOKEN
|
||||
})
|
||||
|
||||
axios
|
||||
.get('/SASjsApi/info')
|
||||
.then((res) => res.data)
|
||||
.then((data: any) => {
|
||||
setMode(data.mode)
|
||||
})
|
||||
.catch(() => {})
|
||||
}, [])
|
||||
|
||||
const logout = useCallback(() => {
|
||||
@@ -82,6 +98,7 @@ const AppContextProvider = (props: { children: ReactNode }) => {
|
||||
setUsername,
|
||||
displayName,
|
||||
setDisplayName,
|
||||
mode,
|
||||
logout
|
||||
}}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user