1
0
mirror of https://github.com/sasjs/server.git synced 2025-12-11 19:44:35 +00:00

feat: sas9 mocker improved - public access denied scenario

This commit is contained in:
2022-09-07 18:48:56 +02:00
parent 70655e74d3
commit 06d3b17154
5 changed files with 106 additions and 58 deletions

View File

@@ -0,0 +1 @@
Public access has been denied.

View File

@@ -62,48 +62,6 @@ components:
- clientSecret - clientSecret
type: object type: object
additionalProperties: false additionalProperties: false
IRecordOfAny:
properties: {}
type: object
additionalProperties: {}
LogLine:
properties:
line:
type: string
required:
- line
type: object
additionalProperties: false
HTTPHeaders:
properties: {}
type: object
additionalProperties:
type: string
ExecuteReturnJsonResponse:
properties:
status:
type: string
_webout:
anyOf:
-
type: string
-
$ref: '#/components/schemas/IRecordOfAny'
log:
items:
$ref: '#/components/schemas/LogLine'
type: array
message:
type: string
httpHeaders:
$ref: '#/components/schemas/HTTPHeaders'
required:
- status
- _webout
- log
- httpHeaders
type: object
additionalProperties: false
RunTimeType: RunTimeType:
enum: enum:
- sas - sas
@@ -550,7 +508,7 @@ components:
- setting - setting
type: object type: object
additionalProperties: false additionalProperties: false
ExecuteReturnJsonPayload: ExecutePostRequestPayload:
properties: properties:
_program: _program:
type: string type: string
@@ -698,7 +656,9 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/ExecuteReturnJsonResponse' anyOf:
- {type: string}
- {type: string, format: byte}
description: 'Execute SAS code.' description: 'Execute SAS code.'
summary: 'Run SAS Code and returns log' summary: 'Run SAS Code and returns log'
tags: tags:
@@ -1687,7 +1647,7 @@ paths:
parameters: [] parameters: []
/SASjsApi/stp/execute: /SASjsApi/stp/execute:
get: get:
operationId: ExecuteReturnRaw operationId: ExecuteGetRequest
responses: responses:
'200': '200':
description: Ok description: Ok
@@ -1714,17 +1674,16 @@ paths:
type: string type: string
example: /Projects/myApp/some/program example: /Projects/myApp/some/program
post: post:
operationId: ExecuteReturnJson operationId: ExecutePostRequest
responses: responses:
'200': '200':
description: Ok description: Ok
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/ExecuteReturnJsonResponse' anyOf:
examples: - {type: string}
'Example 1': - {type: string, format: byte}
value: {status: success, _webout: 'webout content', log: [], httpHeaders: {Content-type: application/zip, Cache-Control: 'public, max-age=1000'}}
description: "Trigger a SAS or JS program using the _program URL parameter.\n\nAccepts URL parameters and file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms\n\nThe response will be a JSON object with the following root attributes:\nlog, webout, headers.\n\nThe webout attribute will be nested JSON ONLY if the response-header\ncontains a content-type of application/json AND it is valid JSON.\nOtherwise it will be a stringified version of the webout content." description: "Trigger a SAS or JS program using the _program URL parameter.\n\nAccepts URL parameters and file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms\n\nThe response will be a JSON object with the following root attributes:\nlog, webout, headers.\n\nThe webout attribute will be nested JSON ONLY if the response-header\ncontains a content-type of application/json AND it is valid JSON.\nOtherwise it will be a stringified version of the webout content."
summary: 'Execute a Stored Program, return a JSON object' summary: 'Execute a Stored Program, return a JSON object'
tags: tags:
@@ -1746,7 +1705,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/ExecuteReturnJsonPayload' $ref: '#/components/schemas/ExecutePostRequestPayload'
/: /:
get: get:
operationId: Home operationId: Home

View File

