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:
1
api/mocks/generic/sas9/public-access-denied
Normal file
1
api/mocks/generic/sas9/public-access-denied
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Public access has been denied.
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user