Redirect to login when groups or accounts 403

This commit is contained in:
Alex Gleason 2023-06-29 15:10:45 -05:00
parent 5ffaaa4c3a
commit bcac58b9c3
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
7 changed files with 68 additions and 10 deletions

View file

@ -1,3 +1,6 @@
import { useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { Entities } from 'soapbox/entity-store/entities';
import { useEntity } from 'soapbox/entity-store/hooks';
import { useFeatures, useLoggedIn } from 'soapbox/hooks';
@ -12,11 +15,12 @@ interface UseAccountOpts {
function useAccount(accountId?: string, opts: UseAccountOpts = {}) {
const api = useApi();
const history = useHistory();
const features = useFeatures();
const { me } = useLoggedIn();
const { withRelationship } = opts;
const { entity: account, ...result } = useEntity<Account>(
const { entity: account, isUnauthorized, ...result } = useEntity<Account>(
[Entities.ACCOUNTS, accountId!],
() => api.get(`/api/v1/accounts/${accountId}`),
{ schema: accountSchema, enabled: !!accountId },
@ -30,10 +34,17 @@ function useAccount(accountId?: string, opts: UseAccountOpts = {}) {
const isBlocked = account?.relationship?.blocked_by === true;
const isUnavailable = (me === account?.id) ? false : (isBlocked && !features.blockersVisible);
useEffect(() => {
if (isUnauthorized) {
history.push('/login');
}
}, [isUnauthorized]);
return {
...result,
isLoading: result.isLoading,
isRelationshipLoading,
isUnauthorized,
isUnavailable,
account: account ? { ...account, relationship } : undefined,
};

View file

@ -1,3 +1,6 @@
import { useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { Entities } from 'soapbox/entity-store/entities';
import { useEntityLookup } from 'soapbox/entity-store/hooks';
import { useFeatures, useLoggedIn } from 'soapbox/hooks';
@ -13,10 +16,11 @@ interface UseAccountLookupOpts {
function useAccountLookup(acct: string | undefined, opts: UseAccountLookupOpts = {}) {
const api = useApi();
const features = useFeatures();
const history = useHistory();
const { me } = useLoggedIn();
const { withRelationship } = opts;
const { entity: account, ...result } = useEntityLookup<Account>(
const { entity: account, isUnauthorized, ...result } = useEntityLookup<Account>(
Entities.ACCOUNTS,
(account) => account.acct === acct,
() => api.get(`/api/v1/accounts/lookup?acct=${acct}`),
@ -31,10 +35,17 @@ function useAccountLookup(acct: string | undefined, opts: UseAccountLookupOpts =
const isBlocked = account?.relationship?.blocked_by === true;
const isUnavailable = (me === account?.id) ? false : (isBlocked && !features.blockersVisible);
useEffect(() => {
if (isUnauthorized) {
history.push('/login');
}
}, [isUnauthorized]);
return {
...result,
isLoading: result.isLoading,
isRelationshipLoading,
isUnauthorized,
isUnavailable,
account: account ? { ...account, relationship } : undefined,
};

View file

@ -1,3 +1,6 @@
import { useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { Entities } from 'soapbox/entity-store/entities';
import { useEntity } from 'soapbox/entity-store/hooks';
import { useApi } from 'soapbox/hooks';
@ -7,8 +10,9 @@ import { useGroupRelationship } from './useGroupRelationship';
function useGroup(groupId: string, refetch = true) {
const api = useApi();
const history = useHistory();
const { entity: group, ...result } = useEntity<Group>(
const { entity: group, isUnauthorized, ...result } = useEntity<Group>(
[Entities.GROUPS, groupId],
() => api.get(`/api/v1/groups/${groupId}`),
{
@ -19,8 +23,15 @@ function useGroup(groupId: string, refetch = true) {
);
const { groupRelationship: relationship } = useGroupRelationship(groupId);
useEffect(() => {
if (isUnauthorized) {
history.push('/login');
}
}, [isUnauthorized]);
return {
...result,
isUnauthorized,
group: group ? { ...group, relationship: relationship || null } : undefined,
};
}

View file

@ -1,24 +1,37 @@
import { useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { Entities } from 'soapbox/entity-store/entities';
import { useEntityLookup } from 'soapbox/entity-store/hooks';
import { useApi } from 'soapbox/hooks/useApi';
import { useFeatures } from 'soapbox/hooks/useFeatures';
import { groupSchema } from 'soapbox/schemas';
import { useGroupRelationship } from './useGroupRelationship';
function useGroupLookup(slug: string) {
const api = useApi();
const features = useFeatures();
const history = useHistory();
const { entity: group, ...result } = useEntityLookup(
const { entity: group, isUnauthorized, ...result } = useEntityLookup(
Entities.GROUPS,
(group) => group.slug === slug,
() => api.get(`/api/v1/groups/lookup?name=${slug}`),
{ schema: groupSchema, enabled: !!slug },
{ schema: groupSchema, enabled: features.groups && !!slug },
);
const { groupRelationship: relationship } = useGroupRelationship(group?.id);
useEffect(() => {
if (isUnauthorized) {
history.push('/login');
}
}, [isUnauthorized]);
return {
...result,
isUnauthorized,
entity: group ? { ...group, relationship: relationship || null } : undefined,
};
}

View file

@ -1,4 +1,5 @@
import { useEffect } from 'react';
import { AxiosError } from 'axios';
import { useEffect, useState } from 'react';
import z from 'zod';
import { useAppDispatch, useAppSelector, useLoading } from 'soapbox/hooks';
@ -24,6 +25,8 @@ function useEntity<TEntity extends Entity>(
opts: UseEntityOpts<TEntity> = {},
) {
const [isFetching, setPromise] = useLoading(true);
const [error, setError] = useState<unknown>();
const dispatch = useAppDispatch();
const [entityType, entityId] = path;
@ -35,6 +38,7 @@ function useEntity<TEntity extends Entity>(
const isEnabled = opts.enabled ?? true;
const isLoading = isFetching && !entity;
const isLoaded = !isFetching && !!entity;
const fetchEntity = async () => {
try {
@ -42,7 +46,7 @@ function useEntity<TEntity extends Entity>(
const entity = schema.parse(response.data);
dispatch(importEntities([entity], entityType));
} catch (e) {
// do nothing
setError(e);
}
};
@ -58,6 +62,10 @@ function useEntity<TEntity extends Entity>(
fetchEntity,
isFetching,
isLoading,
isLoaded,
error,
isUnauthorized: error instanceof AxiosError && error.response?.status === 401,
isForbidden: error instanceof AxiosError && error.response?.status === 403,
};
}

View file

@ -1,4 +1,5 @@
import { useEffect } from 'react';
import { AxiosError } from 'axios';
import { useEffect, useState } from 'react';
import { z } from 'zod';
import { useAppDispatch, useAppSelector, useLoading } from 'soapbox/hooks';
@ -23,6 +24,7 @@ function useEntityLookup<TEntity extends Entity>(
const dispatch = useAppDispatch();
const [isFetching, setPromise] = useLoading(true);
const [error, setError] = useState<unknown>();
const entity = useAppSelector(state => findEntity(state, entityType, lookupFn));
const isEnabled = opts.enabled ?? true;
@ -34,7 +36,7 @@ function useEntityLookup<TEntity extends Entity>(
const entity = schema.parse(response.data);
dispatch(importEntities([entity], entityType));
} catch (e) {
// do nothing
setError(e);
}
};
@ -51,6 +53,8 @@ function useEntityLookup<TEntity extends Entity>(
fetchEntity,
isFetching,
isLoading,
isUnauthorized: error instanceof AxiosError && error.response?.status === 401,
isForbidden: error instanceof AxiosError && error.response?.status === 403,
};
}

View file

@ -327,7 +327,7 @@ const SwitchingColumnsArea: React.FC<ISwitchingColumnsArea> = ({ children }) =>
{features.groupsTags && <WrappedRoute path='/group/:groupSlug/tags' exact page={GroupPage} component={GroupTagsSlug} content={children} />}
{features.groupsTags && <WrappedRoute path='/group/:groupSlug/tag/:tagId' exact page={GroupsPendingPage} component={GroupTagTimelineSlug} content={children} />}
{features.groups && <WrappedRoute path='/group/:groupSlug' exact page={GroupPage} component={GroupTimelineSlug} content={children} />}
{features.groups && <WrappedRoute path='/group/:groupSlug' publicRoute exact page={GroupPage} component={GroupTimelineSlug} content={children} />}
{features.groups && <WrappedRoute path='/group/:groupSlug/members' exact page={GroupPage} component={GroupMembersSlug} content={children} />}
{features.groups && <WrappedRoute path='/group/:groupSlug/media' publicRoute={!authenticatedProfile} component={GroupGallerySlug} page={GroupPage} content={children} />}
{features.groups && <WrappedRoute path='/group/:groupSlug/manage' exact page={ManageGroupsPage} component={ManageGroupSlug} content={children} />}