feat: introduce app configs with shared auth clients (#1213)
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import createAppAuthClient from './mutations/create-app-auth-client.ee';
|
||||
import createAppConfig from './mutations/create-app-config.ee';
|
||||
import createConnection from './mutations/create-connection';
|
||||
import createFlow from './mutations/create-flow';
|
||||
import createRole from './mutations/create-role.ee';
|
||||
@@ -17,6 +19,8 @@ import login from './mutations/login';
|
||||
import registerUser from './mutations/register-user.ee';
|
||||
import resetConnection from './mutations/reset-connection';
|
||||
import resetPassword from './mutations/reset-password.ee';
|
||||
import updateAppAuthClient from './mutations/update-app-auth-client.ee';
|
||||
import updateAppConfig from './mutations/update-app-config.ee';
|
||||
import updateConfig from './mutations/update-config.ee';
|
||||
import updateConnection from './mutations/update-connection';
|
||||
import updateCurrentUser from './mutations/update-current-user';
|
||||
@@ -30,6 +34,8 @@ import upsertSamlAuthProvidersRoleMappings from './mutations/upsert-saml-auth-pr
|
||||
import verifyConnection from './mutations/verify-connection';
|
||||
|
||||
const mutationResolvers = {
|
||||
createAppAuthClient,
|
||||
createAppConfig,
|
||||
createConnection,
|
||||
createFlow,
|
||||
createRole,
|
||||
@@ -49,6 +55,8 @@ const mutationResolvers = {
|
||||
registerUser,
|
||||
resetConnection,
|
||||
resetPassword,
|
||||
updateAppAuthClient,
|
||||
updateAppConfig,
|
||||
updateConfig,
|
||||
updateConnection,
|
||||
updateCurrentUser,
|
||||
|
@@ -0,0 +1,35 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
appConfigId: string;
|
||||
name: string;
|
||||
formattedAuthDefaults?: IJSONObject;
|
||||
active?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const createAppAuthClient = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const appConfig = await AppConfig
|
||||
.query()
|
||||
.findById(params.input.appConfigId)
|
||||
.throwIfNotFound();
|
||||
|
||||
const appAuthClient = await appConfig
|
||||
.$relatedQuery('appAuthClients')
|
||||
.insert(
|
||||
params.input
|
||||
);
|
||||
|
||||
return appAuthClient;
|
||||
};
|
||||
|
||||
export default createAppAuthClient;
|
@@ -0,0 +1,36 @@
|
||||
import App from '../../models/app';
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
key: string;
|
||||
allowCustomConnection?: boolean;
|
||||
shared?: boolean;
|
||||
disabled?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const createAppConfig = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const key = params.input.key;
|
||||
|
||||
const app = await App.findOneByKey(key);
|
||||
|
||||
if (!app) throw new Error('The app cannot be found!');
|
||||
|
||||
const appConfig = await AppConfig
|
||||
.query()
|
||||
.insert(
|
||||
params.input
|
||||
);
|
||||
|
||||
return appConfig;
|
||||
};
|
||||
|
||||
export default createAppConfig;
|
@@ -1,13 +1,16 @@
|
||||
import App from '../../models/app';
|
||||
import Context from '../../types/express/context';
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import App from '../../models/app';
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
key: string;
|
||||
appAuthClientId: string;
|
||||
formattedData: IJSONObject;
|
||||
};
|
||||
};
|
||||
|
||||
const createConnection = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
@@ -15,13 +18,42 @@ const createConnection = async (
|
||||
) => {
|
||||
context.currentUser.can('create', 'Connection');
|
||||
|
||||
await App.findOneByKey(params.input.key);
|
||||
const { key, appAuthClientId } = params.input;
|
||||
|
||||
return await context.currentUser.$relatedQuery('connections').insert({
|
||||
key: params.input.key,
|
||||
formattedData: params.input.formattedData,
|
||||
verified: false,
|
||||
});
|
||||
const app = await App.findOneByKey(key);
|
||||
|
||||
const appConfig = await AppConfig.query().findOne({ key });
|
||||
|
||||
let formattedData = params.input.formattedData;
|
||||
if (appConfig) {
|
||||
if (appConfig.disabled) throw new Error('This application has been disabled for new connections!');
|
||||
|
||||
if (!appConfig.allowCustomConnection && formattedData) throw new Error(`Custom connections cannot be created for ${app.name}!`);
|
||||
|
||||
if (appConfig.shared && !formattedData) {
|
||||
const authClient = await appConfig
|
||||
.$relatedQuery('appAuthClients')
|
||||
.findById(appAuthClientId)
|
||||
.where({
|
||||
active: true
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
formattedData = authClient.formattedAuthDefaults;
|
||||
}
|
||||
}
|
||||
|
||||
const createdConnection = await context
|
||||
.currentUser
|
||||
.$relatedQuery('connections')
|
||||
.insert({
|
||||
key,
|
||||
appAuthClientId,
|
||||
formattedData,
|
||||
verified: false,
|
||||
});
|
||||
|
||||
return createdConnection;
|
||||
};
|
||||
|
||||
export default createConnection;
|
||||
|
@@ -0,0 +1,28 @@
|
||||
import Context from '../../types/express/context';
|
||||
import AppAuthClient from '../../models/app-auth-client';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const deleteAppAuthClient = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
context.currentUser.can('delete', 'App');
|
||||
|
||||
await AppAuthClient
|
||||
.query()
|
||||
.delete()
|
||||
.findOne({
|
||||
id: params.input.id,
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
export default deleteAppAuthClient;
|
@@ -0,0 +1,38 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import AppAuthClient from '../../models/app-auth-client';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
name: string;
|
||||
formattedAuthDefaults?: IJSONObject;
|
||||
active?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const updateAppAuthClient = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const {
|
||||
id,
|
||||
...appAuthClientData
|
||||
} = params.input;
|
||||
|
||||
const appAuthClient = await AppAuthClient
|
||||
.query()
|
||||
.findById(id)
|
||||
.throwIfNotFound();
|
||||
|
||||
await appAuthClient
|
||||
.$query()
|
||||
.patch(appAuthClientData);
|
||||
|
||||
return appAuthClient;
|
||||
};
|
||||
|
||||
export default updateAppAuthClient;
|
@@ -0,0 +1,39 @@
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
allowCustomConnection?: boolean;
|
||||
shared?: boolean;
|
||||
disabled?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const updateAppConfig = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const {
|
||||
id,
|
||||
...appConfigToUpdate
|
||||
} = params.input;
|
||||
|
||||
const appConfig = await AppConfig
|
||||
.query()
|
||||
.findById(id)
|
||||
.throwIfNotFound();
|
||||
|
||||
await appConfig
|
||||
.$query()
|
||||
.patch(
|
||||
appConfigToUpdate
|
||||
);
|
||||
|
||||
return appConfig;
|
||||
};
|
||||
|
||||
export default updateAppConfig;
|
@@ -1,10 +1,12 @@
|
||||
import Context from '../../types/express/context';
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import Context from '../../types/express/context';
|
||||
import AppAuthClient from '../../models/app-auth-client';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
formattedData: IJSONObject;
|
||||
formattedData?: IJSONObject;
|
||||
appAuthClientId?: string;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -22,10 +24,21 @@ const updateConnection = async (
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
let formattedData = params.input.formattedData;
|
||||
|
||||
if (params.input.appAuthClientId) {
|
||||
const appAuthClient = await AppAuthClient
|
||||
.query()
|
||||
.findById(params.input.appAuthClientId)
|
||||
.throwIfNotFound();
|
||||
|
||||
formattedData = appAuthClient.formattedAuthDefaults;
|
||||
}
|
||||
|
||||
connection = await connection.$query().patchAndFetch({
|
||||
formattedData: {
|
||||
...connection.formattedData,
|
||||
...params.input.formattedData,
|
||||
...formattedData,
|
||||
},
|
||||
});
|
||||
|
||||
|
@@ -0,0 +1,30 @@
|
||||
import AppAuthClient from '../../models/app-auth-client';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
const getAppAuthClient = async (_parent: unknown, params: Params, context: Context) => {
|
||||
let canSeeAllClients = false;
|
||||
try {
|
||||
context.currentUser.can('read', 'App');
|
||||
|
||||
canSeeAllClients = true;
|
||||
} catch {
|
||||
// void
|
||||
}
|
||||
|
||||
const appAuthClient = AppAuthClient
|
||||
.query()
|
||||
.findById(params.id)
|
||||
.throwIfNotFound();
|
||||
|
||||
if (!canSeeAllClients) {
|
||||
appAuthClient.where({ active: true });
|
||||
}
|
||||
|
||||
return await appAuthClient;
|
||||
};
|
||||
|
||||
export default getAppAuthClient;
|
@@ -0,0 +1,40 @@
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
appKey: string;
|
||||
active: boolean;
|
||||
};
|
||||
|
||||
const getAppAuthClients = async (_parent: unknown, params: Params, context: Context) => {
|
||||
let canSeeAllClients = false;
|
||||
try {
|
||||
context.currentUser.can('read', 'App');
|
||||
|
||||
canSeeAllClients = true;
|
||||
} catch {
|
||||
// void
|
||||
}
|
||||
|
||||
const appConfig = await AppConfig
|
||||
.query()
|
||||
.findOne({
|
||||
key: params.appKey,
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
const appAuthClients = appConfig
|
||||
.$relatedQuery('appAuthClients')
|
||||
.where({ active: params.active })
|
||||
.skipUndefined();
|
||||
|
||||
if (!canSeeAllClients) {
|
||||
appAuthClients.where({
|
||||
active: true
|
||||
})
|
||||
}
|
||||
|
||||
return await appAuthClients;
|
||||
};
|
||||
|
||||
export default getAppAuthClients;
|
23
packages/backend/src/graphql/queries/get-app-config.ee.ts
Normal file
23
packages/backend/src/graphql/queries/get-app-config.ee.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
key: string;
|
||||
};
|
||||
|
||||
const getAppConfig = async (_parent: unknown, params: Params, context: Context) => {
|
||||
context.currentUser.can('create', 'Connection');
|
||||
|
||||
const appConfig = await AppConfig
|
||||
.query()
|
||||
.withGraphFetched({
|
||||
appAuthClients: true
|
||||
})
|
||||
.findOne({
|
||||
key: params.key
|
||||
});
|
||||
|
||||
return appConfig;
|
||||
};
|
||||
|
||||
export default getAppConfig;
|
@@ -19,6 +19,10 @@ const getApp = async (_parent: unknown, params: Params, context: Context) => {
|
||||
const connections = await connectionBaseQuery
|
||||
.clone()
|
||||
.select('connections.*')
|
||||
.withGraphFetched({
|
||||
appConfig: true,
|
||||
appAuthClient: true
|
||||
})
|
||||
.fullOuterJoinRelated('steps')
|
||||
.where({
|
||||
'connections.key': params.key,
|
||||
|
@@ -1,9 +1,12 @@
|
||||
import getApp from './queries/get-app';
|
||||
import getAppAuthClient from './queries/get-app-auth-client.ee';
|
||||
import getAppAuthClients from './queries/get-app-auth-clients.ee';
|
||||
import getAppConfig from './queries/get-app-config.ee';
|
||||
import getApps from './queries/get-apps';
|
||||
import getAutomatischInfo from './queries/get-automatisch-info';
|
||||
import getBillingAndUsage from './queries/get-billing-and-usage.ee';
|
||||
import getConnectedApps from './queries/get-connected-apps';
|
||||
import getConfig from './queries/get-config.ee';
|
||||
import getConnectedApps from './queries/get-connected-apps';
|
||||
import getCurrentUser from './queries/get-current-user';
|
||||
import getDynamicData from './queries/get-dynamic-data';
|
||||
import getDynamicFields from './queries/get-dynamic-fields';
|
||||
@@ -30,6 +33,9 @@ import testConnection from './queries/test-connection';
|
||||
|
||||
const queryResolvers = {
|
||||
getApp,
|
||||
getAppAuthClient,
|
||||
getAppAuthClients,
|
||||
getAppConfig,
|
||||
getApps,
|
||||
getAutomatischInfo,
|
||||
getBillingAndUsage,
|
||||
|
@@ -5,6 +5,9 @@ type Query {
|
||||
onlyWithActions: Boolean
|
||||
): [App]
|
||||
getApp(key: String!): App
|
||||
getAppConfig(key: String!): AppConfig
|
||||
getAppAuthClient(id: String!): AppAuthClient
|
||||
getAppAuthClients(appKey: String!, active: Boolean): [AppAuthClient]
|
||||
getConnectedApps(name: String): [App]
|
||||
testConnection(id: String!): Connection
|
||||
getFlow(id: String!): Flow
|
||||
@@ -49,10 +52,12 @@ type Query {
|
||||
getUser(id: String!): User
|
||||
getUsers(limit: Int!, offset: Int!): UserConnection
|
||||
healthcheck: AppHealth
|
||||
listSamlAuthProviders: [ListSamlAuthProviders]
|
||||
listSamlAuthProviders: [ListSamlAuthProvider]
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createAppConfig(input: CreateAppConfigInput): AppConfig
|
||||
createAppAuthClient(input: CreateAppAuthClientInput): AppAuthClient
|
||||
createConnection(input: CreateConnectionInput): Connection
|
||||
createFlow(input: CreateFlowInput): Flow
|
||||
createRole(input: CreateRoleInput): Role
|
||||
@@ -72,6 +77,8 @@ type Mutation {
|
||||
registerUser(input: RegisterUserInput): User
|
||||
resetConnection(input: ResetConnectionInput): Connection
|
||||
resetPassword(input: ResetPasswordInput): Boolean
|
||||
updateAppAuthClient(input: UpdateAppAuthClientInput): AppAuthClient
|
||||
updateAppConfig(input: UpdateAppConfigInput): AppConfig
|
||||
updateConfig(input: JSONObject): JSONObject
|
||||
updateConnection(input: UpdateConnectionInput): Connection
|
||||
updateCurrentUser(input: UpdateCurrentUserInput): User
|
||||
@@ -162,6 +169,16 @@ type SubstepArgumentAdditionalFieldsArgument {
|
||||
value: String
|
||||
}
|
||||
|
||||
type AppConfig {
|
||||
id: String
|
||||
key: String
|
||||
allowCustomConnection: Boolean
|
||||
canConnect: Boolean
|
||||
canCustomConnect: Boolean
|
||||
shared: Boolean
|
||||
disabled: Boolean
|
||||
}
|
||||
|
||||
type App {
|
||||
name: String
|
||||
key: String
|
||||
@@ -181,7 +198,9 @@ type App {
|
||||
type AppAuth {
|
||||
fields: [Field]
|
||||
authenticationSteps: [AuthenticationStep]
|
||||
sharedAuthenticationSteps: [AuthenticationStep]
|
||||
reconnectionSteps: [ReconnectionStep]
|
||||
sharedReconnectionSteps: [ReconnectionStep]
|
||||
}
|
||||
|
||||
enum ArgumentEnumType {
|
||||
@@ -219,6 +238,8 @@ type AuthLink {
|
||||
type Connection {
|
||||
id: String
|
||||
key: String
|
||||
reconnectable: Boolean
|
||||
appAuthClientId: String
|
||||
formattedData: ConnectionData
|
||||
verified: Boolean
|
||||
app: App
|
||||
@@ -328,7 +349,8 @@ type UserEdge {
|
||||
|
||||
input CreateConnectionInput {
|
||||
key: String!
|
||||
formattedData: JSONObject!
|
||||
appAuthClientId: String
|
||||
formattedData: JSONObject
|
||||
}
|
||||
|
||||
input GenerateAuthUrlInput {
|
||||
@@ -337,7 +359,8 @@ input GenerateAuthUrlInput {
|
||||
|
||||
input UpdateConnectionInput {
|
||||
id: String!
|
||||
formattedData: JSONObject!
|
||||
formattedData: JSONObject
|
||||
appAuthClientId: String
|
||||
}
|
||||
|
||||
input ResetConnectionInput {
|
||||
@@ -690,7 +713,7 @@ type PaymentPlan {
|
||||
productId: String
|
||||
}
|
||||
|
||||
type ListSamlAuthProviders {
|
||||
type ListSamlAuthProvider {
|
||||
id: String
|
||||
name: String
|
||||
issuer: String
|
||||
@@ -725,6 +748,41 @@ type Subject {
|
||||
key: String
|
||||
}
|
||||
|
||||
input CreateAppConfigInput {
|
||||
key: String
|
||||
allowCustomConnection: Boolean
|
||||
shared: Boolean
|
||||
disabled: Boolean
|
||||
}
|
||||
|
||||
input UpdateAppConfigInput {
|
||||
id: String
|
||||
allowCustomConnection: Boolean
|
||||
shared: Boolean
|
||||
disabled: Boolean
|
||||
}
|
||||
|
||||
type AppAuthClient {
|
||||
id: String
|
||||
appConfigId: String
|
||||
name: String
|
||||
active: Boolean
|
||||
}
|
||||
|
||||
input CreateAppAuthClientInput {
|
||||
appConfigId: String
|
||||
name: String
|
||||
formattedAuthDefaults: JSONObject
|
||||
active: Boolean
|
||||
}
|
||||
|
||||
input UpdateAppAuthClientInput {
|
||||
id: String
|
||||
name: String
|
||||
formattedAuthDefaults: JSONObject
|
||||
active: Boolean
|
||||
}
|
||||
|
||||
schema {
|
||||
query: Query
|
||||
mutation: Mutation
|
||||
|
Reference in New Issue
Block a user