-
Notifications
You must be signed in to change notification settings - Fork 336
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Matt Krick <[email protected]>
- Loading branch information
Showing
11 changed files
with
209 additions
and
59 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import graphql from 'babel-plugin-relay/macro' | ||
import {useMutation} from 'react-relay' | ||
import {useUploadIdPMetadataMutation as TuseUploadIdPMetadataMutation} from '../__generated__/useUploadIdPMetadataMutation.graphql' | ||
|
||
const mutation = graphql` | ||
mutation useUploadIdPMetadataMutation($file: File!, $orgId: ID!) { | ||
uploadIdPMetadata(file: $file, orgId: $orgId) { | ||
... on ErrorPayload { | ||
error { | ||
message | ||
} | ||
} | ||
... on UploadIdPMetadataSuccess { | ||
url | ||
} | ||
} | ||
} | ||
` | ||
interface TTuseUploadIdPMetadataMutation extends Omit<TuseUploadIdPMetadataMutation, 'variables'> { | ||
variables: Omit<TuseUploadIdPMetadataMutation['variables'], 'file'> | ||
uploadables: {file: File} | ||
} | ||
|
||
export const useUploadIdPMetadata = () => { | ||
const [commit, submitting] = useMutation<TTuseUploadIdPMetadataMutation>(mutation) | ||
type Execute = ( | ||
config: Parameters<typeof commit>[0] & {uploadables: {file: File}} | ||
) => ReturnType<typeof commit> | ||
|
||
const execute: Execute = (config) => { | ||
const {variables} = config | ||
const {orgId} = variables | ||
return commit({ | ||
updater: (store) => { | ||
const org = store.get(orgId) | ||
org?.setValue(orgId, 'id') | ||
}, | ||
// allow components to override default handlers | ||
...config | ||
}) | ||
} | ||
return [execute, submitting] as const | ||
} |
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
24 changes: 24 additions & 0 deletions
24
packages/server/graphql/public/mutations/uploadIdPMetadata.ts
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,24 @@ | ||
import getFileStoreManager from '../../../fileStorage/getFileStoreManager' | ||
import {MutationResolvers} from '../resolverTypes' | ||
|
||
const uploadIdPMetadata: MutationResolvers['uploadIdPMetadata'] = async (_, {file, orgId}) => { | ||
// VALIDATION | ||
const {contentType, buffer: jsonBuffer} = file | ||
const buffer = Buffer.from(jsonBuffer.data) | ||
if (!contentType || !contentType.includes('xml')) { | ||
return {error: {message: 'file must be XML'}} | ||
} | ||
if (buffer.byteLength > 1000000) { | ||
return {error: {message: 'file must be less than 1MB'}} | ||
} | ||
if (buffer.byteLength <= 1) { | ||
return {error: {message: 'file must be larger than 1 byte'}} | ||
} | ||
|
||
// RESOLUTION | ||
const manager = getFileStoreManager() | ||
const url = await manager.putOrgIdPMetadata(buffer, orgId) | ||
return {url} | ||
} | ||
|
||
export default uploadIdPMetadata |
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,9 @@ | ||
export const getResolverDotPath = ( | ||
dotPath: ResolverDotPath, | ||
source: Record<string, any>, | ||
args: Record<string, any> | ||
) => { | ||
return dotPath.split('.').reduce((val: any, key) => val?.[key], {source, args}) | ||
} | ||
|
||
export type ResolverDotPath = `source.${string}` | `args.${string}` |
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,25 +1,16 @@ | ||
import {rule} from 'graphql-shield' | ||
import {GQLContext} from '../../graphql' | ||
import {TierEnum} from '../resolverTypes' | ||
import {ResolverDotPath, getResolverDotPath} from './getResolverDotPath' | ||
|
||
const resolve = async (requiredTier: TierEnum, orgId: string, {dataLoader}: GQLContext) => { | ||
const organization = await dataLoader.get('organizations').load(orgId) | ||
if (!organization) return new Error('Organization not found') | ||
const {tier} = organization | ||
if (tier !== requiredTier) return new Error(`Organization is not ${requiredTier}`) | ||
return true | ||
} | ||
|
||
export const isOrgTierSource = (requiredTier: TierEnum) => | ||
rule(`isOrgTierSource-${requiredTier}`, {cache: 'strict'})( | ||
async ({id: orgId}, _args, context: GQLContext) => { | ||
return resolve(requiredTier, orgId, context) | ||
} | ||
) | ||
|
||
export const isOrgTier = (requiredTier: TierEnum) => | ||
rule(`isOrgTier-${requiredTier}`, {cache: 'strict'})( | ||
async (_source, {orgId}, context: GQLContext) => { | ||
return resolve(requiredTier, orgId, context) | ||
export const isOrgTier = (orgIdDotPath: ResolverDotPath, requiredTier: TierEnum) => | ||
rule(`isViewerOnOrg-${orgIdDotPath}-${requiredTier}`, {cache: 'strict'})( | ||
async (source, args, {dataLoader}: GQLContext) => { | ||
const orgId = getResolverDotPath(orgIdDotPath, source, args) | ||
const organization = await dataLoader.get('organizations').load(orgId) | ||
if (!organization) return new Error('Organization not found') | ||
const {tier} = organization | ||
if (tier !== requiredTier) return new Error(`Organization is not ${requiredTier}`) | ||
return true | ||
} | ||
) |
43 changes: 16 additions & 27 deletions
43
packages/server/graphql/public/rules/isViewerBillingLeader.ts
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,31 +1,20 @@ | ||
import {rule} from 'graphql-shield' | ||
import {getUserId} from '../../../utils/authorization' | ||
import {GQLContext} from '../../graphql' | ||
import {ResolverDotPath, getResolverDotPath} from './getResolverDotPath' | ||
|
||
const resolve = async (orgId: string, {authToken, dataLoader}: GQLContext) => { | ||
const viewerId = getUserId(authToken) | ||
const organizationUser = await dataLoader | ||
.get('organizationUsersByUserIdOrgId') | ||
.load({orgId, userId: viewerId}) | ||
if (!organizationUser) return new Error('Organization User not found') | ||
const {role} = organizationUser | ||
if (role !== 'BILLING_LEADER' && role !== 'ORG_ADMIN') | ||
return new Error('User is not billing leader') | ||
return true | ||
} | ||
|
||
export const isViewerBillingLeader = rule({cache: 'strict'})(async ( | ||
_source, | ||
{orgId}, | ||
context: GQLContext | ||
) => { | ||
return resolve(orgId, context) | ||
}) | ||
|
||
export const isViewerBillingLeaderSource = rule({cache: 'strict'})(async ( | ||
{id: orgId}, | ||
_args, | ||
context: GQLContext | ||
) => { | ||
return resolve(orgId, context) | ||
}) | ||
export const isViewerBillingLeader = (orgIdDotPath: ResolverDotPath) => | ||
rule(`isViewerBillingLeader-${orgIdDotPath}`, {cache: 'strict'})( | ||
async (source, args, {authToken, dataLoader}: GQLContext) => { | ||
const orgId = getResolverDotPath(orgIdDotPath, source, args) | ||
const viewerId = getUserId(authToken) | ||
const organizationUser = await dataLoader | ||
.get('organizationUsersByUserIdOrgId') | ||
.load({orgId, userId: viewerId}) | ||
if (!organizationUser) return new Error('Organization User not found') | ||
const {role} = organizationUser | ||
if (role !== 'BILLING_LEADER' && role !== 'ORG_ADMIN') | ||
return new Error('User is not billing leader') | ||
return true | ||
} | ||
) |
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,17 @@ | ||
import {rule} from 'graphql-shield' | ||
import {getUserId} from '../../../utils/authorization' | ||
import {GQLContext} from '../../graphql' | ||
import {ResolverDotPath, getResolverDotPath} from './getResolverDotPath' | ||
|
||
export const isViewerOnOrg = (orgIdDotPath: ResolverDotPath) => | ||
rule(`isViewerOnOrg-${orgIdDotPath}`, {cache: 'strict'})( | ||
async (source, args, {authToken, dataLoader}: GQLContext) => { | ||
const orgId = getResolverDotPath(orgIdDotPath, source, args) | ||
const viewerId = getUserId(authToken) | ||
const organizationUser = await dataLoader | ||
.get('organizationUsersByUserIdOrgId') | ||
.load({orgId, userId: viewerId}) | ||
if (!organizationUser) return new Error('Viewer is not on Organization') | ||
return true | ||
} | ||
) |
22 changes: 22 additions & 0 deletions
22
packages/server/graphql/public/typeDefs/uploadIdPMetadata.graphql
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,22 @@ | ||
extend type Mutation { | ||
""" | ||
Upload the IdP Metadata file for an org for those who cannot self-host the file | ||
""" | ||
uploadIdPMetadata( | ||
""" | ||
the XML Metadata file for the IdP | ||
""" | ||
file: File! | ||
|
||
""" | ||
The orgId to upload the IdP Metadata for | ||
""" | ||
orgId: ID! | ||
): UploadIdPMetadataPayload! | ||
} | ||
|
||
union UploadIdPMetadataPayload = ErrorPayload | UploadIdPMetadataSuccess | ||
|
||
type UploadIdPMetadataSuccess { | ||
url: String! | ||
} |