mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Merge remote-tracking branch 'origin/master' into 734-authorization-problem-disabling-post
This commit is contained in:
commit
736dc7d601
@ -29,7 +29,7 @@ script:
|
||||
- docker-compose exec backend yarn run test:jest --ci --verbose=false --coverage
|
||||
- docker-compose exec backend yarn run db:reset
|
||||
- docker-compose exec backend yarn run db:seed
|
||||
- docker-compose exec backend yarn run test:cucumber
|
||||
- docker-compose exec backend yarn run test:cucumber --tags "not @wip"
|
||||
- docker-compose exec backend yarn run db:reset
|
||||
- docker-compose exec backend yarn run db:seed
|
||||
# Frontend
|
||||
|
||||
@ -52,7 +52,7 @@
|
||||
"cheerio": "~1.0.0-rc.3",
|
||||
"cors": "~2.8.5",
|
||||
"cross-env": "~5.2.0",
|
||||
"date-fns": "2.0.0-alpha.32",
|
||||
"date-fns": "2.0.0-alpha.33",
|
||||
"debug": "~4.1.1",
|
||||
"dotenv": "~8.0.0",
|
||||
"express": "~4.17.1",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { rule, shield, allow, or } from 'graphql-shield'
|
||||
import { rule, shield, deny, allow, or } from 'graphql-shield'
|
||||
|
||||
/*
|
||||
* TODO: implement
|
||||
@ -16,6 +16,12 @@ const isAdmin = rule()(async (parent, args, { user }, info) => {
|
||||
return user && user.role === 'admin'
|
||||
})
|
||||
|
||||
const onlyYourself = rule({
|
||||
cache: 'no_cache',
|
||||
})(async (parent, args, context, info) => {
|
||||
return context.user.id === args.id
|
||||
})
|
||||
|
||||
const isMyOwn = rule({
|
||||
cache: 'no_cache',
|
||||
})(async (parent, args, context, info) => {
|
||||
@ -48,6 +54,13 @@ const belongsToMe = rule({
|
||||
return Boolean(notification)
|
||||
})
|
||||
|
||||
/* TODO: decide if we want to remove this check: the check
|
||||
* `onlyEnabledContent` throws authorization errors only if you have
|
||||
* arguments for `disabled` or `deleted` assuming these are filter
|
||||
* parameters. Soft-delete middleware obfuscates data on its way out
|
||||
* anyways. Furthermore, `neo4j-graphql-js` offers many ways to filter for
|
||||
* data so I believe, this is not a good check anyways.
|
||||
*/
|
||||
const onlyEnabledContent = rule({
|
||||
cache: 'strict',
|
||||
})(async (parent, args, ctx, info) => {
|
||||
@ -81,46 +94,62 @@ const isAuthor = rule({
|
||||
})
|
||||
|
||||
// Permissions
|
||||
const permissions = shield({
|
||||
Query: {
|
||||
Notification: isAdmin,
|
||||
statistics: allow,
|
||||
currentUser: allow,
|
||||
Post: or(onlyEnabledContent, isModerator),
|
||||
const permissions = shield(
|
||||
{
|
||||
Query: {
|
||||
'*': deny,
|
||||
findPosts: allow,
|
||||
Category: isAdmin,
|
||||
Tag: isAdmin,
|
||||
Report: isModerator,
|
||||
Notification: isAdmin,
|
||||
statistics: allow,
|
||||
currentUser: allow,
|
||||
Post: or(onlyEnabledContent, isModerator),
|
||||
Comment: allow,
|
||||
User: allow,
|
||||
isLoggedIn: allow,
|
||||
},
|
||||
Mutation: {
|
||||
'*': deny,
|
||||
login: allow,
|
||||
UpdateNotification: belongsToMe,
|
||||
CreateUser: isAdmin,
|
||||
UpdateUser: onlyYourself,
|
||||
CreatePost: isAuthenticated,
|
||||
UpdatePost: isAuthor,
|
||||
DeletePost: isAuthor,
|
||||
report: isAuthenticated,
|
||||
CreateBadge: isAdmin,
|
||||
UpdateBadge: isAdmin,
|
||||
DeleteBadge: isAdmin,
|
||||
AddUserBadges: isAdmin,
|
||||
CreateSocialMedia: isAuthenticated,
|
||||
DeleteSocialMedia: isAuthenticated,
|
||||
// AddBadgeRewarded: isAdmin,
|
||||
// RemoveBadgeRewarded: isAdmin,
|
||||
reward: isAdmin,
|
||||
unreward: isAdmin,
|
||||
// addFruitToBasket: isAuthenticated
|
||||
follow: isAuthenticated,
|
||||
unfollow: isAuthenticated,
|
||||
shout: isAuthenticated,
|
||||
unshout: isAuthenticated,
|
||||
changePassword: isAuthenticated,
|
||||
enable: isModerator,
|
||||
disable: isModerator,
|
||||
CreateComment: isAuthenticated,
|
||||
DeleteComment: isAuthor,
|
||||
},
|
||||
User: {
|
||||
email: isMyOwn,
|
||||
password: isMyOwn,
|
||||
privateKey: isMyOwn,
|
||||
},
|
||||
},
|
||||
Mutation: {
|
||||
UpdateNotification: belongsToMe,
|
||||
CreatePost: isAuthenticated,
|
||||
UpdatePost: isAuthor,
|
||||
DeletePost: isAuthor,
|
||||
report: isAuthenticated,
|
||||
CreateBadge: isAdmin,
|
||||
UpdateBadge: isAdmin,
|
||||
DeleteBadge: isAdmin,
|
||||
AddUserBadges: isAdmin,
|
||||
CreateSocialMedia: isAuthenticated,
|
||||
DeleteSocialMedia: isAuthenticated,
|
||||
// AddBadgeRewarded: isAdmin,
|
||||
// RemoveBadgeRewarded: isAdmin,
|
||||
reward: isAdmin,
|
||||
unreward: isAdmin,
|
||||
// addFruitToBasket: isAuthenticated
|
||||
follow: isAuthenticated,
|
||||
unfollow: isAuthenticated,
|
||||
shout: isAuthenticated,
|
||||
unshout: isAuthenticated,
|
||||
changePassword: isAuthenticated,
|
||||
enable: isModerator,
|
||||
disable: isModerator,
|
||||
CreateComment: isAuthenticated,
|
||||
DeleteComment: isAuthor,
|
||||
// CreateUser: allow,
|
||||
{
|
||||
fallbackRule: allow,
|
||||
},
|
||||
User: {
|
||||
email: isMyOwn,
|
||||
password: isMyOwn,
|
||||
privateKey: isMyOwn,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
export default permissions
|
||||
|
||||
@ -7,12 +7,14 @@ let headers
|
||||
const factory = Factory()
|
||||
|
||||
beforeEach(async () => {
|
||||
await factory.create('User', { email: 'user@example.org', password: '1234' })
|
||||
const adminParams = { role: 'admin', email: 'admin@example.org', password: '1234' }
|
||||
await factory.create('User', adminParams)
|
||||
await factory.create('User', {
|
||||
email: 'someone@example.org',
|
||||
password: '1234',
|
||||
})
|
||||
headers = await login({ email: 'user@example.org', password: '1234' })
|
||||
// we need to be an admin, otherwise we're not authorized to create a user
|
||||
headers = await login(adminParams)
|
||||
authenticatedClient = new GraphQLClient(host, { headers })
|
||||
})
|
||||
|
||||
|
||||
@ -315,6 +315,8 @@ describe('change password', () => {
|
||||
describe('do not expose private RSA key', () => {
|
||||
let headers
|
||||
let client
|
||||
let authenticatedClient
|
||||
|
||||
const queryUserPuplicKey = gql`
|
||||
query($queriedUserSlug: String) {
|
||||
User(slug: $queriedUserSlug) {
|
||||
@ -332,7 +334,7 @@ describe('do not expose private RSA key', () => {
|
||||
}
|
||||
`
|
||||
|
||||
const actionGenUserWithKeys = async () => {
|
||||
const generateUserWithKeys = async authenticatedClient => {
|
||||
// Generate user with "privateKey" via 'CreateUser' mutation instead of using the factories "factory.create('User', {...})", see above.
|
||||
const variables = {
|
||||
id: 'bcb2d923-f3af-479e-9f00-61b12e864667',
|
||||
@ -341,7 +343,7 @@ describe('do not expose private RSA key', () => {
|
||||
name: 'Apfel Strudel',
|
||||
email: 'apfel-strudel@test.org',
|
||||
}
|
||||
await client.request(
|
||||
await authenticatedClient.request(
|
||||
gql`
|
||||
mutation($id: ID, $password: String!, $slug: String, $name: String, $email: String!) {
|
||||
CreateUser(id: $id, password: $password, slug: $slug, name: $name, email: $email) {
|
||||
@ -353,14 +355,23 @@ describe('do not expose private RSA key', () => {
|
||||
)
|
||||
}
|
||||
|
||||
// not authenticate
|
||||
beforeEach(async () => {
|
||||
const adminParams = {
|
||||
role: 'admin',
|
||||
email: 'admin@example.org',
|
||||
password: '1234',
|
||||
}
|
||||
// create an admin user who has enough permissions to create other users
|
||||
await factory.create('User', adminParams)
|
||||
const headers = await login(adminParams)
|
||||
authenticatedClient = new GraphQLClient(host, { headers })
|
||||
// but also create an unauthenticated client to issue the `User` query
|
||||
client = new GraphQLClient(host)
|
||||
})
|
||||
|
||||
describe('unauthenticated query of "publicKey" (does the RSA key pair get generated at all?)', () => {
|
||||
it('returns publicKey', async () => {
|
||||
await actionGenUserWithKeys()
|
||||
await generateUserWithKeys(authenticatedClient)
|
||||
await expect(
|
||||
await client.request(queryUserPuplicKey, { queriedUserSlug: 'apfel-strudel' }),
|
||||
).toEqual(
|
||||
@ -378,7 +389,7 @@ describe('do not expose private RSA key', () => {
|
||||
|
||||
describe('unauthenticated query of "privateKey"', () => {
|
||||
it('throws "Not Authorised!"', async () => {
|
||||
await actionGenUserWithKeys()
|
||||
await generateUserWithKeys(authenticatedClient)
|
||||
await expect(
|
||||
client.request(queryUserPrivateKey, { queriedUserSlug: 'apfel-strudel' }),
|
||||
).rejects.toThrow('Not Authorised')
|
||||
@ -393,7 +404,7 @@ describe('do not expose private RSA key', () => {
|
||||
|
||||
describe('authenticated query of "publicKey"', () => {
|
||||
it('returns publicKey', async () => {
|
||||
await actionGenUserWithKeys()
|
||||
await generateUserWithKeys(authenticatedClient)
|
||||
await expect(
|
||||
await client.request(queryUserPuplicKey, { queriedUserSlug: 'apfel-strudel' }),
|
||||
).toEqual(
|
||||
@ -411,7 +422,7 @@ describe('do not expose private RSA key', () => {
|
||||
|
||||
describe('authenticated query of "privateKey"', () => {
|
||||
it('throws "Not Authorised!"', async () => {
|
||||
await actionGenUserWithKeys()
|
||||
await generateUserWithKeys(authenticatedClient)
|
||||
await expect(
|
||||
client.request(queryUserPrivateKey, { queriedUserSlug: 'apfel-strudel' }),
|
||||
).rejects.toThrow('Not Authorised')
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import { host } from '../../jest/helpers'
|
||||
import { login, host } from '../../jest/helpers'
|
||||
import Factory from '../../seed/factories'
|
||||
|
||||
const factory = Factory()
|
||||
@ -18,27 +18,58 @@ describe('users', () => {
|
||||
}
|
||||
}
|
||||
`
|
||||
client = new GraphQLClient(host)
|
||||
|
||||
it('with password and email', async () => {
|
||||
describe('given valid password and email', () => {
|
||||
const variables = {
|
||||
name: 'John Doe',
|
||||
password: '123',
|
||||
email: '123@123.de',
|
||||
}
|
||||
const expected = {
|
||||
CreateUser: {
|
||||
id: expect.any(String),
|
||||
},
|
||||
}
|
||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||
|
||||
describe('unauthenticated', () => {
|
||||
beforeEach(async () => {
|
||||
client = new GraphQLClient(host)
|
||||
})
|
||||
|
||||
it('is not allowed to create users', async () => {
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated admin', () => {
|
||||
beforeEach(async () => {
|
||||
const adminParams = {
|
||||
role: 'admin',
|
||||
email: 'admin@example.org',
|
||||
password: '1234',
|
||||
}
|
||||
await factory.create('User', adminParams)
|
||||
const headers = await login(adminParams)
|
||||
client = new GraphQLClient(host, { headers })
|
||||
})
|
||||
|
||||
it('is allowed to create new users', async () => {
|
||||
const expected = {
|
||||
CreateUser: {
|
||||
id: expect.any(String),
|
||||
},
|
||||
}
|
||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('UpdateUser', () => {
|
||||
beforeEach(async () => {
|
||||
await factory.create('User', { id: 'u47', name: 'John Doe' })
|
||||
})
|
||||
const userParams = {
|
||||
email: 'user@example.org',
|
||||
password: '1234',
|
||||
id: 'u47',
|
||||
name: 'John Doe',
|
||||
}
|
||||
const variables = {
|
||||
id: 'u47',
|
||||
name: 'John Doughnut',
|
||||
}
|
||||
|
||||
const mutation = `
|
||||
mutation($id: ID!, $name: String) {
|
||||
@ -48,38 +79,62 @@ describe('users', () => {
|
||||
}
|
||||
}
|
||||
`
|
||||
client = new GraphQLClient(host)
|
||||
|
||||
it('name within specifications', async () => {
|
||||
const variables = {
|
||||
id: 'u47',
|
||||
name: 'James Doe',
|
||||
}
|
||||
const expected = {
|
||||
UpdateUser: {
|
||||
id: 'u47',
|
||||
beforeEach(async () => {
|
||||
await factory.create('User', userParams)
|
||||
})
|
||||
|
||||
describe('as another user', () => {
|
||||
beforeEach(async () => {
|
||||
const someoneElseParams = {
|
||||
email: 'someoneElse@example.org',
|
||||
password: '1234',
|
||||
name: 'James Doe',
|
||||
},
|
||||
}
|
||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||
}
|
||||
|
||||
await factory.create('User', someoneElseParams)
|
||||
const headers = await login(someoneElseParams)
|
||||
client = new GraphQLClient(host, { headers })
|
||||
})
|
||||
|
||||
it('is not allowed to change other user accounts', async () => {
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||
})
|
||||
})
|
||||
|
||||
it('with no name', async () => {
|
||||
const variables = {
|
||||
id: 'u47',
|
||||
name: null,
|
||||
}
|
||||
const expected = 'Username must be at least 3 characters long!'
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow(expected)
|
||||
})
|
||||
describe('as the same user', () => {
|
||||
beforeEach(async () => {
|
||||
const headers = await login(userParams)
|
||||
client = new GraphQLClient(host, { headers })
|
||||
})
|
||||
|
||||
it('with too short name', async () => {
|
||||
const variables = {
|
||||
id: 'u47',
|
||||
name: ' ',
|
||||
}
|
||||
const expected = 'Username must be at least 3 characters long!'
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow(expected)
|
||||
it('name within specifications', async () => {
|
||||
const expected = {
|
||||
UpdateUser: {
|
||||
id: 'u47',
|
||||
name: 'John Doughnut',
|
||||
},
|
||||
}
|
||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||
})
|
||||
|
||||
it('with no name', async () => {
|
||||
const variables = {
|
||||
id: 'u47',
|
||||
name: null,
|
||||
}
|
||||
const expected = 'Username must be at least 3 characters long!'
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow(expected)
|
||||
})
|
||||
|
||||
it('with too short name', async () => {
|
||||
const variables = {
|
||||
id: 'u47',
|
||||
name: ' ',
|
||||
}
|
||||
const expected = 'Username must be at least 3 characters long!'
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow(expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -10,6 +10,7 @@ Feature: Follow a user
|
||||
| stuart-little |
|
||||
| tero-vota |
|
||||
|
||||
@wip
|
||||
Scenario: Send a follow to a user inbox and make sure it's added to the right followers collection
|
||||
When I send a POST request with the following activity to "/activitypub/users/tero-vota/inbox":
|
||||
"""
|
||||
|
||||
@ -27,6 +27,7 @@ Feature: Like an object like an article or note
|
||||
}
|
||||
"""
|
||||
|
||||
@wip
|
||||
Scenario: Send a like of a person to an users inbox and make sure it's added to the likes collection
|
||||
When I send a POST request with the following activity to "/activitypub/users/karl-heinz/inbox":
|
||||
"""
|
||||
|
||||
@ -2586,10 +2586,10 @@ data-urls@^1.0.0:
|
||||
whatwg-mimetype "^2.2.0"
|
||||
whatwg-url "^7.0.0"
|
||||
|
||||
date-fns@2.0.0-alpha.32:
|
||||
version "2.0.0-alpha.32"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.32.tgz#e36472aac2ff49b199dd7b257807ca1c89bbe603"
|
||||
integrity sha512-Rv9g3piGxEp10ujgPODgVDfkn1Xz1HDZASEKmGsP/RhU4DCAugdNfnEjE52mvz8G3Ha5XFlQMDEb6Lwryb9Wfg==
|
||||
date-fns@2.0.0-alpha.33:
|
||||
version "2.0.0-alpha.33"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.33.tgz#c2f73c3cc50ac301c9217eb93603c9bc40e891bf"
|
||||
integrity sha512-tqUVEk3oxnJuNIvwAMKHAMo4uFRG0zXvjxZQll+BonoPt+m4NMcUgO14NDxbHuy7uYcrVErd2GdSsw02EDZQ7w==
|
||||
|
||||
debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
|
||||
version "2.6.9"
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
"cross-env": "^5.2.0",
|
||||
"cypress": "^3.3.1",
|
||||
"cypress-cucumber-preprocessor": "^1.12.0",
|
||||
"cypress-file-upload": "^3.1.3",
|
||||
"cypress-file-upload": "^3.1.4",
|
||||
"cypress-plugin-retries": "^1.2.2",
|
||||
"dotenv": "^8.0.0",
|
||||
"faker": "^4.1.0",
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<ds-flex-item :width="width">
|
||||
<ds-card :image="post.image" :class="{ 'post-card': true, 'disabled-content': post.disabled }">
|
||||
<ds-card
|
||||
:image="post.image | proxyApiUrl"
|
||||
:class="{ 'post-card': true, 'disabled-content': post.disabled }"
|
||||
>
|
||||
<!-- Post Link Target -->
|
||||
<nuxt-link
|
||||
class="post-link"
|
||||
|
||||
@ -1822,10 +1822,10 @@ cypress-cucumber-preprocessor@^1.12.0:
|
||||
glob "^7.1.2"
|
||||
through "^2.3.8"
|
||||
|
||||
cypress-file-upload@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-3.1.3.tgz#1627f46e2f07ff0e19452f01cd66318d44069799"
|
||||
integrity sha512-77GphGrVYzIOueAgTYQhttUMmBswoDmCYEFca/o+THylJXcl5Cmh5dIxsqzHCpOl0FdIzRmNsiJki6rW6UHiyQ==
|
||||
cypress-file-upload@^3.1.4:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-3.1.4.tgz#cc208cb937a3abb136b52309eaf4637d5676c5bd"
|
||||
integrity sha512-4aZeJOYFhYiP+nk9Mo5YHWqComsT24J9OBQVJzvkEzw7g1v2ogGe7nLT/U7Fsm/Xjl1Tyxsc0xxECa254WfQqg==
|
||||
|
||||
cypress-plugin-retries@^1.2.2:
|
||||
version "1.2.2"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user