1
Fork 0

Secure graphql endpoint

add option to scan individual users photos
This commit is contained in:
viktorstrate 2019-08-23 22:14:12 +02:00
parent 25a00e93f1
commit 2a58f0b5ab
7 changed files with 83 additions and 6 deletions

View File

@ -1,7 +1,5 @@
# PhotoView # PhotoView
> NOTE: This software should not be used in production yet, since it is in early development, and still contains security holes.
[Follow the development progress here](https://www.notion.so/e8a95059eaa74fd7b8b69f8c949c9d08?v=c999e954941b43f4bf5fcec8451a789a) [Follow the development progress here](https://www.notion.so/e8a95059eaa74fd7b8b69f8c949c9d08?v=c999e954941b43f4bf5fcec8451a789a)
![screenshot](/screenshot.png) ![screenshot](/screenshot.png)

View File

@ -7,6 +7,7 @@ import http from 'http'
import PhotoScanner from './scanner/Scanner' import PhotoScanner from './scanner/Scanner'
import _ from 'lodash' import _ from 'lodash'
import config from './config' import config from './config'
import gql from 'graphql-tag'
import { getUserFromToken, getTokenFromBearer } from './token' import { getUserFromToken, getTokenFromBearer } from './token'
@ -32,6 +33,7 @@ const scanner = new PhotoScanner(driver)
app.use((req, res, next) => { app.use((req, res, next) => {
req.driver = driver req.driver = driver
req.scanner = scanner req.scanner = scanner
next() next()
}) })
@ -41,6 +43,30 @@ setInterval(scanner.scanAll, 1000 * 60 * 60 * 4)
// Specify port and path for GraphQL endpoint // Specify port and path for GraphQL endpoint
const graphPath = '/graphql' const graphPath = '/graphql'
app.use(graphPath, (req, res, next) => {
if (req.body.query) {
const query = gql(req.body.query)
const defs = query.definitions.filter(x => x.kind == 'OperationDefinition')
const selections = defs.reduce((prev, curr) => {
return prev.concat(curr.selectionSet.selections)
}, [])
const names = selections.map(x => x.name.value)
const illegalNames = names.filter(
name => name.substr(0, 1) == name.substr(0, 1).match(/[A-Z]/)
)
if (illegalNames.length > 0) {
return res
.status(403)
.send({ error: `Illegal query, types not allowed: ${illegalNames}` })
}
}
next()
})
const endpointUrl = new URL(config.host) const endpointUrl = new URL(config.host)
// endpointUrl.port = process.env.GRAPHQL_LISTEN_PORT || 4001 // endpointUrl.port = process.env.GRAPHQL_LISTEN_PORT || 4001

View File

@ -4,6 +4,38 @@ const Mutation = {
async scanAll(root, args, ctx, info) { async scanAll(root, args, ctx, info) {
ctx.scanner.scanAll() ctx.scanner.scanAll()
return {
finished: false,
success: true,
progress: 0,
message: 'Starting scanner',
}
},
async scanUser(root, args, ctx, info) {
const session = ctx.driver.session()
const userResult = await session.run(
`MATCH (u:User { id: {userId} }) RETURN u`,
{
userId: args.userId,
}
)
session.close()
if (userResult.records.length == 0) {
return {
finished: false,
success: false,
progress: 0,
message: 'Could not scan user: User not found',
}
}
const user = userResult.records[0].get('u').properties
ctx.scanner.scanUser(user)
return { return {
finished: false, finished: false,
success: true, success: true,

View File

@ -120,7 +120,7 @@ class PhotoScanner {
async scanUser(user) { async scanUser(user) {
await _execScan(this, async () => { await _execScan(this, async () => {
await _scanUser({ driver: this.driver, scanAlbum: this.scanAlbum }, user) await _scanUser(this, user)
}) })
} }

View File

@ -130,6 +130,7 @@ type Mutation {
@neo4j_ignore @neo4j_ignore
scanAll: ScannerResult! @isAuthenticated @neo4j_ignore scanAll: ScannerResult! @isAuthenticated @neo4j_ignore
scanUser(userId: ID!): ScannerResult! @isAuthenticated @neo4j_ignore
initialSetupWizard( initialSetupWizard(
username: String! username: String!
@ -151,6 +152,7 @@ type Query {
siteInfo: SiteInfo siteInfo: SiteInfo
myUser: User @isAuthenticated myUser: User @isAuthenticated
user: [User] @hasRole(roles: [admin])
myAlbums: [Album] @isAuthenticated myAlbums: [Album] @isAuthenticated
album(id: ID): Album @isAuthenticated album(id: ID): Album @isAuthenticated

View File

@ -51,6 +51,14 @@ const changeUserPasswordMutation = gql`
} }
` `
const scanUserMutation = gql`
mutation scanUser($userId: ID!) {
scanUser(userId: $userId) {
success
}
}
`
const ChangePasswordModal = ({ onClose, user, ...props }) => { const ChangePasswordModal = ({ onClose, user, ...props }) => {
const [passwordInput, setPasswordInput] = useState('') const [passwordInput, setPasswordInput] = useState('')
@ -223,6 +231,17 @@ const UserRow = ({ user, refetchUsers }) => {
<Icon name="edit" /> <Icon name="edit" />
Edit Edit
</Button> </Button>
<Mutation mutation={scanUserMutation}>
{(scanUser, { data, called }) => (
<Button
disabled={called}
onClick={() => scanUser({ variables: { userId: user.id } })}
>
<Icon name="sync" />
Scan
</Button>
)}
</Mutation>
<Button onClick={() => setChangePassword(true)}> <Button onClick={() => setChangePassword(true)}>
<Icon name="key" /> <Icon name="key" />
Change password Change password

View File

@ -8,7 +8,7 @@ import AddUserRow from './AddUserRow'
const usersQuery = gql` const usersQuery = gql`
query settingsUsersQuery { query settingsUsersQuery {
User { user {
id id
username username
rootPath rootPath
@ -24,8 +24,8 @@ const UsersTable = () => {
<Query query={usersQuery}> <Query query={usersQuery}>
{({ loading, error, data, refetch }) => { {({ loading, error, data, refetch }) => {
let userRows = [] let userRows = []
if (data && data.User) { if (data && data.user) {
userRows = data.User.map(user => ( userRows = data.user.map(user => (
<UserRow user={user} refetchUsers={refetch} key={user.id} /> <UserRow user={user} refetchUsers={refetch} key={user.id} />
)) ))
} }