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

feat: added get authorizedRoutes api endpoint

This commit is contained in:
2022-07-04 19:14:06 +05:00
parent e54a09db19
commit b10e932605
8 changed files with 109 additions and 42 deletions

View File

@@ -548,6 +548,16 @@ components:
- runTimes - runTimes
type: object type: object
additionalProperties: false additionalProperties: false
AuthorizedRoutesResponse:
properties:
routes:
items:
type: string
type: array
required:
- routes
type: object
additionalProperties: false
ExecuteReturnJsonPayload: ExecuteReturnJsonPayload:
properties: properties:
_program: _program:
@@ -1593,6 +1603,24 @@ paths:
- Info - Info
security: [] security: []
parameters: [] parameters: []
/SASjsApi/info/authorizedRoutes:
get:
operationId: AuthorizedRoutes
responses:
'200':
description: Ok
content:
application/json:
schema:
$ref: '#/components/schemas/AuthorizedRoutesResponse'
examples:
'Example 1':
value: {routes: [/AppStream, /SASjsApi/stp/execute]}
summary: 'Get authorized routes.'
tags:
- Info
security: []
parameters: []
/SASjsApi/session: /SASjsApi/session:
get: get:
operationId: Session operationId: Session

View File

@@ -1,4 +1,8 @@
import { Route, Tags, Example, Get } from 'tsoa' import { Route, Tags, Example, Get } from 'tsoa'
import { getAuthorizedRoutes } from '../utils'
export interface AuthorizedRoutesResponse {
URIs: string[]
}
export interface InfoResponse { export interface InfoResponse {
mode: string mode: string
@@ -36,4 +40,19 @@ export class InfoController {
} }
return response return response
} }
/**
* @summary Get authorized routes.
*
*/
@Example<AuthorizedRoutesResponse>({
URIs: ['/AppStream', '/SASjsApi/stp/execute']
})
@Get('/authorizedRoutes')
public authorizedRoutes(): AuthorizedRoutesResponse {
const response = {
URIs: getAuthorizedRoutes()
}
return response
}
} }

View File

@@ -13,4 +13,14 @@ infoRouter.get('/', async (req, res) => {
} }
}) })
infoRouter.get('/authorizedRoutes', async (req, res) => {
const controller = new InfoController()
try {
const response = controller.authorizedRoutes()
res.send(response)
} catch (err: any) {
res.status(403).send(err.toString())
}
})
export default infoRouter export default infoRouter

View File

@@ -0,0 +1,17 @@
export const getAuthorizedRoutes = () => {
const streamingApps = Object.keys(process.appStreamConfig)
const streamingAppsRoutes = streamingApps.map((app) => `/AppStream/${app}`)
return [...StaticAuthorizedRoutes, ...streamingAppsRoutes]
}
const StaticAuthorizedRoutes = [
'/AppStream',
'/SASjsApi/code/execute',
'/SASjsApi/stp/execute',
'/SASjsApi/drive/deploy',
'/SASjsApi/drive/upload',
'/SASjsApi/drive/file',
'/SASjsApi/drive/folder',
'/SASjsApi/drive/fileTree',
'/SASjsApi/permission'
]

View File

@@ -8,6 +8,7 @@ export * from './file'
export * from './generateAccessToken' export * from './generateAccessToken'
export * from './generateAuthCode' export * from './generateAuthCode'
export * from './generateRefreshToken' export * from './generateRefreshToken'
export * from './getAuthorizedRoutes'
export * from './getCertificates' export * from './getCertificates'
export * from './getDesktopFields' export * from './getDesktopFields'
export * from './getPreProgramVariables' export * from './getPreProgramVariables'

View File

