@punch-in/strapi-admin 1.2.14 → 1.2.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useReducer, useEffect } from 'react';
|
|
2
|
-
import { Checkbox, Button, Flex
|
|
2
|
+
import { Checkbox, Button, Flex } from '@punch-in/buffet-modern-core';
|
|
3
3
|
import { LoadingIndicator } from '@punch-in/buffet-modern-styles';
|
|
4
4
|
import { useIntl } from 'react-intl';
|
|
5
5
|
import { get } from 'lodash';
|
|
@@ -99,31 +99,33 @@ const Login = ({ children, formErrors, modifiedData, onChange, onSubmit, request
|
|
|
99
99
|
</form>
|
|
100
100
|
{children}
|
|
101
101
|
</Box>
|
|
102
|
-
{/* SSO Providers Section */}
|
|
103
|
-
{!isLoading && providers.length > 0 && (
|
|
104
|
-
<>
|
|
105
|
-
<Separator />
|
|
106
|
-
<Padded top bottom size="smd">
|
|
107
|
-
<BaselineAlignment top size="3px" />
|
|
108
|
-
</Padded>
|
|
109
|
-
<div style={{
|
|
110
|
-
background: '#d2e9fe',
|
|
111
|
-
padding: '1.4rem',
|
|
112
|
-
boxShadow: 'rgb(221, 221, 221) 12px 12px 9px 0px' }}>
|
|
113
|
-
<Flex flexWrap="wrap" justifyContent="center" alignItems="center">
|
|
114
|
-
<div>{formatMessage({ id: 'Auth.components.sso.info' })}: </div>
|
|
115
|
-
{providers.map(provider => (
|
|
116
|
-
<div key={provider.uid} style={{ padding: '5px 4px' }}>
|
|
117
|
-
<ProviderButton provider={provider} />
|
|
118
|
-
</div>
|
|
119
|
-
))}
|
|
120
|
-
</Flex>
|
|
121
|
-
</div>
|
|
122
|
-
</>
|
|
123
|
-
)}
|
|
124
102
|
</BaselineAlignment>
|
|
125
103
|
</Section>
|
|
104
|
+
{/* Forgot password link, directly below the login form */}
|
|
126
105
|
<AuthLink label="Auth.link.forgot-password" to="/auth/forgot-password" />
|
|
106
|
+
{/* SSO Providers Section - pinned to the bottom of the viewport */}
|
|
107
|
+
{!isLoading && providers.length > 0 && (
|
|
108
|
+
<div
|
|
109
|
+
style={{
|
|
110
|
+
position: 'fixed',
|
|
111
|
+
left: 0,
|
|
112
|
+
right: 0,
|
|
113
|
+
bottom: 0,
|
|
114
|
+
background: '#d2e9fe',
|
|
115
|
+
padding: '1.4rem',
|
|
116
|
+
boxShadow: 'rgb(221, 221, 221) 0px -6px 9px 0px',
|
|
117
|
+
}}
|
|
118
|
+
>
|
|
119
|
+
<Flex flexWrap="wrap" justifyContent="center" alignItems="center">
|
|
120
|
+
<div>{formatMessage({ id: 'Auth.components.sso.info' })}: </div>
|
|
121
|
+
{providers.map(provider => (
|
|
122
|
+
<div key={provider.uid} style={{ padding: '5px 4px' }}>
|
|
123
|
+
<ProviderButton provider={provider} />
|
|
124
|
+
</div>
|
|
125
|
+
))}
|
|
126
|
+
</Flex>
|
|
127
|
+
</div>
|
|
128
|
+
)}
|
|
127
129
|
</>
|
|
128
130
|
);
|
|
129
131
|
};
|
|
@@ -167,7 +167,10 @@ module.exports = {
|
|
|
167
167
|
return ctx.badRequest('ValidationError', err);
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
// Awaited: in Lambda, un-awaited async work is frozen once the response
|
|
171
|
+
// returns, so the email send (SES invoke) would often never run. Still
|
|
172
|
+
// 204 whether or not the address matched an account - no existence leak.
|
|
173
|
+
await strapi.admin.services.auth.forgotPassword(input);
|
|
171
174
|
|
|
172
175
|
ctx.status = 204;
|
|
173
176
|
},
|
package/package.json
CHANGED
package/services/auth.js
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
const bcrypt = require('bcryptjs');
|
|
4
4
|
const _ = require('lodash');
|
|
5
5
|
const { getAbsoluteAdminUrl } = require('strapi-utils');
|
|
6
|
+
const { LambdaClient, InvokeCommand } = require('@aws-sdk/client-lambda');
|
|
7
|
+
|
|
8
|
+
// SDK v3 client-lambda is preinstalled by the Node.js 18+ Lambda managed
|
|
9
|
+
// runtime, so it does not need to be bundled in the strapi-lambda layer.
|
|
10
|
+
// See controllers/authentication.js for the same pattern (SSO providers).
|
|
11
|
+
const lambdaClient = new LambdaClient({});
|
|
6
12
|
|
|
7
13
|
/**
|
|
8
14
|
* hashes a password
|
|
@@ -77,10 +83,26 @@ const forgotPassword = async ({ email } = {}) => {
|
|
|
77
83
|
const resetPasswordToken = strapi.admin.services.token.createToken();
|
|
78
84
|
await strapi.admin.services.user.updateById(user.id, { resetPasswordToken });
|
|
79
85
|
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
// getAbsoluteAdminUrl now resolves to the tenant's real admin SPA origin
|
|
87
|
+
// (ADMIN_URL in prod-stack-*.yaml), not localhost.
|
|
88
|
+
const url = `${_.trimEnd(
|
|
89
|
+
getAbsoluteAdminUrl(strapi.config),
|
|
90
|
+
'/'
|
|
83
91
|
)}/auth/reset-password?code=${resetPasswordToken}`;
|
|
92
|
+
|
|
93
|
+
// ADMIN_EMAIL_MODE=invoke: this Lambda's VPC has no internet route, so the
|
|
94
|
+
// email is sent by the central AdminEmailFunction, reached via a direct SDK
|
|
95
|
+
// invoke (features/API-REDESIGN.md). Anything else keeps the old
|
|
96
|
+
// strapi-plugin-email path for local/dev.
|
|
97
|
+
if (process.env.ADMIN_EMAIL_MODE === 'invoke') {
|
|
98
|
+
return sendAdminEmailViaInvoke({
|
|
99
|
+
kind: 'forgot-password',
|
|
100
|
+
to: user.email,
|
|
101
|
+
firstname: user.firstname,
|
|
102
|
+
url,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
84
106
|
return strapi.plugins.email.services.email
|
|
85
107
|
.sendTemplatedEmail(
|
|
86
108
|
{
|
|
@@ -100,6 +122,42 @@ const forgotPassword = async ({ email } = {}) => {
|
|
|
100
122
|
});
|
|
101
123
|
};
|
|
102
124
|
|
|
125
|
+
// Invokes stripe-charge's AdminEmailFunction (see features/API-REDESIGN.md).
|
|
126
|
+
// Errors are logged, never surfaced to the caller - a forgot-password request
|
|
127
|
+
// returns 204 whether or not the email actually went out.
|
|
128
|
+
const sendAdminEmailViaInvoke = async payload => {
|
|
129
|
+
const functionName = process.env.ADMIN_EMAIL_FUNCTION_NAME;
|
|
130
|
+
if (!functionName) {
|
|
131
|
+
strapi.log.error('ADMIN_EMAIL_FUNCTION_NAME is not configured');
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const response = await lambdaClient.send(
|
|
137
|
+
new InvokeCommand({
|
|
138
|
+
FunctionName: functionName,
|
|
139
|
+
InvocationType: 'RequestResponse',
|
|
140
|
+
Payload: Buffer.from(JSON.stringify(payload)),
|
|
141
|
+
})
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
if (response.FunctionError) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
`AdminEmailFunction returned ${response.FunctionError}: ${Buffer.from(
|
|
147
|
+
response.Payload
|
|
148
|
+
).toString('utf8')}`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const result = JSON.parse(Buffer.from(response.Payload).toString('utf8'));
|
|
153
|
+
if (!result.ok) {
|
|
154
|
+
strapi.log.error(`AdminEmailFunction rejected request: ${result.error}`);
|
|
155
|
+
}
|
|
156
|
+
} catch (err) {
|
|
157
|
+
strapi.log.error(`Failed to invoke AdminEmailFunction: ${err.message}`);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
103
161
|
/**
|
|
104
162
|
* Reset a user password
|
|
105
163
|
* @param {Object} param params
|