-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #142 from CentreForDigitalHumanities/feature/user-…
…permissions Feature/user permissions
- Loading branch information
Showing
24 changed files
with
225 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,21 @@ | ||
import json | ||
from django.conf import settings | ||
from django.contrib.auth.models import AnonymousUser | ||
from user.models import User | ||
from typing import Union | ||
from graphene import ResolveInfo | ||
|
||
|
||
class GraphQLAuthMiddleware: | ||
def resolve(self, next, root, info, **kwargs): | ||
request = info.context | ||
def has_mutation_permission(user: Union[AnonymousUser, User]) -> bool: | ||
return user.is_superuser or getattr(user, "is_contributor", False) | ||
|
||
|
||
# Allow introspection queries to pass through in development mode. | ||
if settings.DEBUG and self.is_introspection_query(request): | ||
return next(root, info, **kwargs) | ||
def is_mutation(info: ResolveInfo) -> bool: | ||
return info.parent_type.name == "Mutation" | ||
|
||
if info.context.user.is_authenticated: | ||
return next(root, info, **kwargs) | ||
|
||
raise Exception("User is not authenticated") | ||
class GraphQLAuthMiddleware: | ||
|
||
def resolve(self, next, root, info: ResolveInfo, **kwargs): | ||
if is_mutation(info) and not has_mutation_permission(info.context.user): | ||
raise Exception("User is not authorised to make mutations") | ||
|
||
def is_introspection_query(self, request): | ||
data = json.loads(request.body) | ||
query = data.get("query") # type: str | ||
return query.startswith("query IntrospectionQuery") | ||
return next(root, info, **kwargs) |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from rest_framework.status import is_success | ||
import json | ||
|
||
QUERY_EXAMPLE = """ | ||
query TestQuery { | ||
sources { | ||
id | ||
} | ||
} | ||
""" | ||
|
||
MUTATION_EXAMPLE = """ | ||
mutation TestMutation { | ||
createAgent(agentData: {name: "test", source: "1"}) { | ||
ok | ||
} | ||
} | ||
""" | ||
|
||
|
||
def test_middleware_passes_queries(client, db): | ||
response = client.post( | ||
"/api/graphql", | ||
{ | ||
"operationName": "TestQuery", | ||
"query": QUERY_EXAMPLE, | ||
}, | ||
) | ||
|
||
assert is_success(response.status_code) | ||
data = json.loads(response.content) | ||
assert not "errors" in data | ||
|
||
|
||
def test_middleware_blocks_mutation_from_unauthorised_user(user_client, source): | ||
response = user_client.post( | ||
"/api/graphql", | ||
{ | ||
"operationName": "TestMutation", | ||
"query": MUTATION_EXAMPLE, | ||
}, | ||
) | ||
|
||
assert is_success(response.status_code) | ||
data = json.loads(response.content) | ||
assert data["errors"][0]["message"] == "User is not authorised to make mutations" | ||
|
||
|
||
def test_middleware_passes_mutation_from_authorised_user(user, user_client, source): | ||
user.is_contributor = True | ||
user.save() | ||
|
||
response = user_client.post( | ||
"/api/graphql", | ||
{ | ||
"operationName": "TestMutation", | ||
"query": MUTATION_EXAMPLE, | ||
}, | ||
) | ||
|
||
assert is_success(response.status_code) | ||
data = json.loads(response.content) | ||
assert not "errors" in data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Generated by Django 4.2.7 on 2024-10-01 13:24 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('user', '0003_sitedomain'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='user', | ||
name='is_contributor', | ||
field=models.BooleanField(default=False, help_text='Whether this user is a contributor on the project; this enables them to enter or edit research data.'), | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,8 @@ const fakeUserResponse: UserResponse = { | |
email: "[email protected]", | ||
first_name: "Frodo", | ||
last_name: "Baggins", | ||
is_staff: false | ||
is_staff: false, | ||
is_contributor: true, | ||
} | ||
|
||
const fakeAdminResponse: UserResponse = { | ||
|
@@ -21,7 +22,8 @@ const fakeAdminResponse: UserResponse = { | |
email: "[email protected]", | ||
first_name: "Gandalf", | ||
last_name: "The Grey", | ||
is_staff: true | ||
is_staff: true, | ||
is_contributor: false, | ||
} | ||
|
||
|
||
|
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { inject } from "@angular/core"; | ||
import { CanActivateFn, Router } from "@angular/router"; | ||
import { AuthService } from "@services/auth.service"; | ||
import { ToastService } from "@services/toast.service"; | ||
import { filter, map } from "rxjs"; | ||
|
||
|
||
export const ContributorGuard: CanActivateFn = () => { | ||
const authService = inject(AuthService); | ||
const toastService = inject(ToastService); | ||
const router = inject(Router); | ||
|
||
return authService.currentUser$.pipe( | ||
filter((user) => user !== undefined), | ||
map((user) => { | ||
if (user?.isContributor) { | ||
return true; | ||
} else { | ||
let body = 'You do not have permission to view this page.'; | ||
if (!user) { body += ' Do you need to sign in?'; } | ||
toastService.show({ | ||
type: 'danger', | ||
header: 'Not authorised', | ||
body, | ||
}); | ||
return router.createUrlTree(['/']); | ||
} | ||
}), | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { TestBed } from "@angular/core/testing"; | ||
import { CanActivateFn } from "@angular/router"; | ||
import { ContributorGuard } from "./contributor.guard"; | ||
|
||
|
||
|
||
describe("LoggedOnGuard", () => { | ||
const executeGuard: CanActivateFn = (...guardParameters) => | ||
TestBed.runInInjectionContext(() => ContributorGuard(...guardParameters)); | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({}); | ||
}); | ||
|
||
it("should be created", () => { | ||
expect(executeGuard).toBeTruthy(); | ||
}); | ||
}); |
File renamed without changes.
Oops, something went wrong.