@@ -1,5 +1,6 @@
import Joi from 'joi' import Joi from 'joi'
import { PermissionSetting, PrincipalType } from '../controllers/permission' import { PermissionSetting, PrincipalType } from '../controllers/permission'
import { getAuthorizedRoutes } from './getAuthorizedRoutes'
const usernameSchema = Joi.string().lowercase().alphanum().min(3).max(16) const usernameSchema = Joi.string().lowercase().alphanum().min(3).max(16)
const passwordSchema = Joi.string().min(6).max(1024) const passwordSchema = Joi.string().min(6).max(1024)
@@ -88,7 +89,9 @@ export const registerClientValidation = (data: any): Joi.ValidationResult =>
export const registerPermissionValidation = (data: any): Joi.ValidationResult => export const registerPermissionValidation = (data: any): Joi.ValidationResult =>
Joi.object({ Joi.object({
uri: Joi.string().required(), uri: Joi.string()
.required()
.valid(...getAuthorizedRoutes()),
setting: Joi.string() setting: Joi.string()
.required() .required()
.valid(...Object.values(PermissionSetting)), .valid(...Object.values(PermissionSetting)),

View File

@@ -1,10 +1,4 @@
import React, { import React, { useState, useEffect, Dispatch, SetStateAction } from 'react'
useState,
useEffect,
useMemo,
Dispatch,
SetStateAction
} from 'react'
import axios from 'axios' import axios from 'axios'
import { import {
Button, Button,
@@ -13,15 +7,14 @@ import {
DialogContent, DialogContent,
DialogActions, DialogActions,
TextField, TextField,
CircularProgress CircularProgress,
Autocomplete
} from '@mui/material' } from '@mui/material'
import { styled } from '@mui/material/styles' import { styled } from '@mui/material/styles'
import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete'
import { BootstrapDialogTitle } from '../../components/dialogTitle' import { BootstrapDialogTitle } from '../../components/dialogTitle'
import { import {
PermissionResponse,
UserResponse, UserResponse,
GroupResponse, GroupResponse,
RegisterPermissionPayload RegisterPermissionPayload
@@ -39,18 +32,16 @@ const BootstrapDialog = styled(Dialog)(({ theme }) => ({
type AddPermissionModalProps = { type AddPermissionModalProps = {
open: boolean open: boolean
handleOpen: Dispatch<SetStateAction<boolean>> handleOpen: Dispatch<SetStateAction<boolean>>
permissions: PermissionResponse[]
addPermission: (addPermissionPayload: RegisterPermissionPayload) => void addPermission: (addPermissionPayload: RegisterPermissionPayload) => void
} }
const filter = createFilterOptions<string>()
const AddPermissionModal = ({ const AddPermissionModal = ({
open, open,
handleOpen, handleOpen,
permissions,
addPermission addPermission
}: AddPermissionModalProps) => { }: AddPermissionModalProps) => {
const [URIs, setURIs] = useState<string[]>([])
const [loadingURIs, setLoadingURIs] = useState(false)
const [uri, setUri] = useState<string>() const [uri, setUri] = useState<string>()
const [principalType, setPrincipalType] = useState('user') const [principalType, setPrincipalType] = useState('user')
const [userPrincipal, setUserPrincipal] = useState<UserResponse>() const [userPrincipal, setUserPrincipal] = useState<UserResponse>()
@@ -60,6 +51,23 @@ const AddPermissionModal = ({
const [userPrincipals, setUserPrincipals] = useState<UserResponse[]>([]) const [userPrincipals, setUserPrincipals] = useState<UserResponse[]>([])
const [groupPrincipals, setGroupPrincipals] = useState<GroupResponse[]>([]) const [groupPrincipals, setGroupPrincipals] = useState<GroupResponse[]>([])
useEffect(() => {
setLoadingURIs(true)
axios
.get('/SASjsApi/info/authorizedRoutes')
.then((res: any) => {
if (res.data) {
setURIs(res.data.URIs)
}
})
.catch((err) => {
console.log(err)
})
.finally(() => {
setLoadingURIs(false)
})
}, [])
useEffect(() => { useEffect(() => {
setLoadingPrincipals(true) setLoadingPrincipals(true)
axios axios
@@ -97,12 +105,6 @@ const AddPermissionModal = ({
addPermission(addPermissionPayload) addPermission(addPermissionPayload)
} }
const URIs = useMemo(() => {
return permissions
.map((permission) => permission.uri)
.filter((uri, index, array) => array.indexOf(uri) === index)
}, [permissions])
const addButtonDisabled = const addButtonDisabled =
!uri || (principalType === 'user' ? !userPrincipal : !groupPrincipal) !uri || (principalType === 'user' ? !userPrincipal : !groupPrincipal)
@@ -118,29 +120,17 @@ const AddPermissionModal = ({
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item xs={12}> <Grid item xs={12}>
<Autocomplete <Autocomplete
options={URIs}
disableClearable disableClearable
value={uri} value={uri}
onChange={(event, newValue) => setUri(newValue)} onChange={(event: any, newValue: string) => setUri(newValue)}
filterOptions={(options, params) => { renderInput={(params) =>
const filtered = filter(options, params) loadingURIs ? (
<CircularProgress />
const { inputValue } = params ) : (
<TextField {...params} label="Principal" />
const isExisting = options.some(
(option) => inputValue === option
) )
if (inputValue !== '' && !isExisting) { }
filtered.push(inputValue)
}
return filtered
}}
selectOnFocus
clearOnBlur
handleHomeEndKeys
options={URIs}
renderOption={(props, option) => <li {...props}>{option}</li>}
freeSolo
renderInput={(params) => <TextField {...params} label="URI" />}
/> />
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>

View File

@@ -330,7 +330,6 @@ const Permission = () => {
<AddPermissionModal <AddPermissionModal
open={addPermissionModalOpen} open={addPermissionModalOpen}
handleOpen={setAddPermissionModalOpen} handleOpen={setAddPermissionModalOpen}
permissions={permissions}
addPermission={addPermission} addPermission={addPermission}
/> />
<UpdatePermissionModal <UpdatePermissionModal