feat: new session alert
This commit is contained in:
parent
83d60612f2
commit
f3f233eb14
5 changed files with 161 additions and 8 deletions
14
app/config/locale/templates/email-session-alert.tpl
Normal file
14
app/config/locale/templates/email-session-alert.tpl
Normal file
|
@ -0,0 +1,14 @@
|
|||
<p>{{hello}},</p>
|
||||
|
||||
<p>{{body}}</p>
|
||||
|
||||
<ol>
|
||||
<li>{{device}}</li>
|
||||
<li>{{ipAddress}}</li>
|
||||
<li>{{country}}</li>
|
||||
</ol>
|
||||
|
||||
<p>{{footer}}</p>
|
||||
|
||||
<p style="margin-bottom: 0px;">{{thanks}}</p>
|
||||
<p style="margin-top: 0px;">{{signature}}</p>
|
|
@ -18,6 +18,15 @@
|
|||
"emails.magicSession.securityPhrase": "Security phrase for this email is {{b}}{{phrase}}{{/b}}. You can trust this email if this phrase matches the phrase shown during sign in.",
|
||||
"emails.magicSession.thanks": "Thanks,",
|
||||
"emails.magicSession.signature": "{{project}} team",
|
||||
"emails.sessionAlert.subject": "New session alert for {{project}}",
|
||||
"emails.sessionAlert.hello":"Hello {{user}}",
|
||||
"emails.sessionAlert.body": "We're writing to inform you that a new session has been initiated on your {{b}}{{project}}{{/b}} account, on {{b}}{{dateTime}}{{/b}}. \nHere are the details of the new session: ",
|
||||
"emails.sessionAlert.device": "Device: {{b}}{{agentDevice}}{{/b}}",
|
||||
"emails.sessionAlert.ipAddress": "IP Address: {{b}}{{ipAddress}}{{/b}}",
|
||||
"emails.sessionAlert.country": "Country: {{b}}{{country}}{{/b}}",
|
||||
"emails.sessionAlert.footer": "If you didn't request the sign in, you can safely ignore this email. If you suspect unauthorized activity, please secure your account immediately.",
|
||||
"emails.sessionAlert.thanks": "Thanks,",
|
||||
"emails.sessionAlert.signature": "{{project}} team",
|
||||
"emails.otpSession.subject": "OTP for {{project}} Login",
|
||||
"emails.otpSession.hello": "Hello {{user}}",
|
||||
"emails.otpSession.description": "Enter the following verification code when prompted to securely sign in to your {{b}}{{project}}{{/b}} account. This code will expire in 15 minutes.",
|
||||
|
|
|
@ -166,6 +166,93 @@ $createSession = function (string $userId, string $secret, Request $request, Res
|
|||
$response->dynamic($session, Response::MODEL_SESSION);
|
||||
};
|
||||
|
||||
$sendSessionEmail = function (Request $request, Locale $locale, Document $user, Document $project, Reader $geodb, Event $queueForMails) {
|
||||
$subject = $locale->getText("emails.sessionAlert.subject");
|
||||
$customTemplate = $project->getAttribute('templates', [])['email.sessionAlert-' . $locale->default] ?? [];
|
||||
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
$agentDevice = $detector->getDevice();
|
||||
|
||||
$record = $geodb->get($request->getIP());
|
||||
$countryCode = $record['country']['iso_code'] ?? '';
|
||||
|
||||
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-session-alert.tpl');
|
||||
$message
|
||||
->setParam('{{hello}}', $locale->getText("emails.sessionAlert.hello"))
|
||||
->setParam('{{body}}', $locale->getText("emails.sessionAlert.body"))
|
||||
->setParam('{{device}}', $locale->getText("emails.sessionAlert.device"))
|
||||
->setParam('{{ipAddress}}', $locale->getText("emails.sessionAlert.ipAddress"))
|
||||
->setParam('{{country}}', $locale->getText("emails.sessionAlert.country"))
|
||||
->setParam('{{footer}}', $locale->getText("emails.sessionAlert.footer"))
|
||||
->setParam('{{signature}}', $locale->getText("emails.sessionAlert.signature"));
|
||||
|
||||
$body = $message->render();
|
||||
|
||||
$smtp = $project->getAttribute('smtp', []);
|
||||
$smtpEnabled = $smtp['enabled'] ?? false;
|
||||
|
||||
$senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
|
||||
$senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
|
||||
$replyTo = "";
|
||||
|
||||
if ($smtpEnabled) {
|
||||
if (!empty($smtp['senderEmail'])) {
|
||||
$senderEmail = $smtp['senderEmail'];
|
||||
}
|
||||
if (!empty($smtp['senderName'])) {
|
||||
$senderName = $smtp['senderName'];
|
||||
}
|
||||
if (!empty($smtp['replyTo'])) {
|
||||
$replyTo = $smtp['replyTo'];
|
||||
}
|
||||
|
||||
$queueForMails
|
||||
->setSmtpHost($smtp['host'] ?? '')
|
||||
->setSmtpPort($smtp['port'] ?? '')
|
||||
->setSmtpUsername($smtp['username'] ?? '')
|
||||
->setSmtpPassword($smtp['password'] ?? '')
|
||||
->setSmtpSecure($smtp['secure'] ?? '');
|
||||
|
||||
if (!empty($customTemplate)) {
|
||||
if (!empty($customTemplate['senderEmail'])) {
|
||||
$senderEmail = $customTemplate['senderEmail'];
|
||||
}
|
||||
if (!empty($customTemplate['senderName'])) {
|
||||
$senderName = $customTemplate['senderName'];
|
||||
}
|
||||
if (!empty($customTemplate['replyTo'])) {
|
||||
$replyTo = $customTemplate['replyTo'];
|
||||
}
|
||||
|
||||
$body = $customTemplate['message'] ?? '';
|
||||
$subject = $customTemplate['subject'] ?? $subject;
|
||||
}
|
||||
|
||||
$queueForMails
|
||||
->setSmtpReplyTo($replyTo)
|
||||
->setSmtpSenderEmail($senderEmail)
|
||||
->setSmtpSenderName($senderName);
|
||||
}
|
||||
|
||||
$emailVariables = [
|
||||
'direction' => $locale->getText('settings.direction'),
|
||||
// {{user}}, {{redirect}} and {{project}} are required in default and custom templates
|
||||
'user' => $user->getAttribute('name'),
|
||||
'project' => $project->getAttribute('name'),
|
||||
'redirect' => $url,
|
||||
'agentDevice' => $agentDevice['deviceBrand'] ?? $agentDevice['deviceBrand'] ?? 'UNKNOWN',
|
||||
'ipAddress' => $request->getIP(),
|
||||
'country' => $locale->getText('countries.' . strtolower($countryCode), $locale->getText('locale.country.unknown')),
|
||||
];
|
||||
|
||||
$queueForMails
|
||||
->setSubject($subject)
|
||||
->setBody($body)
|
||||
->setVariables($emailVariables)
|
||||
->setRecipient($email)
|
||||
->trigger();
|
||||
}
|
||||
|
||||
App::post('/v1/account')
|
||||
->desc('Create account')
|
||||
->groups(['api', 'account', 'auth'])
|
||||
|
|
|
@ -93,7 +93,15 @@ App::post('/v1/projects')
|
|||
}
|
||||
|
||||
$auth = Config::getParam('auth', []);
|
||||
$auths = ['limit' => 0, 'maxSessions' => APP_LIMIT_USER_SESSIONS_DEFAULT, 'passwordHistory' => 0, 'passwordDictionary' => false, 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, 'personalDataCheck' => false];
|
||||
$auths = [
|
||||
'limit' => 0,
|
||||
'maxSessions' => APP_LIMIT_USER_SESSIONS_DEFAULT,
|
||||
'passwordHistory' => 0, 'passwordDictionary' => false,
|
||||
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG,
|
||||
'personalDataCheck' => false,
|
||||
'sessionEmails' => false,
|
||||
];
|
||||
|
||||
foreach ($auth as $index => $method) {
|
||||
$auths[$method['key'] ?? ''] = true;
|
||||
}
|
||||
|
@ -355,7 +363,7 @@ App::patch('/v1/projects/:projectId')
|
|||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/team')
|
||||
->desc('Update Project Team')
|
||||
->desc('Update project team')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
|
@ -596,6 +604,37 @@ App::patch('/v1/projects/:projectId/oauth2')
|
|||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/auth/session-emails')
|
||||
->desc('Update project sessions emails')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'projects')
|
||||
->label('sdk.method', 'updateSessionEmails')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_PROJECT)
|
||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
->param('sessionEmails', false, new Boolean(true), 'Set to true to enable session emails.')
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $projectId, bool $sessionEmails, Response $response, Database $dbForConsole) {
|
||||
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$auths = $project->getAttribute('auths', []);
|
||||
$auths['sessionEmails'] = $sessionEmails;
|
||||
|
||||
$dbForConsole->updateDocument('projects', $project->getId(), $project
|
||||
->setAttribute('auths', $auths));
|
||||
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/auth/limit')
|
||||
->desc('Update project users limit')
|
||||
->groups(['api', 'projects'])
|
||||
|
|
|
@ -138,6 +138,12 @@ class Project extends Model
|
|||
'default' => false,
|
||||
'example' => true,
|
||||
])
|
||||
->addRule('authSessionEmails', [
|
||||
'type' => self::TYPE_BOOLEAN,
|
||||
'description' => 'Whether or not to send session emails to users.',
|
||||
'default' => false,
|
||||
'example' => true,
|
||||
])
|
||||
->addRule('oAuthProviders', [
|
||||
'type' => Response::MODEL_AUTH_PROVIDER,
|
||||
'description' => 'List of Auth Providers.',
|
||||
|
@ -220,8 +226,7 @@ class Project extends Model
|
|||
'description' => 'SMTP server secure protocol',
|
||||
'default' => '',
|
||||
'example' => 'tls',
|
||||
])
|
||||
;
|
||||
]);
|
||||
|
||||
$services = Config::getParam('services', []);
|
||||
$auth = Config::getParam('auth', []);
|
||||
|
@ -236,8 +241,7 @@ class Project extends Model
|
|||
'description' => $name . ' auth method status',
|
||||
'example' => true,
|
||||
'default' => true,
|
||||
])
|
||||
;
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($services as $service) {
|
||||
|
@ -254,8 +258,7 @@ class Project extends Model
|
|||
'description' => $name . ' service status',
|
||||
'example' => true,
|
||||
'default' => true,
|
||||
])
|
||||
;
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -321,6 +324,7 @@ class Project extends Model
|
|||
$document->setAttribute('authPasswordHistory', $authValues['passwordHistory'] ?? 0);
|
||||
$document->setAttribute('authPasswordDictionary', $authValues['passwordDictionary'] ?? false);
|
||||
$document->setAttribute('authPersonalDataCheck', $authValues['personalDataCheck'] ?? false);
|
||||
$document->setAttribute('authSessionEmails', $authValues['sessionEmails'] ?? false);
|
||||
|
||||
foreach ($auth as $index => $method) {
|
||||
$key = $method['key'];
|
||||
|
|
Loading…
Reference in a new issue