Add fields for admin panel access and workspace version (#10451)
Prepare for better version upgrade system + split admin panel into two permissions + fix GraphQL generation detection --------- Co-authored-by: ehconitin <nitinkoche03@gmail.com>
This commit is contained in:
17
.github/workflows/ci-server.yaml
vendored
17
.github/workflows/ci-server.yaml
vendored
@ -108,15 +108,14 @@ jobs:
|
|||||||
- name: GraphQL / Check for Pending Generation
|
- name: GraphQL / Check for Pending Generation
|
||||||
if: steps.changed-files.outputs.any_changed == 'true'
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
run: |
|
run: |
|
||||||
GRAPHQL_GENERATE_OUTPUT=$(npx nx run twenty-front:graphql:generate || true)
|
# Run GraphQL generation commands
|
||||||
GRAPHQL_METADATA_OUTPUT=$(npx nx run twenty-front:graphql:generate --configuration=metadata || true)
|
npx nx run twenty-front:graphql:generate
|
||||||
if [[ $GRAPHQL_GENERATE_OUTPUT == *"No changes detected"* && $GRAPHQL_METADATA_OUTPUT == *"No changes detected"* ]]; then
|
npx nx run twenty-front:graphql:generate --configuration=metadata
|
||||||
echo "GraphQL generation check passed."
|
|
||||||
else
|
# Check if any files were modified
|
||||||
echo "::error::Unexpected GraphQL changes detected. Please run the required commands and commit the changes."
|
if ! git diff --quiet; then
|
||||||
echo "$GRAPHQL_GENERATE_OUTPUT"
|
echo "::error::GraphQL schema changes detected. Please run 'npx nx run twenty-front:graphql:generate' and 'npx nx run twenty-front:graphql:generate --configuration=metadata' and commit the changes."
|
||||||
echo "$GRAPHQL_METADATA_OUTPUT"
|
exit 1
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
- name: Save server setup
|
- name: Save server setup
|
||||||
uses: ./.github/workflows/actions/save-cache
|
uses: ./.github/workflows/actions/save-cache
|
||||||
|
|||||||
@ -1815,7 +1815,7 @@ export enum SettingsPermissions {
|
|||||||
ROLES = 'ROLES',
|
ROLES = 'ROLES',
|
||||||
SECURITY = 'SECURITY',
|
SECURITY = 'SECURITY',
|
||||||
WORKSPACE = 'WORKSPACE',
|
WORKSPACE = 'WORKSPACE',
|
||||||
WORKSPACE_USERS = 'WORKSPACE_USERS'
|
WORKSPACE_MEMBERS = 'WORKSPACE_MEMBERS'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SetupOidcSsoInput = {
|
export type SetupOidcSsoInput = {
|
||||||
@ -2058,6 +2058,7 @@ export type UpdateWorkspaceInput = {
|
|||||||
export type User = {
|
export type User = {
|
||||||
__typename?: 'User';
|
__typename?: 'User';
|
||||||
analyticsTinybirdJwts?: Maybe<AnalyticsTinybirdJwtMap>;
|
analyticsTinybirdJwts?: Maybe<AnalyticsTinybirdJwtMap>;
|
||||||
|
canAccessFullAdminPanel: Scalars['Boolean']['output'];
|
||||||
canImpersonate: Scalars['Boolean']['output'];
|
canImpersonate: Scalars['Boolean']['output'];
|
||||||
createdAt: Scalars['DateTime']['output'];
|
createdAt: Scalars['DateTime']['output'];
|
||||||
currentUserWorkspace?: Maybe<UserWorkspace>;
|
currentUserWorkspace?: Maybe<UserWorkspace>;
|
||||||
@ -2211,6 +2212,7 @@ export type Workspace = {
|
|||||||
metadataVersion: Scalars['Float']['output'];
|
metadataVersion: Scalars['Float']['output'];
|
||||||
subdomain: Scalars['String']['output'];
|
subdomain: Scalars['String']['output'];
|
||||||
updatedAt: Scalars['DateTime']['output'];
|
updatedAt: Scalars['DateTime']['output'];
|
||||||
|
version?: Maybe<Scalars['String']['output']>;
|
||||||
workspaceMembersCount?: Maybe<Scalars['Float']['output']>;
|
workspaceMembersCount?: Maybe<Scalars['Float']['output']>;
|
||||||
workspaceUrls: WorkspaceUrls;
|
workspaceUrls: WorkspaceUrls;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1846,6 +1846,7 @@ export type UpdateWorkspaceInput = {
|
|||||||
export type User = {
|
export type User = {
|
||||||
__typename?: 'User';
|
__typename?: 'User';
|
||||||
analyticsTinybirdJwts?: Maybe<AnalyticsTinybirdJwtMap>;
|
analyticsTinybirdJwts?: Maybe<AnalyticsTinybirdJwtMap>;
|
||||||
|
canAccessFullAdminPanel: Scalars['Boolean'];
|
||||||
canImpersonate: Scalars['Boolean'];
|
canImpersonate: Scalars['Boolean'];
|
||||||
createdAt: Scalars['DateTime'];
|
createdAt: Scalars['DateTime'];
|
||||||
currentUserWorkspace?: Maybe<UserWorkspace>;
|
currentUserWorkspace?: Maybe<UserWorkspace>;
|
||||||
@ -1989,6 +1990,7 @@ export type Workspace = {
|
|||||||
metadataVersion: Scalars['Float'];
|
metadataVersion: Scalars['Float'];
|
||||||
subdomain: Scalars['String'];
|
subdomain: Scalars['String'];
|
||||||
updatedAt: Scalars['DateTime'];
|
updatedAt: Scalars['DateTime'];
|
||||||
|
version?: Maybe<Scalars['String']>;
|
||||||
workspaceMembersCount?: Maybe<Scalars['Float']>;
|
workspaceMembersCount?: Maybe<Scalars['Float']>;
|
||||||
workspaceUrls: WorkspaceUrls;
|
workspaceUrls: WorkspaceUrls;
|
||||||
};
|
};
|
||||||
@ -2434,7 +2436,7 @@ export type GetSsoIdentityProvidersQueryVariables = Exact<{ [key: string]: never
|
|||||||
|
|
||||||
export type GetSsoIdentityProvidersQuery = { __typename?: 'Query', getSSOIdentityProviders: Array<{ __typename?: 'FindAvailableSSOIDPOutput', type: IdentityProviderType, id: string, name: string, issuer: string, status: SsoIdentityProviderStatus }> };
|
export type GetSsoIdentityProvidersQuery = { __typename?: 'Query', getSSOIdentityProviders: Array<{ __typename?: 'FindAvailableSSOIDPOutput', type: IdentityProviderType, id: string, name: string, issuer: string, status: SsoIdentityProviderStatus }> };
|
||||||
|
|
||||||
export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, analyticsTinybirdJwts?: { __typename?: 'AnalyticsTinybirdJwtMap', getWebhookAnalytics: string, getPageviewsAnalytics: string, getUsersAnalytics: string, getServerlessFunctionDuration: string, getServerlessFunctionSuccessRate: string, getServerlessFunctionErrorCount: string } | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array<SettingsPermissions> | null, objectRecordsPermissions?: Array<PermissionsOnAllObjectRecords> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: FeatureFlagKey, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }> } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> };
|
export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, analyticsTinybirdJwts?: { __typename?: 'AnalyticsTinybirdJwtMap', getWebhookAnalytics: string, getPageviewsAnalytics: string, getUsersAnalytics: string, getServerlessFunctionDuration: string, getServerlessFunctionSuccessRate: string, getServerlessFunctionErrorCount: string } | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array<SettingsPermissions> | null, objectRecordsPermissions?: Array<PermissionsOnAllObjectRecords> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: FeatureFlagKey, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }> } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> };
|
||||||
|
|
||||||
export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }>;
|
export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
@ -2451,7 +2453,7 @@ export type UploadProfilePictureMutation = { __typename?: 'Mutation', uploadProf
|
|||||||
export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>;
|
export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, analyticsTinybirdJwts?: { __typename?: 'AnalyticsTinybirdJwtMap', getWebhookAnalytics: string, getPageviewsAnalytics: string, getUsersAnalytics: string, getServerlessFunctionDuration: string, getServerlessFunctionSuccessRate: string, getServerlessFunctionErrorCount: string } | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array<SettingsPermissions> | null, objectRecordsPermissions?: Array<PermissionsOnAllObjectRecords> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: FeatureFlagKey, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }> } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> } };
|
export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, analyticsTinybirdJwts?: { __typename?: 'AnalyticsTinybirdJwtMap', getWebhookAnalytics: string, getPageviewsAnalytics: string, getUsersAnalytics: string, getServerlessFunctionDuration: string, getServerlessFunctionSuccessRate: string, getServerlessFunctionErrorCount: string } | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array<SettingsPermissions> | null, objectRecordsPermissions?: Array<PermissionsOnAllObjectRecords> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: FeatureFlagKey, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }> } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> } };
|
||||||
|
|
||||||
export type ActivateWorkflowVersionMutationVariables = Exact<{
|
export type ActivateWorkflowVersionMutationVariables = Exact<{
|
||||||
workflowVersionId: Scalars['String'];
|
workflowVersionId: Scalars['String'];
|
||||||
@ -2709,6 +2711,7 @@ export const UserQueryFragmentFragmentDoc = gql`
|
|||||||
firstName
|
firstName
|
||||||
lastName
|
lastName
|
||||||
email
|
email
|
||||||
|
canAccessFullAdminPanel
|
||||||
canImpersonate
|
canImpersonate
|
||||||
supportUserHash
|
supportUserHash
|
||||||
analyticsTinybirdJwts {
|
analyticsTinybirdJwts {
|
||||||
|
|||||||
@ -9,7 +9,9 @@ export const AppRouter = () => {
|
|||||||
|
|
||||||
const currentUser = useRecoilValue(currentUserState);
|
const currentUser = useRecoilValue(currentUserState);
|
||||||
|
|
||||||
const isAdminPageEnabled = currentUser?.canImpersonate;
|
const isAdminPageEnabled =
|
||||||
|
(currentUser?.canImpersonate || currentUser?.canAccessFullAdminPanel) ??
|
||||||
|
false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RouterProvider
|
<RouterProvider
|
||||||
|
|||||||
@ -52,6 +52,7 @@ export const results = {
|
|||||||
firstName: 'firstName',
|
firstName: 'firstName',
|
||||||
lastName: 'lastName',
|
lastName: 'lastName',
|
||||||
email: 'email',
|
email: 'email',
|
||||||
|
canAccessFullAdminPanel: false,
|
||||||
canImpersonate: 'canImpersonate',
|
canImpersonate: 'canImpersonate',
|
||||||
supportUserHash: 'supportUserHash',
|
supportUserHash: 'supportUserHash',
|
||||||
workspaceMember: {
|
workspaceMember: {
|
||||||
|
|||||||
@ -8,6 +8,7 @@ export type CurrentUser = Pick<
|
|||||||
| 'email'
|
| 'email'
|
||||||
| 'supportUserHash'
|
| 'supportUserHash'
|
||||||
| 'analyticsTinybirdJwts'
|
| 'analyticsTinybirdJwts'
|
||||||
|
| 'canAccessFullAdminPanel'
|
||||||
| 'canImpersonate'
|
| 'canImpersonate'
|
||||||
| 'onboardingStatus'
|
| 'onboardingStatus'
|
||||||
| 'userVars'
|
| 'userVars'
|
||||||
|
|||||||
@ -128,6 +128,7 @@ export const queries = {
|
|||||||
firstName
|
firstName
|
||||||
lastName
|
lastName
|
||||||
email
|
email
|
||||||
|
canAccessFullAdminPanel
|
||||||
canImpersonate
|
canImpersonate
|
||||||
supportUserHash
|
supportUserHash
|
||||||
analyticsTinybirdJwts {
|
analyticsTinybirdJwts {
|
||||||
@ -284,6 +285,7 @@ export const responseData = {
|
|||||||
firstName: 'Test',
|
firstName: 'Test',
|
||||||
lastName: 'User',
|
lastName: 'User',
|
||||||
email: 'test@example.com',
|
email: 'test@example.com',
|
||||||
|
canAccessFullAdminPanel: false,
|
||||||
canImpersonate: false,
|
canImpersonate: false,
|
||||||
supportUserHash: null,
|
supportUserHash: null,
|
||||||
analyticsTinybirdJwts: {
|
analyticsTinybirdJwts: {
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { TabList } from '@/ui/layout/tab/components/TabList';
|
|||||||
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
|
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
|
||||||
import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo';
|
import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
import { t } from '@lingui/core/macro';
|
||||||
import { isNonEmptyString } from '@sniptt/guards';
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||||
@ -24,8 +25,8 @@ import {
|
|||||||
} from 'twenty-ui';
|
} from 'twenty-ui';
|
||||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||||
import { useUserLookupAdminPanelMutation } from '~/generated/graphql';
|
import { useUserLookupAdminPanelMutation } from '~/generated/graphql';
|
||||||
import { t } from '@lingui/core/macro';
|
|
||||||
|
|
||||||
|
import { currentUserState } from '@/auth/states/currentUserState';
|
||||||
import packageJson from '../../../../../package.json';
|
import packageJson from '../../../../../package.json';
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
@ -53,6 +54,11 @@ const StyledContentContainer = styled.div`
|
|||||||
padding: ${({ theme }) => theme.spacing(4)} 0;
|
padding: ${({ theme }) => theme.spacing(4)} 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const StyledErrorMessage = styled.div`
|
||||||
|
color: ${({ theme }) => theme.color.red};
|
||||||
|
margin-top: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
export const SettingsAdminGeneral = () => {
|
export const SettingsAdminGeneral = () => {
|
||||||
const [userIdentifier, setUserIdentifier] = useState('');
|
const [userIdentifier, setUserIdentifier] = useState('');
|
||||||
const { enqueueSnackBar } = useSnackBar();
|
const { enqueueSnackBar } = useSnackBar();
|
||||||
@ -67,6 +73,10 @@ export const SettingsAdminGeneral = () => {
|
|||||||
|
|
||||||
const [userLookup] = useUserLookupAdminPanelMutation();
|
const [userLookup] = useUserLookupAdminPanelMutation();
|
||||||
|
|
||||||
|
const currentUser = useRecoilValue(currentUserState);
|
||||||
|
|
||||||
|
const canImpersonate = currentUser?.canImpersonate;
|
||||||
|
|
||||||
const canManageFeatureFlags = useRecoilValue(canManageFeatureFlagsState);
|
const canManageFeatureFlags = useRecoilValue(canManageFeatureFlagsState);
|
||||||
|
|
||||||
const handleSearch = async () => {
|
const handleSearch = async () => {
|
||||||
@ -154,9 +164,16 @@ export const SettingsAdminGeneral = () => {
|
|||||||
accent="blue"
|
accent="blue"
|
||||||
title={t`Search`}
|
title={t`Search`}
|
||||||
onClick={handleSearch}
|
onClick={handleSearch}
|
||||||
disabled={!userIdentifier.trim() || isUserLookupLoading}
|
disabled={
|
||||||
|
!userIdentifier.trim() || isUserLookupLoading || !canImpersonate
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
|
{!canImpersonate && (
|
||||||
|
<StyledErrorMessage>
|
||||||
|
{t`You do not have access to impersonate users.`}
|
||||||
|
</StyledErrorMessage>
|
||||||
|
)}
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
{isDefined(userLookupResult) && (
|
{isDefined(userLookupResult) && (
|
||||||
|
|||||||
@ -21,6 +21,7 @@ const mockCurrentUser = {
|
|||||||
email: 'fake@email.com',
|
email: 'fake@email.com',
|
||||||
supportUserHash: null,
|
supportUserHash: null,
|
||||||
analyticsTinybirdJwts: null,
|
analyticsTinybirdJwts: null,
|
||||||
|
canAccessFullAdminPanel: false,
|
||||||
canImpersonate: false,
|
canImpersonate: false,
|
||||||
onboardingStatus: OnboardingStatus.COMPLETED,
|
onboardingStatus: OnboardingStatus.COMPLETED,
|
||||||
userVars: {},
|
userVars: {},
|
||||||
|
|||||||
@ -56,7 +56,9 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
|
|||||||
const isFunctionSettingsEnabled = false;
|
const isFunctionSettingsEnabled = false;
|
||||||
const isBillingEnabled = billing?.isBillingEnabled ?? false;
|
const isBillingEnabled = billing?.isBillingEnabled ?? false;
|
||||||
const currentUser = useRecoilValue(currentUserState);
|
const currentUser = useRecoilValue(currentUserState);
|
||||||
const isAdminEnabled = currentUser?.canImpersonate ?? false;
|
const isAdminEnabled =
|
||||||
|
(currentUser?.canImpersonate || currentUser?.canAccessFullAdminPanel) ??
|
||||||
|
false;
|
||||||
const labPublicFeatureFlags = useRecoilValue(labPublicFeatureFlagsState);
|
const labPublicFeatureFlags = useRecoilValue(labPublicFeatureFlagsState);
|
||||||
|
|
||||||
const featureFlags = useFeatureFlagsMap();
|
const featureFlags = useFeatureFlagsMap();
|
||||||
|
|||||||
@ -7,6 +7,7 @@ export const USER_QUERY_FRAGMENT = gql`
|
|||||||
firstName
|
firstName
|
||||||
lastName
|
lastName
|
||||||
email
|
email
|
||||||
|
canAccessFullAdminPanel
|
||||||
canImpersonate
|
canImpersonate
|
||||||
supportUserHash
|
supportUserHash
|
||||||
analyticsTinybirdJwts {
|
analyticsTinybirdJwts {
|
||||||
|
|||||||
@ -19,6 +19,7 @@ type MockedUser = Pick<
|
|||||||
| 'email'
|
| 'email'
|
||||||
| 'firstName'
|
| 'firstName'
|
||||||
| 'lastName'
|
| 'lastName'
|
||||||
|
| 'canAccessFullAdminPanel'
|
||||||
| 'canImpersonate'
|
| 'canImpersonate'
|
||||||
| '__typename'
|
| '__typename'
|
||||||
| 'supportUserHash'
|
| 'supportUserHash'
|
||||||
@ -123,6 +124,7 @@ export const mockedUserData: MockedUser = {
|
|||||||
email: 'charles@test.com',
|
email: 'charles@test.com',
|
||||||
firstName: 'Charles',
|
firstName: 'Charles',
|
||||||
lastName: 'Test',
|
lastName: 'Test',
|
||||||
|
canAccessFullAdminPanel: false,
|
||||||
canImpersonate: false,
|
canImpersonate: false,
|
||||||
supportUserHash:
|
supportUserHash:
|
||||||
'a95afad9ff6f0b364e2a3fd3e246a1a852c22b6e55a3ca33745a86c201f9c10d',
|
'a95afad9ff6f0b364e2a3fd3e246a1a852c22b6e55a3ca33745a86c201f9c10d',
|
||||||
@ -148,6 +150,7 @@ export const mockedOnboardingUserData = (
|
|||||||
email: 'workspace-onboarding@test.com',
|
email: 'workspace-onboarding@test.com',
|
||||||
firstName: '',
|
firstName: '',
|
||||||
lastName: '',
|
lastName: '',
|
||||||
|
canAccessFullAdminPanel: false,
|
||||||
canImpersonate: false,
|
canImpersonate: false,
|
||||||
supportUserHash:
|
supportUserHash:
|
||||||
'4fb61d34ed3a4aeda2476d4b308b5162db9e1809b2b8277e6fdc6efc4a609254',
|
'4fb61d34ed3a4aeda2476d4b308b5162db9e1809b2b8277e6fdc6efc4a609254',
|
||||||
|
|||||||
@ -22,6 +22,7 @@ export const seedUsers = async (
|
|||||||
'email',
|
'email',
|
||||||
'passwordHash',
|
'passwordHash',
|
||||||
'canImpersonate',
|
'canImpersonate',
|
||||||
|
'canAccessFullAdminPanel',
|
||||||
])
|
])
|
||||||
.orIgnore()
|
.orIgnore()
|
||||||
.values([
|
.values([
|
||||||
@ -33,6 +34,7 @@ export const seedUsers = async (
|
|||||||
passwordHash:
|
passwordHash:
|
||||||
'$2b$10$66d.6DuQExxnrfI9rMqOg.U1XIYpagr6Lv05uoWLYbYmtK0HDIvS6', // Applecar2025
|
'$2b$10$66d.6DuQExxnrfI9rMqOg.U1XIYpagr6Lv05uoWLYbYmtK0HDIvS6', // Applecar2025
|
||||||
canImpersonate: true,
|
canImpersonate: true,
|
||||||
|
canAccessFullAdminPanel: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: DEV_SEED_USER_IDS.JONY,
|
id: DEV_SEED_USER_IDS.JONY,
|
||||||
@ -42,6 +44,7 @@ export const seedUsers = async (
|
|||||||
passwordHash:
|
passwordHash:
|
||||||
'$2b$10$66d.6DuQExxnrfI9rMqOg.U1XIYpagr6Lv05uoWLYbYmtK0HDIvS6', // Applecar2025
|
'$2b$10$66d.6DuQExxnrfI9rMqOg.U1XIYpagr6Lv05uoWLYbYmtK0HDIvS6', // Applecar2025
|
||||||
canImpersonate: true,
|
canImpersonate: true,
|
||||||
|
canAccessFullAdminPanel: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: DEV_SEED_USER_IDS.PHIL,
|
id: DEV_SEED_USER_IDS.PHIL,
|
||||||
@ -51,6 +54,7 @@ export const seedUsers = async (
|
|||||||
passwordHash:
|
passwordHash:
|
||||||
'$2b$10$66d.6DuQExxnrfI9rMqOg.U1XIYpagr6Lv05uoWLYbYmtK0HDIvS6', // Applecar2025
|
'$2b$10$66d.6DuQExxnrfI9rMqOg.U1XIYpagr6Lv05uoWLYbYmtK0HDIvS6', // Applecar2025
|
||||||
canImpersonate: true,
|
canImpersonate: true,
|
||||||
|
canAccessFullAdminPanel: true,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
.execute();
|
.execute();
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
export class AddAccessToFullAdminAndWorkspaceVersion1740415309924
|
||||||
|
implements MigrationInterface
|
||||||
|
{
|
||||||
|
name = 'AddAccessToFullAdminAndWorkspaceVersion1740415309924';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "core"."user" ADD "canAccessFullAdminPanel" boolean NOT NULL DEFAULT false`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "core"."workspace" ADD "version" character varying`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "core"."workspace" DROP COLUMN "version"`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "core"."user" DROP COLUMN "canAccessFullAdminPanel"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -207,6 +207,7 @@ export class SignInUpService {
|
|||||||
const userToCreate = this.userRepository.create({
|
const userToCreate = this.userRepository.create({
|
||||||
...newUser,
|
...newUser,
|
||||||
defaultAvatarUrl: imagePath,
|
defaultAvatarUrl: imagePath,
|
||||||
|
canAccessFullAdminPanel: false,
|
||||||
canImpersonate: false,
|
canImpersonate: false,
|
||||||
} as Partial<User>);
|
} as Partial<User>);
|
||||||
|
|
||||||
@ -289,6 +290,7 @@ export class SignInUpService {
|
|||||||
const user: PartialUserWithPicture = {
|
const user: PartialUserWithPicture = {
|
||||||
...partialUserWithPicture,
|
...partialUserWithPicture,
|
||||||
canImpersonate: false,
|
canImpersonate: false,
|
||||||
|
canAccessFullAdminPanel: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!user.email) {
|
if (!user.email) {
|
||||||
@ -303,6 +305,7 @@ export class SignInUpService {
|
|||||||
|
|
||||||
// if the workspace doesn't exist it means it's the first user of the workspace
|
// if the workspace doesn't exist it means it's the first user of the workspace
|
||||||
user.canImpersonate = true;
|
user.canImpersonate = true;
|
||||||
|
user.canAccessFullAdminPanel = true;
|
||||||
|
|
||||||
// let the creation of the first workspace
|
// let the creation of the first workspace
|
||||||
if (workspacesCount > 0) {
|
if (workspacesCount > 0) {
|
||||||
|
|||||||
@ -69,6 +69,10 @@ export class User {
|
|||||||
@Column({ default: false })
|
@Column({ default: false })
|
||||||
canImpersonate: boolean;
|
canImpersonate: boolean;
|
||||||
|
|
||||||
|
@Field()
|
||||||
|
@Column({ default: false })
|
||||||
|
canAccessFullAdminPanel: boolean;
|
||||||
|
|
||||||
@Field()
|
@Field()
|
||||||
@CreateDateColumn({ type: 'timestamptz' })
|
@CreateDateColumn({ type: 'timestamptz' })
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
|||||||
@ -15,12 +15,12 @@ import {
|
|||||||
|
|
||||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||||
import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity';
|
import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity';
|
||||||
|
import { ApprovedAccessDomain } from 'src/engine/core-modules/approved-access-domain/approved-access-domain.entity';
|
||||||
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||||
import { KeyValuePair } from 'src/engine/core-modules/key-value-pair/key-value-pair.entity';
|
import { KeyValuePair } from 'src/engine/core-modules/key-value-pair/key-value-pair.entity';
|
||||||
import { PostgresCredentials } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.entity';
|
import { PostgresCredentials } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.entity';
|
||||||
import { WorkspaceSSOIdentityProvider } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
|
import { WorkspaceSSOIdentityProvider } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
|
||||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||||
import { ApprovedAccessDomain } from 'src/engine/core-modules/approved-access-domain/approved-access-domain.entity';
|
|
||||||
|
|
||||||
registerEnumType(WorkspaceActivationStatus, {
|
registerEnumType(WorkspaceActivationStatus, {
|
||||||
name: 'WorkspaceActivationStatus',
|
name: 'WorkspaceActivationStatus',
|
||||||
@ -150,4 +150,8 @@ export class Workspace {
|
|||||||
@Field()
|
@Field()
|
||||||
@Column({ default: false })
|
@Column({ default: false })
|
||||||
isCustomDomainEnabled: boolean;
|
isCustomDomainEnabled: boolean;
|
||||||
|
|
||||||
|
@Field(() => String, { nullable: true })
|
||||||
|
@Column({ type: 'varchar', nullable: true })
|
||||||
|
version: string | null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user