@@ -3,6 +3,7 @@ import express, { ErrorRequestHandler } from 'express'
import csrf, { CookieOptions } from 'csurf' import csrf, { CookieOptions } from 'csurf'
import cookieParser from 'cookie-parser' import cookieParser from 'cookie-parser'
import dotenv from 'dotenv' import dotenv from 'dotenv'
import bodyParser from 'body-parser'
import { import {
copySASjsCore, copySASjsCore,
@@ -77,6 +78,15 @@ export default setProcessVariables().then(async () => {
app.use(express.json({ limit: '100mb' })) app.use(express.json({ limit: '100mb' }))
app.use(express.static(path.join(__dirname, '../public'))) app.use(express.static(path.join(__dirname, '../public')))
// Body parser is used for decoding the formdata on POST request.
// Currently only place we use it is SAS9 Mock - POST /SASLogon/login
app.use(
bodyParser.urlencoded({
extended: true
})
)
app.use(bodyParser.json())
await setupFolders() await setupFolders()
await copySASjsCore() await copySASjsCore()

View File

@@ -15,7 +15,7 @@ export interface MockFileRead {
} }
export class MockSas9Controller { export class MockSas9Controller {
private loggedIn: boolean = false private loggedIn: string | undefined
@Get('/SASStoredProcess') @Get('/SASStoredProcess')
public async sasStoredProcess(): Promise<Sas9Response> { public async sasStoredProcess(): Promise<Sas9Response> {
@@ -46,6 +46,13 @@ export class MockSas9Controller {
} }
} }
if (this.isPublicAccount()) {
return {
content: '',
redirect: '/SASLogon/Login'
}
}
let program = req.query._program?.toString() || '' let program = req.query._program?.toString() || ''
program = program.replace('/', '') program = program.replace('/', '')
@@ -68,6 +75,23 @@ export class MockSas9Controller {
@Get('/SASLogon/login') @Get('/SASLogon/login')
public async loginGet(): Promise<Sas9Response> { public async loginGet(): Promise<Sas9Response> {
if (this.loggedIn) {
if (this.isPublicAccount()) {
return {
content: '',
redirect: '/SASStoredProcess/Logoff?publicDenied=true'
}
} else {
return await getMockResponseFromFile([
process.cwd(),
'mocks',
'generic',
'sas9',
'logged-in'
])
}
}
return await getMockResponseFromFile([ return await getMockResponseFromFile([
process.cwd(), process.cwd(),
'mocks', 'mocks',
@@ -78,8 +102,8 @@ export class MockSas9Controller {
} }
@Post('/SASLogon/login') @Post('/SASLogon/login')
public async loginPost(): Promise<Sas9Response> { public async loginPost(req: express.Request): Promise<Sas9Response> {
this.loggedIn = true this.loggedIn = req.body.username
return await getMockResponseFromFile([ return await getMockResponseFromFile([
process.cwd(), process.cwd(),
@@ -91,8 +115,18 @@ export class MockSas9Controller {
} }
@Get('/SASLogon/logout') @Get('/SASLogon/logout')
public async logout(): Promise<Sas9Response> { public async logout(req: express.Request): Promise<Sas9Response> {
this.loggedIn = false this.loggedIn = undefined
if (req.query.publicDenied === 'true') {
return await getMockResponseFromFile([
process.cwd(),
'mocks',
'generic',
'sas9',
'public-access-denied'
])
}
return await getMockResponseFromFile([ return await getMockResponseFromFile([
process.cwd(), process.cwd(),
@@ -102,6 +136,20 @@ export class MockSas9Controller {
'logged-out' 'logged-out'
]) ])
} }
@Get('/SASStoredProcess/Logoff') //publicDenied=true
public async logoff(req: express.Request): Promise<Sas9Response> {
const params = req.query.publicDenied
? `?publicDenied=${req.query.publicDenied}`
: ''
return {
content: '',
redirect: '/SASLogon/logout' + params
}
}
private isPublicAccount = () => this.loggedIn?.toLowerCase() === 'public'
} }
/** /**

View File

@@ -58,6 +58,11 @@ sas9WebRouter.post('/SASStoredProcess/do/', async (req, res) => {
sas9WebRouter.get('/SASLogon/login', async (req, res) => { sas9WebRouter.get('/SASLogon/login', async (req, res) => {
const response = await controller.loginGet() const response = await controller.loginGet()
if (response.redirect) {
res.redirect(response.redirect)
return
}
try { try {
res.send(response.content) res.send(response.content)
} catch (err: any) { } catch (err: any) {
@@ -66,7 +71,12 @@ sas9WebRouter.get('/SASLogon/login', async (req, res) => {
}) })
sas9WebRouter.post('/SASLogon/login', async (req, res) => { sas9WebRouter.post('/SASLogon/login', async (req, res) => {
const response = await controller.loginPost() const response = await controller.loginPost(req)
if (response.redirect) {
res.redirect(response.redirect)
return
}
try { try {
res.send(response.content) res.send(response.content)
@@ -76,7 +86,27 @@ sas9WebRouter.post('/SASLogon/login', async (req, res) => {
}) })
sas9WebRouter.get('/SASLogon/logout', async (req, res) => { sas9WebRouter.get('/SASLogon/logout', async (req, res) => {
const response = await controller.logout() const response = await controller.logout(req)
if (response.redirect) {
res.redirect(response.redirect)
return
}
try {
res.send(response.content)
} catch (err: any) {
res.status(403).send(err.toString())
}
})
sas9WebRouter.get('/SASStoredProcess/Logoff', async (req, res) => {
const response = await controller.logoff(req)
if (response.redirect) {
res.redirect(response.redirect)
return
}
try { try {
res.send(response.content) res.send(response.content)