add screenshots and minor fixes
|
@ -1231,7 +1231,7 @@
|
||||||
"securityKeyRemoveSuccess": "Security key removed successfully",
|
"securityKeyRemoveSuccess": "Security key removed successfully",
|
||||||
"securityKeyRemoveError": "Failed to remove security key",
|
"securityKeyRemoveError": "Failed to remove security key",
|
||||||
"securityKeyLoadError": "Failed to load security keys",
|
"securityKeyLoadError": "Failed to load security keys",
|
||||||
"securityKeyLogin": "Sign in with security key",
|
"securityKeyLogin": "Continue with security key",
|
||||||
"securityKeyAuthError": "Failed to authenticate with security key",
|
"securityKeyAuthError": "Failed to authenticate with security key",
|
||||||
"securityKeyRecommendation": "Register a backup security key on another device to ensure you always have access to your account.",
|
"securityKeyRecommendation": "Register a backup security key on another device to ensure you always have access to your account.",
|
||||||
"registering": "Registering...",
|
"registering": "Registering...",
|
||||||
|
|
Before Width: | Height: | Size: 574 KiB |
BIN
public/screenshots/create-api-key.png
Normal file
After Width: | Height: | Size: 748 KiB |
BIN
public/screenshots/create-idp.png
Normal file
After Width: | Height: | Size: 688 KiB |
BIN
public/screenshots/create-resource.png
Normal file
After Width: | Height: | Size: 687 KiB |
BIN
public/screenshots/create-share-link.png
Normal file
After Width: | Height: | Size: 669 KiB |
BIN
public/screenshots/create-site.png
Normal file
After Width: | Height: | Size: 713 KiB |
BIN
public/screenshots/edit-resource.png
Normal file
After Width: | Height: | Size: 636 KiB |
Before Width: | Height: | Size: 434 KiB After Width: | Height: | Size: 713 KiB |
BIN
public/screenshots/resource-auth.png
Normal file
After Width: | Height: | Size: 356 KiB |
BIN
public/screenshots/resource-authentication.png
Normal file
After Width: | Height: | Size: 707 KiB |
BIN
public/screenshots/resources.png
Normal file
After Width: | Height: | Size: 713 KiB |
BIN
public/screenshots/roles.png
Normal file
After Width: | Height: | Size: 556 KiB |
BIN
public/screenshots/site-online.png
Normal file
After Width: | Height: | Size: 585 KiB |
BIN
public/screenshots/sites.png
Normal file
After Width: | Height: | Size: 674 KiB |
BIN
public/screenshots/users.png
Normal file
After Width: | Height: | Size: 597 KiB |
|
@ -815,7 +815,7 @@ authRouter.post(
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) => `login:${req.body.email}`,
|
keyGenerator: (req) => `login:${req.body.email || req.ip}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only log in ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only log in ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
|
@ -830,7 +830,7 @@ authRouter.post(
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 900,
|
max: 900,
|
||||||
keyGenerator: (req) => `newtGetToken:${req.body.newtId}`,
|
keyGenerator: (req) => `newtGetToken:${req.body.newtId || req.ip}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only request a Newt token ${900} times every ${15} minutes. Please try again later.`;
|
const message = `You can only request a Newt token ${900} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
|
@ -844,7 +844,7 @@ authRouter.post(
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 900,
|
max: 900,
|
||||||
keyGenerator: (req) => `newtGetToken:${req.body.newtId}`,
|
keyGenerator: (req) => `newtGetToken:${req.body.newtId || req.ip}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only request an Olm token ${900} times every ${15} minutes. Please try again later.`;
|
const message = `You can only request an Olm token ${900} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
|
@ -860,13 +860,7 @@ authRouter.post(
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) => {
|
keyGenerator: (req) => {
|
||||||
// user is authenticated, so we can use their userId;
|
return `signup:${req.body.email || req.user?.userId || req.ip}`;
|
||||||
// otherwise, they provide the email
|
|
||||||
if (req.body.email) {
|
|
||||||
return `signup:${req.body.email}`;
|
|
||||||
} else {
|
|
||||||
return `signup:${req.user!.userId}`;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only enable 2FA ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only enable 2FA ${15} times every ${15} minutes. Please try again later.`;
|
||||||
|
@ -882,13 +876,7 @@ authRouter.post(
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) => {
|
keyGenerator: (req) => {
|
||||||
// user is authenticated, so we can use their userId;
|
return `signup:${req.body.email || req.user?.userId || req.ip}`;
|
||||||
// otherwise, they provide the email
|
|
||||||
if (req.body.email) {
|
|
||||||
return `signup:${req.body.email}`;
|
|
||||||
} else {
|
|
||||||
return `signup:${req.user!.userId}`;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only request a 2FA code ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only request a 2FA code ${15} times every ${15} minutes. Please try again later.`;
|
||||||
|
@ -896,7 +884,6 @@ authRouter.post(
|
||||||
},
|
},
|
||||||
store: createStore()
|
store: createStore()
|
||||||
}),
|
}),
|
||||||
|
|
||||||
auth.requestTotpSecret
|
auth.requestTotpSecret
|
||||||
);
|
);
|
||||||
authRouter.post(
|
authRouter.post(
|
||||||
|
@ -905,7 +892,7 @@ authRouter.post(
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) => `signup:${req.user!.userId}`,
|
keyGenerator: (req) => `signup:${req.user?.userId || req.ip}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only disable 2FA ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only disable 2FA ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
|
@ -919,7 +906,7 @@ authRouter.post(
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) => `signup:${req.body.email}`,
|
keyGenerator: (req) => `signup:${req.body.email || req.ip}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only sign up ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only sign up ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
|
@ -936,7 +923,7 @@ authRouter.post(
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) => `requestEmailVerificationCode:${req.body.email}`,
|
keyGenerator: (req) => `requestEmailVerificationCode:${req.body.email || req.ip}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only request an email verification code ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only request an email verification code ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
|
@ -957,7 +944,7 @@ authRouter.post(
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) => `requestPasswordReset:${req.body.email}`,
|
keyGenerator: (req) => `requestPasswordReset:${req.body.email || req.ip}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only request a password reset ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only request a password reset ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
|
@ -972,7 +959,7 @@ authRouter.post(
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) => `resetPassword:${req.body.email}`,
|
keyGenerator: (req) => `resetPassword:${req.body.email || req.ip}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only request a password reset ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only request a password reset ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
|
@ -988,7 +975,7 @@ authRouter.post(
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) =>
|
keyGenerator: (req) =>
|
||||||
`authWithPassword:${req.ip}:${req.params.resourceId}`,
|
`authWithPassword:${req.ip}:${req.params.resourceId || req.ip}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only authenticate with password ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only authenticate with password ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
|
@ -1003,7 +990,7 @@ authRouter.post(
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) =>
|
keyGenerator: (req) =>
|
||||||
`authWithPincode:${req.ip}:${req.params.resourceId}`,
|
`authWithPincode:${req.ip}:${req.params.resourceId || req.ip}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only authenticate with pincode ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only authenticate with pincode ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
|
@ -1050,7 +1037,7 @@ authRouter.post(
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||||
max: 5, // Allow 5 security key registrations per 15 minutes
|
max: 5, // Allow 5 security key registrations per 15 minutes
|
||||||
keyGenerator: (req) => `securityKeyRegister:${req.user!.userId}`,
|
keyGenerator: (req) => `securityKeyRegister:${req.user?.userId || req.ip}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only register a security key ${5} times every ${15} minutes. Please try again later.`;
|
const message = `You can only register a security key ${5} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
|
@ -1070,11 +1057,7 @@ authRouter.post(
|
||||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||||
max: 10, // Allow 10 authentication attempts per 15 minutes per IP
|
max: 10, // Allow 10 authentication attempts per 15 minutes per IP
|
||||||
keyGenerator: (req) => {
|
keyGenerator: (req) => {
|
||||||
if (req.body.email) {
|
return `securityKeyAuth:${req.body.email || req.ip}`;
|
||||||
return `securityKeyAuth:${req.body.email}`;
|
|
||||||
} else {
|
|
||||||
return `securityKeyAuth:${req.ip}`;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only attempt security key authentication ${10} times every ${15} minutes. Please try again later.`;
|
const message = `You can only attempt security key authentication ${10} times every ${15} minutes. Please try again later.`;
|
||||||
|
@ -1096,7 +1079,7 @@ authRouter.delete(
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||||
max: 20, // Allow 10 authentication attempts per 15 minutes per IP
|
max: 20, // Allow 10 authentication attempts per 15 minutes per IP
|
||||||
keyGenerator: (req) => `securityKeyAuth:${req.user!.userId}`,
|
keyGenerator: (req) => `securityKeyAuth:${req.user?.userId || req.ip}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only delete a security key ${10} times every ${15} minutes. Please try again later.`;
|
const message = `You can only delete a security key ${10} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
|
|
|
@ -291,6 +291,19 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) {
|
||||||
<div>
|
<div>
|
||||||
{!accessDenied ? (
|
{!accessDenied ? (
|
||||||
<div>
|
<div>
|
||||||
|
<div className="text-center mb-2">
|
||||||
|
<span className="text-sm text-muted-foreground">
|
||||||
|
{t("poweredBy")}{" "}
|
||||||
|
<Link
|
||||||
|
href="https://github.com/fosrl/pangolin"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
Pangolin
|
||||||
|
</Link>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>{getTitle()}</CardTitle>
|
<CardTitle>{getTitle()}</CardTitle>
|
||||||
|
|