fix: improve smtp email validation and fix enableAndDisableSSO mutation (#3689)
Co-authored-by: Balu Babu <balub997@gmail.com>
This commit is contained in:
committed by
Andrew Bastin
parent
0df194f9c5
commit
54d8378ccf
@@ -28,6 +28,13 @@ export const JSON_INVALID = 'json_invalid';
|
|||||||
*/
|
*/
|
||||||
export const AUTH_PROVIDER_NOT_SPECIFIED = 'auth/provider_not_specified';
|
export const AUTH_PROVIDER_NOT_SPECIFIED = 'auth/provider_not_specified';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auth Provider not specified
|
||||||
|
* (Auth)
|
||||||
|
*/
|
||||||
|
export const AUTH_PROVIDER_NOT_CONFIGURED =
|
||||||
|
'auth/provider_not_configured_correctly';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Environment variable "VITE_ALLOWED_AUTH_PROVIDERS" is not present in .env file
|
* Environment variable "VITE_ALLOWED_AUTH_PROVIDERS" is not present in .env file
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,10 +1,33 @@
|
|||||||
|
import { AuthProvider } from 'src/auth/helper';
|
||||||
|
import { AUTH_PROVIDER_NOT_CONFIGURED } from 'src/errors';
|
||||||
import { PrismaService } from 'src/prisma/prisma.service';
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
|
import { InfraConfigEnum } from 'src/types/InfraConfig';
|
||||||
|
import { throwErr } from 'src/utils';
|
||||||
|
|
||||||
export enum ServiceStatus {
|
export enum ServiceStatus {
|
||||||
ENABLE = 'ENABLE',
|
ENABLE = 'ENABLE',
|
||||||
DISABLE = 'DISABLE',
|
DISABLE = 'DISABLE',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AuthProviderConfigurations = {
|
||||||
|
[AuthProvider.GOOGLE]: [
|
||||||
|
InfraConfigEnum.GOOGLE_CLIENT_ID,
|
||||||
|
InfraConfigEnum.GOOGLE_CLIENT_SECRET,
|
||||||
|
],
|
||||||
|
[AuthProvider.GITHUB]: [
|
||||||
|
InfraConfigEnum.GITHUB_CLIENT_ID,
|
||||||
|
InfraConfigEnum.GITHUB_CLIENT_SECRET,
|
||||||
|
],
|
||||||
|
[AuthProvider.MICROSOFT]: [
|
||||||
|
InfraConfigEnum.MICROSOFT_CLIENT_ID,
|
||||||
|
InfraConfigEnum.MICROSOFT_CLIENT_SECRET,
|
||||||
|
],
|
||||||
|
[AuthProvider.EMAIL]: [
|
||||||
|
InfraConfigEnum.MAILER_SMTP_URL,
|
||||||
|
InfraConfigEnum.MAILER_ADDRESS_FROM,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load environment variables from the database and set them in the process
|
* Load environment variables from the database and set them in the process
|
||||||
*
|
*
|
||||||
@@ -42,3 +65,42 @@ export function stopApp() {
|
|||||||
process.kill(process.pid, 'SIGTERM');
|
process.kill(process.pid, 'SIGTERM');
|
||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the configured SSO providers
|
||||||
|
* @returns Array of configured SSO providers
|
||||||
|
*/
|
||||||
|
export function getConfiguredSSOProviders() {
|
||||||
|
const allowedAuthProviders: string[] =
|
||||||
|
process.env.VITE_ALLOWED_AUTH_PROVIDERS.split(',');
|
||||||
|
let configuredAuthProviders: string[] = [];
|
||||||
|
|
||||||
|
const addProviderIfConfigured = (provider) => {
|
||||||
|
const configParameters: string[] = AuthProviderConfigurations[provider];
|
||||||
|
|
||||||
|
const isConfigured = configParameters.every((configParameter) => {
|
||||||
|
return process.env[configParameter];
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isConfigured) configuredAuthProviders.push(provider);
|
||||||
|
};
|
||||||
|
|
||||||
|
allowedAuthProviders.forEach((provider) => addProviderIfConfigured(provider));
|
||||||
|
|
||||||
|
if (configuredAuthProviders.length === 0) {
|
||||||
|
throwErr(AUTH_PROVIDER_NOT_CONFIGURED);
|
||||||
|
} else if (allowedAuthProviders.length !== configuredAuthProviders.length) {
|
||||||
|
const unConfiguredAuthProviders = allowedAuthProviders.filter(
|
||||||
|
(provider) => {
|
||||||
|
return !configuredAuthProviders.includes(provider);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
`${unConfiguredAuthProviders.join(
|
||||||
|
',',
|
||||||
|
)} SSO auth provider(s) are not configured properly. Do configure them from Admin Dashboard.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return configuredAuthProviders.join(',');
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ import {
|
|||||||
INFRA_CONFIG_UPDATE_FAILED,
|
INFRA_CONFIG_UPDATE_FAILED,
|
||||||
INFRA_CONFIG_SERVICE_NOT_CONFIGURED,
|
INFRA_CONFIG_SERVICE_NOT_CONFIGURED,
|
||||||
} from 'src/errors';
|
} from 'src/errors';
|
||||||
import { throwErr, validateEmail, validateSMTPUrl } from 'src/utils';
|
import { throwErr, validateSMTPEmail, validateSMTPUrl } from 'src/utils';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { ServiceStatus, stopApp } from './helper';
|
import { ServiceStatus, getConfiguredSSOProviders, stopApp } from './helper';
|
||||||
import { EnableAndDisableSSOArgs, InfraConfigArgs } from './input-args';
|
import { EnableAndDisableSSOArgs, InfraConfigArgs } from './input-args';
|
||||||
import { AuthProvider } from 'src/auth/helper';
|
import { AuthProvider } from 'src/auth/helper';
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ export class InfraConfigService implements OnModuleInit {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
|
name: InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
|
||||||
value: process.env.VITE_ALLOWED_AUTH_PROVIDERS.toLocaleUpperCase(),
|
value: getConfiguredSSOProviders(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -130,6 +130,19 @@ export class InfraConfigService implements OnModuleInit {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all the InfraConfigs as map
|
||||||
|
* @returns InfraConfig map
|
||||||
|
*/
|
||||||
|
async getInfraConfigsMap() {
|
||||||
|
const infraConfigs = await this.prisma.infraConfig.findMany();
|
||||||
|
const infraConfigMap: Record<string, string> = {};
|
||||||
|
infraConfigs.forEach((config) => {
|
||||||
|
infraConfigMap[config.name] = config.value;
|
||||||
|
});
|
||||||
|
return infraConfigMap;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update InfraConfig by name
|
* Update InfraConfig by name
|
||||||
* @param name Name of the InfraConfig
|
* @param name Name of the InfraConfig
|
||||||
@@ -187,30 +200,24 @@ export class InfraConfigService implements OnModuleInit {
|
|||||||
/**
|
/**
|
||||||
* Check if the service is configured or not
|
* Check if the service is configured or not
|
||||||
* @param service Service can be Auth Provider, Mailer, Audit Log etc.
|
* @param service Service can be Auth Provider, Mailer, Audit Log etc.
|
||||||
|
* @param configMap Map of all the infra configs
|
||||||
* @returns Either true or false
|
* @returns Either true or false
|
||||||
*/
|
*/
|
||||||
isServiceConfigured(service: AuthProvider) {
|
isServiceConfigured(
|
||||||
|
service: AuthProvider,
|
||||||
|
configMap: Record<string, string>,
|
||||||
|
) {
|
||||||
switch (service) {
|
switch (service) {
|
||||||
case AuthProvider.GOOGLE:
|
case AuthProvider.GOOGLE:
|
||||||
return (
|
return configMap.GOOGLE_CLIENT_ID && configMap.GOOGLE_CLIENT_SECRET;
|
||||||
this.configService.get<string>('INFRA.GOOGLE_CLIENT_ID') &&
|
|
||||||
this.configService.get<string>('INFRA.GOOGLE_CLIENT_SECRET')
|
|
||||||
);
|
|
||||||
case AuthProvider.GITHUB:
|
case AuthProvider.GITHUB:
|
||||||
return (
|
return configMap.GITHUB_CLIENT_ID && configMap.GITHUB_CLIENT_SECRET;
|
||||||
this.configService.get<string>('INFRA.GITHUB_CLIENT_ID') &&
|
|
||||||
!this.configService.get<string>('INFRA.GITHUB_CLIENT_SECRET')
|
|
||||||
);
|
|
||||||
case AuthProvider.MICROSOFT:
|
case AuthProvider.MICROSOFT:
|
||||||
return (
|
return (
|
||||||
this.configService.get<string>('INFRA.MICROSOFT_CLIENT_ID') &&
|
configMap.MICROSOFT_CLIENT_ID && configMap.MICROSOFT_CLIENT_SECRET
|
||||||
!this.configService.get<string>('INFRA.MICROSOFT_CLIENT_SECRET')
|
|
||||||
);
|
);
|
||||||
case AuthProvider.EMAIL:
|
case AuthProvider.EMAIL:
|
||||||
return (
|
return configMap.MAILER_SMTP_URL && configMap.MAILER_ADDRESS_FROM;
|
||||||
this.configService.get<string>('INFRA.MAILER_SMTP_URL') &&
|
|
||||||
this.configService.get<string>('INFRA.MAILER_ADDRESS_FROM')
|
|
||||||
);
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -229,11 +236,11 @@ export class InfraConfigService implements OnModuleInit {
|
|||||||
|
|
||||||
let updatedAuthProviders = allowedAuthProviders;
|
let updatedAuthProviders = allowedAuthProviders;
|
||||||
|
|
||||||
for (let i = 0; i < providerInfo.length; i++) {
|
const infraConfigMap = await this.getInfraConfigsMap();
|
||||||
const { provider, status } = providerInfo[i];
|
|
||||||
|
|
||||||
|
providerInfo.forEach(({ provider, status }) => {
|
||||||
if (status === ServiceStatus.ENABLE) {
|
if (status === ServiceStatus.ENABLE) {
|
||||||
const isConfigured = this.isServiceConfigured(provider);
|
const isConfigured = this.isServiceConfigured(provider, infraConfigMap);
|
||||||
if (!isConfigured) {
|
if (!isConfigured) {
|
||||||
throwErr(INFRA_CONFIG_SERVICE_NOT_CONFIGURED);
|
throwErr(INFRA_CONFIG_SERVICE_NOT_CONFIGURED);
|
||||||
}
|
}
|
||||||
@@ -243,7 +250,7 @@ export class InfraConfigService implements OnModuleInit {
|
|||||||
(p) => p !== provider,
|
(p) => p !== provider,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
updatedAuthProviders = [...new Set(updatedAuthProviders)];
|
updatedAuthProviders = [...new Set(updatedAuthProviders)];
|
||||||
|
|
||||||
@@ -342,7 +349,7 @@ export class InfraConfigService implements OnModuleInit {
|
|||||||
if (!isValidUrl) return E.left(INFRA_CONFIG_INVALID_INPUT);
|
if (!isValidUrl) return E.left(INFRA_CONFIG_INVALID_INPUT);
|
||||||
break;
|
break;
|
||||||
case InfraConfigEnumForClient.MAILER_ADDRESS_FROM:
|
case InfraConfigEnumForClient.MAILER_ADDRESS_FROM:
|
||||||
const isValidEmail = validateEmail(infraConfigs[i].value);
|
const isValidEmail = validateSMTPEmail(infraConfigs[i].value);
|
||||||
if (!isValidEmail) return E.left(INFRA_CONFIG_INVALID_INPUT);
|
if (!isValidEmail) return E.left(INFRA_CONFIG_INVALID_INPUT);
|
||||||
break;
|
break;
|
||||||
case InfraConfigEnumForClient.GOOGLE_CLIENT_ID:
|
case InfraConfigEnumForClient.GOOGLE_CLIENT_ID:
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ async function bootstrap() {
|
|||||||
console.log(`Port: ${configService.get('PORT')}`);
|
console.log(`Port: ${configService.get('PORT')}`);
|
||||||
|
|
||||||
checkEnvironmentAuthProvider(
|
checkEnvironmentAuthProvider(
|
||||||
|
configService.get('INFRA.VITE_ALLOWED_AUTH_PROVIDERS') ??
|
||||||
configService.get('VITE_ALLOWED_AUTH_PROVIDERS'),
|
configService.get('VITE_ALLOWED_AUTH_PROVIDERS'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,28 @@ export const validateEmail = (email: string) => {
|
|||||||
).test(email);
|
).test(email);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Regular expressions for supported address object formats by nodemailer
|
||||||
|
// check out for more info https://nodemailer.com/message/addresses
|
||||||
|
const emailRegex1 = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||||
|
const emailRegex2 =
|
||||||
|
/^[\w\s]* <([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>$/;
|
||||||
|
const emailRegex3 =
|
||||||
|
/^"[\w\s]+" <([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>$/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if the SMTP email is valid or not
|
||||||
|
* @param email
|
||||||
|
* @returns A Boolean depending on the format of the email
|
||||||
|
*/
|
||||||
|
export const validateSMTPEmail = (email: string) => {
|
||||||
|
// Check if the input matches any of the formats
|
||||||
|
return (
|
||||||
|
emailRegex1.test(email) ||
|
||||||
|
emailRegex2.test(email) ||
|
||||||
|
emailRegex3.test(email)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks to see if the URL is valid or not
|
* Checks to see if the URL is valid or not
|
||||||
* @param url The URL to validate
|
* @param url The URL to validate
|
||||||
|
|||||||
Reference in New Issue
Block a user