Secure graphql endpoint
add option to scan individual users photos
This commit is contained in:
parent
25a00e93f1
commit
2a58f0b5ab
|
@ -1,7 +1,5 @@
|
|||
# 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)
|
||||
|
||||
![screenshot](/screenshot.png)
|
||||
|
|
|
@ -7,6 +7,7 @@ import http from 'http'
|
|||
import PhotoScanner from './scanner/Scanner'
|
||||
import _ from 'lodash'
|
||||
import config from './config'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
import { getUserFromToken, getTokenFromBearer } from './token'
|
||||
|
||||
|
@ -32,6 +33,7 @@ const scanner = new PhotoScanner(driver)
|
|||
app.use((req, res, next) => {
|
||||
req.driver = driver
|
||||
req.scanner = scanner
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
|
@ -41,6 +43,30 @@ setInterval(scanner.scanAll, 1000 * 60 * 60 * 4)
|
|||
// Specify port and path for GraphQL endpoint
|
||||
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)
|
||||
// endpointUrl.port = process.env.GRAPHQL_LISTEN_PORT || 4001
|
||||
|
||||
|
|
|
@ -4,6 +4,38 @@ const Mutation = {
|
|||
async scanAll(root, args, ctx, info) {
|
||||
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 {
|
||||
finished: false,
|
||||
success: true,
|
||||
|
|
|
@ -120,7 +120,7 @@ class PhotoScanner {
|
|||
|
||||
async scanUser(user) {
|
||||
await _execScan(this, async () => {
|
||||
await _scanUser({ driver: this.driver, scanAlbum: this.scanAlbum }, user)
|
||||
await _scanUser(this, user)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -130,6 +130,7 @@ type Mutation {
|
|||
@neo4j_ignore
|
||||
|
||||
scanAll: ScannerResult! @isAuthenticated @neo4j_ignore
|
||||
scanUser(userId: ID!): ScannerResult! @isAuthenticated @neo4j_ignore
|
||||
|
||||
initialSetupWizard(
|
||||
username: String!
|
||||
|
@ -151,6 +152,7 @@ type Query {
|
|||
siteInfo: SiteInfo
|
||||
|
||||
myUser: User @isAuthenticated
|
||||
user: [User] @hasRole(roles: [admin])
|
||||
|
||||
myAlbums: [Album] @isAuthenticated
|
||||
album(id: ID): Album @isAuthenticated
|
||||
|
|
|
@ -51,6 +51,14 @@ const changeUserPasswordMutation = gql`
|
|||
}
|
||||
`
|
||||
|
||||
const scanUserMutation = gql`
|
||||
mutation scanUser($userId: ID!) {
|
||||
scanUser(userId: $userId) {
|
||||
success
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const ChangePasswordModal = ({ onClose, user, ...props }) => {
|
||||
const [passwordInput, setPasswordInput] = useState('')
|
||||
|
||||
|
@ -223,6 +231,17 @@ const UserRow = ({ user, refetchUsers }) => {
|
|||
<Icon name="edit" />
|
||||
Edit
|
||||
</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)}>
|
||||
<Icon name="key" />
|
||||
Change password
|
||||
|
|
|
@ -8,7 +8,7 @@ import AddUserRow from './AddUserRow'
|
|||
|
||||
const usersQuery = gql`
|
||||
query settingsUsersQuery {
|
||||
User {
|
||||
user {
|
||||
id
|
||||
username
|
||||
rootPath
|
||||
|
@ -24,8 +24,8 @@ const UsersTable = () => {
|
|||
<Query query={usersQuery}>
|
||||
{({ loading, error, data, refetch }) => {
|
||||
let userRows = []
|
||||
if (data && data.User) {
|
||||
userRows = data.User.map(user => (
|
||||
if (data && data.user) {
|
||||
userRows = data.user.map(user => (
|
||||
<UserRow user={user} refetchUsers={refetch} key={user.id} />
|
||||
))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue