Skip to content

Commit

Permalink
(fix) token permissions resolution order and ensure backward compatib…
Browse files Browse the repository at this point in the history
…ility with single token management
  • Loading branch information
danh91 committed Dec 11, 2023
1 parent d8081d3 commit 3e03011
Show file tree
Hide file tree
Showing 9 changed files with 458 additions and 33 deletions.
2 changes: 1 addition & 1 deletion modules/core/karrio/server/core/dataunits.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ def contextual_metadata(request: Request):


def contextual_reference(request: Request = None, reduced: bool = True):
import karrio.server.core.validators as validators
import karrio.server.core.gateway as gateway
import karrio.server.core.validators as validators
import karrio.server.core.middleware as middleware

request = request or middleware.SessionContext.get_current_request()
Expand Down
42 changes: 32 additions & 10 deletions modules/core/karrio/server/user/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,28 @@ def object_type(self):

@property
def permissions(self):
_permissions = self.groups.all().values_list("name", flat=True)
import karrio.server.conf as conf
import karrio.server.iam.models as iam
import karrio.server.core.middleware as middleware

ctx = middleware.SessionContext.get_current_request()
_permissions = []

if conf.settings.MULTI_ORGANIZATIONS and ctx.org is not None:
org_user = ctx.org.organization_users.filter(user_id=self.pk)
_permissions = (
iam.ContextPermission.objects.get(
object_pk=org_user.first().pk,
content_type=ContentType.objects.get_for_model(org_user.first()),
)
.groups.all()
.values_list("name", flat=True)
if org_user.exists()
else []
)

if not any(_permissions):
_permissions = self.groups.all().values_list("name", flat=True)

if not any(_permissions) and self.is_superuser:
return Group.objects.all().values_list("name", flat=True)
Expand Down Expand Up @@ -119,30 +140,31 @@ def permissions(self):

_permissions = []

if conf.settings.MULTI_ORGANIZATIONS and self.org.exists():
org_user = self.org.first().organization_users.filter(user_id=self.user_id)
if iam.ContextPermission.objects.filter(object_pk=self.pk).exists():
_permissions = (
iam.ContextPermission.objects.get(
object_pk=org_user.first().pk,
content_type=ContentType.objects.get_for_model(org_user.first()),
object_pk=self.pk,
content_type=ContentType.objects.get_for_model(Token),
)
.groups.all()
.values_list("name", flat=True)
if org_user.exists()
else []
)

if (
not any(_permissions)
and iam.ContextPermission.objects.filter(object_pk=self.pk).exists()
and conf.settings.MULTI_ORGANIZATIONS
and self.org.exists()
):
org_user = self.org.first().organization_users.filter(user_id=self.user_id)
_permissions = (
iam.ContextPermission.objects.get(
object_pk=self.pk,
content_type=ContentType.objects.get_for_model(Token),
object_pk=org_user.first().pk,
content_type=ContentType.objects.get_for_model(org_user.first()),
)
.groups.all()
.values_list("name", flat=True)
if org_user.exists()
else []
)

return _permissions if any(_permissions) else self.user.permissions
Expand Down
1 change: 1 addition & 0 deletions modules/graph/karrio/server/graph/schemas/base/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class UpdateUserInput(utils.BaseInput):

@strawberry.input
class TokenMutationInput(utils.BaseInput):
key: str
password: typing.Optional[str] = strawberry.UNSET
refresh: typing.Optional[bool] = strawberry.UNSET

Expand Down
13 changes: 10 additions & 3 deletions modules/graph/karrio/server/graph/schemas/base/mutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,12 @@ class TokenMutation(utils.BaseMutation):
@utils.authentication_required
@utils.authorization_required()
def mutate(
info: Info, refresh: bool = None, password: str = None
info: Info,
key: str = None,
refresh: bool = None,
password: str = None,
) -> "UserUpdateMutation":
tokens = user_models.Token.access_by(info.context.request)
tokens = user_models.Token.access_by(info.context.request).filter(key=key)

if refresh:
if len(password or "") == 0:
Expand Down Expand Up @@ -105,7 +108,11 @@ def mutate(
api_key = TokenSerializer.map(data=data, context=context).save().instance

if any(permissions):
_auth_ctx = getattr(context, "token", context.user)
_auth_ctx = (
context.token
if hasattr(getattr(info.context.request, "token", None), "permissions")
else context.user
)
_ctx_permissions = getattr(_auth_ctx, "permissions", [])
_invalid_permissions = [_ for _ in permissions if _ not in _ctx_permissions]

Expand Down
16 changes: 0 additions & 16 deletions modules/graph/karrio/server/graph/schemas/base/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,6 @@ def permissions(self: User, info) -> typing.Optional[typing.List[str]]:
if hasattr(getattr(info.context.request, "token", None), "permissions"):
return info.context.request.token.permissions

# Return permissions from org user if multiple orgs context
if hasattr(info.context.request, "org"):
org_user = info.context.request.org.organization_users.filter(
user_id=self.id
)
return (
iam.ContextPermission.objects.get(
object_pk=org_user.first().pk,
content_type=ContentType.objects.get_for_model(org_user.first()),
)
.groups.all()
.values_list("name", flat=True)
if org_user.exists()
else []
)

# Return permissions from user
return info.context.request.user.permissions

Expand Down
2 changes: 1 addition & 1 deletion modules/graph/karrio/server/graph/tests/test_user_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def test_update_token(self):
}
""",
operation_name="mutate_token",
variables=TOKEN_MUTATION_DATA,
variables={**TOKEN_MUTATION_DATA, "key": current_token},
)
response_data = response.data

Expand Down
1 change: 1 addition & 0 deletions packages/types/graphql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6517,6 +6517,7 @@ export interface UpdateAddressTemplateInput {

// null
export interface TokenMutationInput {
key: string;
password?: string | null;
refresh?: boolean | null;
}
Expand Down
5 changes: 3 additions & 2 deletions packages/ui/modals/generate-api-dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useAPITokenMutation } from '@karrio/hooks/api-token';
import { useAPIToken, useAPITokenMutation } from '@karrio/hooks/api-token';
import React, { useContext, useRef, useState } from 'react';
import { Notifier, Notify } from '../components/notifier';
import { NotificationType } from '@karrio/types';
Expand All @@ -12,13 +12,14 @@ export const GenerateAPIModal: React.FC<{ children?: React.ReactNode }> = ({ chi
const { loading, setLoading } = useLoader();
const password = useRef<HTMLInputElement>(null);
const { query: { data: { user } = {} } } = useUser();
const { query: { data: { token } = {} } } = useAPIToken();
const [isActive, setIsActive] = useState<boolean>(false);

const handleSubmit = async (evt: React.FormEvent<HTMLFormElement>) => {
evt.preventDefault();
try {
setLoading(true);
await mutation.updateToken.mutateAsync({ refresh: true, password: password.current?.value });
await mutation.updateToken.mutateAsync({ refresh: true, key: token!.key, password: password.current?.value });
setLoading(false);
setIsActive(false);
notify({ type: NotificationType.success, message: "New token generated successfully!" });
Expand Down
Loading

0 comments on commit 3e03011

Please sign in to comment.