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';
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
@@ -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 { InfraConfigEnum } from 'src/types/InfraConfig';
|
||||
import { throwErr } from 'src/utils';
|
||||
|
||||
export enum ServiceStatus {
|
||||
ENABLE = 'ENABLE',
|
||||
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
|
||||
*
|
||||
@@ -42,3 +65,42 @@ export function stopApp() {
|
||||
process.kill(process.pid, 'SIGTERM');
|
||||
}, 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_SERVICE_NOT_CONFIGURED,
|
||||
} from 'src/errors';
|
||||
import { throwErr, validateEmail, validateSMTPUrl } from 'src/utils';
|
||||
import { throwErr, validateSMTPEmail, validateSMTPUrl } from 'src/utils';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { ServiceStatus, stopApp } from './helper';
|
||||
import { ServiceStatus, getConfiguredSSOProviders, stopApp } from './helper';
|
||||
import { EnableAndDisableSSOArgs, InfraConfigArgs } from './input-args';
|
||||
import { AuthProvider } from 'src/auth/helper';
|
||||
|
||||
@@ -71,7 +71,7 @@ export class InfraConfigService implements OnModuleInit {
|
||||
},
|
||||
{
|
||||
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
|
||||
* @param name Name of the InfraConfig
|
||||
@@ -187,30 +200,24 @@ export class InfraConfigService implements OnModuleInit {
|
||||
/**
|
||||
* Check if the service is configured or not
|
||||
* @param service Service can be Auth Provider, Mailer, Audit Log etc.
|
||||
* @param configMap Map of all the infra configs
|
||||
* @returns Either true or false
|
||||
*/
|
||||
isServiceConfigured(service: AuthProvider) {
|
||||
isServiceConfigured(
|
||||
service: AuthProvider,
|
||||
configMap: Record<string, string>,
|
||||
) {
|
||||
switch (service) {
|
||||
case AuthProvider.GOOGLE:
|
||||
return (
|
||||
this.configService.get<string>('INFRA.GOOGLE_CLIENT_ID') &&
|
||||
this.configService.get<string>('INFRA.GOOGLE_CLIENT_SECRET')
|
||||
);
|
||||
return configMap.GOOGLE_CLIENT_ID && configMap.GOOGLE_CLIENT_SECRET;
|
||||
case AuthProvider.GITHUB:
|
||||
return (
|
||||
this.configService.get<string>('INFRA.GITHUB_CLIENT_ID') &&
|
||||
!this.configService.get<string>('INFRA.GITHUB_CLIENT_SECRET')
|
||||
);
|
||||
return configMap.GITHUB_CLIENT_ID && configMap.GITHUB_CLIENT_SECRET;
|
||||
case AuthProvider.MICROSOFT:
|
||||
return (
|
||||
this.configService.get<string>('INFRA.MICROSOFT_CLIENT_ID') &&
|
||||
!this.configService.get<string>('INFRA.MICROSOFT_CLIENT_SECRET')
|
||||
configMap.MICROSOFT_CLIENT_ID && configMap.MICROSOFT_CLIENT_SECRET
|
||||
);
|
||||
case AuthProvider.EMAIL:
|
||||
return (
|
||||
this.configService.get<string>('INFRA.MAILER_SMTP_URL') &&
|
||||
this.configService.get<string>('INFRA.MAILER_ADDRESS_FROM')
|
||||
);
|
||||
return configMap.MAILER_SMTP_URL && configMap.MAILER_ADDRESS_FROM;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -229,11 +236,11 @@ export class InfraConfigService implements OnModuleInit {
|
||||
|
||||
let updatedAuthProviders = allowedAuthProviders;
|
||||
|
||||
for (let i = 0; i < providerInfo.length; i++) {
|
||||
const { provider, status } = providerInfo[i];
|
||||
const infraConfigMap = await this.getInfraConfigsMap();
|
||||
|
||||
providerInfo.forEach(({ provider, status }) => {
|
||||
if (status === ServiceStatus.ENABLE) {
|
||||
const isConfigured = this.isServiceConfigured(provider);
|
||||
const isConfigured = this.isServiceConfigured(provider, infraConfigMap);
|
||||
if (!isConfigured) {
|
||||
throwErr(INFRA_CONFIG_SERVICE_NOT_CONFIGURED);
|
||||
}
|
||||
@@ -243,7 +250,7 @@ export class InfraConfigService implements OnModuleInit {
|
||||
(p) => p !== provider,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
updatedAuthProviders = [...new Set(updatedAuthProviders)];
|
||||
|
||||
@@ -342,7 +349,7 @@ export class InfraConfigService implements OnModuleInit {
|
||||
if (!isValidUrl) return E.left(INFRA_CONFIG_INVALID_INPUT);
|
||||
break;
|
||||
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);
|
||||
break;
|
||||
case InfraConfigEnumForClient.GOOGLE_CLIENT_ID:
|
||||
|
||||
@@ -17,7 +17,8 @@ async function bootstrap() {
|
||||
console.log(`Port: ${configService.get('PORT')}`);
|
||||
|
||||
checkEnvironmentAuthProvider(
|
||||
configService.get('VITE_ALLOWED_AUTH_PROVIDERS'),
|
||||
configService.get('INFRA.VITE_ALLOWED_AUTH_PROVIDERS') ??
|
||||
configService.get('VITE_ALLOWED_AUTH_PROVIDERS'),
|
||||
);
|
||||
|
||||
app.use(
|
||||
|
||||
@@ -131,6 +131,28 @@ export const validateEmail = (email: string) => {
|
||||
).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
|
||||
* @param url The URL to validate
|
||||
|
||||
Reference in New Issue
Block a user