0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

refactor(core,schemas,cli): save cloud service m2m app credentials (#4109)

This commit is contained in:
Darcy Ye 2023-07-04 18:14:35 +08:00 committed by GitHub
parent 759adf0f18
commit 61c49845a6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 147 additions and 6 deletions

View file

@ -3,6 +3,7 @@ import path from 'node:path';
import {
createDefaultAdminConsoleConfig,
createCloudConnectionConfig,
defaultTenantId,
adminTenantId,
defaultManagementApi,
@ -165,6 +166,16 @@ export const seedTables = async (
await Promise.all([
connection.query(insertInto(createDefaultAdminConsoleConfig(defaultTenantId), 'logto_configs')),
connection.query(
insertInto(
createCloudConnectionConfig(
defaultTenantId,
defaultTenantApplication.id,
defaultTenantApplication.secret
),
'logto_configs'
)
),
connection.query(
insertInto(createDefaultSignInExperience(defaultTenantId, isCloud), 'sign_in_experiences')
),

View file

@ -69,7 +69,9 @@ export class EnvSet {
this.#pool = pool;
const { getOidcConfigs } = createLogtoConfigLibrary(createLogtoConfigQueries(pool));
const { getOidcConfigs } = createLogtoConfigLibrary({
logtoConfigs: createLogtoConfigQueries(pool),
});
const oidcConfigs = await getOidcConfigs();
const endpoint = getTenantEndpoint(this.tenantId, EnvSet.values);

View file

@ -1,12 +1,19 @@
import type { LogtoOidcConfigType } from '@logto/schemas';
import { logtoOidcConfigGuard, LogtoOidcConfigKey } from '@logto/schemas';
import {
cloudApiIndicator,
cloudConnectionDataGuard,
logtoOidcConfigGuard,
LogtoOidcConfigKey,
} from '@logto/schemas';
import chalk from 'chalk';
import { z, ZodError } from 'zod';
import type Queries from '#src/tenants/Queries.js';
import { consoleLog } from '#src/utils/console.js';
export const createLogtoConfigLibrary = ({ getRowsByKeys }: Queries['logtoConfigs']) => {
export const createLogtoConfigLibrary = ({
logtoConfigs: { getRowsByKeys, getCloudConnectionData: queryCloudConnectionData },
}: Pick<Queries, 'logtoConfigs'>) => {
const getOidcConfigs = async (): Promise<LogtoOidcConfigType> => {
try {
const { rows } = await getRowsByKeys(Object.values(LogtoOidcConfigKey));
@ -35,5 +42,20 @@ export const createLogtoConfigLibrary = ({ getRowsByKeys }: Queries['logtoConfig
}
};
return { getOidcConfigs };
const getCloudConnectionData = async () => {
const { value } = await queryCloudConnectionData();
const result = cloudConnectionDataGuard.safeParse(value);
if (!result.success) {
return;
}
return {
appId: result.data.appId,
appSecret: result.data.appSecret,
resource: cloudApiIndicator,
};
};
return { getOidcConfigs, getCloudConnectionData };
};

View file

@ -21,11 +21,17 @@ export const createLogtoConfigQueries = (pool: CommonQueryMethods) => {
returning ${fields.value}
`);
const getCloudConnectionData = async () =>
pool.one<Record<string, unknown>>(sql`
select ${fields.value} from ${table}
where ${fields.key} = ${LogtoTenantConfigKey.CloudConnection}
`);
const getRowsByKeys = async (keys: LogtoConfigKey[]) =>
pool.query<LogtoConfig>(sql`
select ${sql.join([fields.key, fields.value], sql`,`)} from ${table}
where ${fields.key} in (${sql.join(keys, sql`,`)})
`);
return { getAdminConsoleConfig, updateAdminConsoleConfig, getRowsByKeys };
return { getAdminConsoleConfig, updateAdminConsoleConfig, getCloudConnectionData, getRowsByKeys };
};

View file

@ -2,6 +2,7 @@ import { createApplicationLibrary } from '#src/libraries/application.js';
import type { ConnectorLibrary } from '#src/libraries/connector.js';
import { createDomainLibrary } from '#src/libraries/domain.js';
import { createHookLibrary } from '#src/libraries/hook/index.js';
import { createLogtoConfigLibrary } from '#src/libraries/logto-config.js';
import { createPasscodeLibrary } from '#src/libraries/passcode.js';
import { createPhraseLibrary } from '#src/libraries/phrase.js';
import { createResourceLibrary } from '#src/libraries/resource.js';
@ -23,6 +24,7 @@ export default class Libraries {
applications = createApplicationLibrary(this.queries);
verificationStatuses = createVerificationStatusLibrary(this.queries);
domains = createDomainLibrary(this.queries);
logtoConfigs = createLogtoConfigLibrary(this.queries);
constructor(
public readonly tenantId: string,

View file

@ -0,0 +1,13 @@
import { appendPath } from '@silverhand/essentials';
import { EnvSet } from '#src/env-set/index.js';
/** Will use this method in upcoming changes. */
// eslint-disable-next-line import/no-unused-modules
export const getCloudConnectionEndpoints = async () => {
const { cloudUrlSet, adminUrlSet } = EnvSet.values;
return {
tokenEndpoint: appendPath(adminUrlSet.endpoint, 'oidc/token').toString(),
endpoint: appendPath(cloudUrlSet.endpoint, 'api').toString(),
};
};

View file

@ -0,0 +1,85 @@
import { sql } from 'slonik';
import type { AlterationScript } from '../lib/types/alteration.js';
const adminTenantId = 'admin';
const cloudServiceApplicationName = 'Cloud Service';
const cloudConnectionResourceIndicator = 'https://cloud.logto.io/api';
enum ApplicationType {
Native = 'Native',
SPA = 'SPA',
Traditional = 'Traditional',
MachineToMachine = 'MachineToMachine',
}
const cloudConnectionConfigKey = 'cloudConnection';
type Application = {
tenantId: string;
id: string;
name: string;
secret: string;
description: string;
type: ApplicationType;
oidcClientMetadata: unknown;
customClientMetadata: {
tenantId: string;
};
createdAt: number;
};
type CloudConnectionConfig = {
tenantId: string;
key: string;
value: unknown;
};
const alteration: AlterationScript = {
up: async (pool) => {
const rows = await pool.many<Application>(
sql`select * from applications where type = ${ApplicationType.MachineToMachine} and tenant_id = ${adminTenantId} and name = ${cloudServiceApplicationName}`
);
const { rows: existingCloudConnections } = await pool.query<CloudConnectionConfig>(sql`
select * from logto_configs where key = ${cloudConnectionConfigKey}
`);
const tenantIdsWithExistingRecords = new Set(
existingCloudConnections.map(({ tenantId }) => tenantId)
);
const filteredRows = rows.filter(
({ customClientMetadata: { tenantId } }) => !tenantIdsWithExistingRecords.has(tenantId)
);
if (filteredRows.length === 0) {
return;
}
await pool.query(sql`
insert into logto_configs (tenant_id, key, value) values ${sql.join(
filteredRows.map(({ id, secret, customClientMetadata }) => {
const { tenantId } = customClientMetadata;
const cloudConnectionValue = {
appId: id,
appSecret: secret,
resource: cloudConnectionResourceIndicator,
};
return sql`(${tenantId}, ${cloudConnectionConfigKey}, ${JSON.stringify(
cloudConnectionValue
)})`;
}),
sql`,`
)}
`);
},
down: async (pool) => {
await pool.query(sql`
delete from logto_configs where key = ${cloudConnectionConfigKey}
`);
},
};
export default alteration;

View file

@ -26,7 +26,7 @@ export const createDefaultAdminConsoleConfig = (
},
} satisfies CreateLogtoConfig);
export const createDefaultCloudConnectionConfig = (
export const createCloudConnectionConfig = (
forTenantId: string,
appId: string,
appSecret: string