mirror of
https://github.com/swissmakers/swiss-datashare.git
synced 2026-04-17 12:43:13 +02:00
feat(share): add share ID length setting (#677)
- Add share ID length to share > settings - Use cryptographically secure RNG for IDs - Use secure default value for IDs length - Add FR and EN translation Co-authored-by: Romain Ricard <romain.ricard@mines-ales.org>
This commit is contained in:
@@ -51,6 +51,11 @@ const configVariables: ConfigVariables = {
|
|||||||
defaultValue: "0",
|
defaultValue: "0",
|
||||||
secret: false,
|
secret: false,
|
||||||
},
|
},
|
||||||
|
shareIdLength: {
|
||||||
|
type: "number",
|
||||||
|
defaultValue: "8",
|
||||||
|
secret: false,
|
||||||
|
},
|
||||||
maxSize: {
|
maxSize: {
|
||||||
type: "number",
|
type: "number",
|
||||||
defaultValue: "1000000000",
|
defaultValue: "1000000000",
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ const showCreateUploadModal = (
|
|||||||
allowUnauthenticatedShares: boolean;
|
allowUnauthenticatedShares: boolean;
|
||||||
enableEmailRecepients: boolean;
|
enableEmailRecepients: boolean;
|
||||||
maxExpirationInHours: number;
|
maxExpirationInHours: number;
|
||||||
|
shareIdLength: number;
|
||||||
simplified: boolean;
|
simplified: boolean;
|
||||||
},
|
},
|
||||||
files: FileUpload[],
|
files: FileUpload[],
|
||||||
@@ -72,18 +73,28 @@ const showCreateUploadModal = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateLink = () =>
|
const generateShareId = (length: number = 16) => {
|
||||||
Buffer.from(Math.random().toString(), "utf8")
|
const chars =
|
||||||
.toString("base64")
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
.substring(10, 17);
|
let result = "";
|
||||||
|
const randomArray = new Uint8Array(length >= 3 ? length : 3);
|
||||||
|
crypto.getRandomValues(randomArray);
|
||||||
|
randomArray.forEach((number) => {
|
||||||
|
result += chars[number % chars.length];
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
const generateAvailableLink = async (times = 10): Promise<string> => {
|
const generateAvailableLink = async (
|
||||||
|
shareIdLength: number,
|
||||||
|
times: number = 10,
|
||||||
|
): Promise<string> => {
|
||||||
if (times <= 0) {
|
if (times <= 0) {
|
||||||
throw new Error("Could not generate available link");
|
throw new Error("Could not generate available link");
|
||||||
}
|
}
|
||||||
const _link = generateLink();
|
const _link = generateShareId(shareIdLength);
|
||||||
if (!(await shareService.isShareIdAvailable(_link))) {
|
if (!(await shareService.isShareIdAvailable(_link))) {
|
||||||
return await generateAvailableLink(times - 1);
|
return await generateAvailableLink(shareIdLength, times - 1);
|
||||||
} else {
|
} else {
|
||||||
return _link;
|
return _link;
|
||||||
}
|
}
|
||||||
@@ -102,12 +113,13 @@ const CreateUploadModalBody = ({
|
|||||||
allowUnauthenticatedShares: boolean;
|
allowUnauthenticatedShares: boolean;
|
||||||
enableEmailRecepients: boolean;
|
enableEmailRecepients: boolean;
|
||||||
maxExpirationInHours: number;
|
maxExpirationInHours: number;
|
||||||
|
shareIdLength: number;
|
||||||
};
|
};
|
||||||
}) => {
|
}) => {
|
||||||
const modals = useModals();
|
const modals = useModals();
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
|
|
||||||
const generatedLink = generateLink();
|
const generatedLink = generateShareId(options.shareIdLength);
|
||||||
|
|
||||||
const [showNotSignedInAlert, setShowNotSignedInAlert] = useState(true);
|
const [showNotSignedInAlert, setShowNotSignedInAlert] = useState(true);
|
||||||
|
|
||||||
@@ -229,7 +241,12 @@ const CreateUploadModalBody = ({
|
|||||||
<Button
|
<Button
|
||||||
style={{ flex: "0 0 auto" }}
|
style={{ flex: "0 0 auto" }}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => form.setFieldValue("link", generateLink())}
|
onClick={() =>
|
||||||
|
form.setFieldValue(
|
||||||
|
"link",
|
||||||
|
generateShareId(options.shareIdLength),
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<FormattedMessage id="common.button.generate" />
|
<FormattedMessage id="common.button.generate" />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -461,6 +478,7 @@ const SimplifiedCreateUploadModalModal = ({
|
|||||||
allowUnauthenticatedShares: boolean;
|
allowUnauthenticatedShares: boolean;
|
||||||
enableEmailRecepients: boolean;
|
enableEmailRecepients: boolean;
|
||||||
maxExpirationInHours: number;
|
maxExpirationInHours: number;
|
||||||
|
shareIdLength: number;
|
||||||
};
|
};
|
||||||
}) => {
|
}) => {
|
||||||
const modals = useModals();
|
const modals = useModals();
|
||||||
@@ -485,10 +503,12 @@ const SimplifiedCreateUploadModalModal = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = form.onSubmit(async (values) => {
|
const onSubmit = form.onSubmit(async (values) => {
|
||||||
const link = await generateAvailableLink().catch(() => {
|
const link = await generateAvailableLink(options.shareIdLength).catch(
|
||||||
|
() => {
|
||||||
toast.error(t("upload.modal.link.error.taken"));
|
toast.error(t("upload.modal.link.error.taken"));
|
||||||
return undefined;
|
return undefined;
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if (!link) {
|
if (!link) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -468,6 +468,9 @@ export default {
|
|||||||
"admin.config.share.max-expiration": "Max expiration",
|
"admin.config.share.max-expiration": "Max expiration",
|
||||||
"admin.config.share.max-expiration.description":
|
"admin.config.share.max-expiration.description":
|
||||||
"Maximum share expiration in hours. Set to 0 to allow unlimited expiration.",
|
"Maximum share expiration in hours. Set to 0 to allow unlimited expiration.",
|
||||||
|
"admin.config.share.share-id-length": "Default share ID length",
|
||||||
|
"admin.config.share.share-id-length.description":
|
||||||
|
"Default length for the generated ID of a share. This value is also used to generate links for reverse shares. A value below 8 is not considered secure.",
|
||||||
"admin.config.share.max-size": "Max size",
|
"admin.config.share.max-size": "Max size",
|
||||||
"admin.config.share.max-size.description": "Maximum share size in bytes",
|
"admin.config.share.max-size.description": "Maximum share size in bytes",
|
||||||
"admin.config.share.zip-compression-level": "Zip compression level",
|
"admin.config.share.zip-compression-level": "Zip compression level",
|
||||||
|
|||||||
@@ -336,6 +336,9 @@ export default {
|
|||||||
"admin.config.share.allow-unauthenticated-shares.description": "Permet aux visiteurs de créer des partages",
|
"admin.config.share.allow-unauthenticated-shares.description": "Permet aux visiteurs de créer des partages",
|
||||||
"admin.config.share.max-expiration": "Échéance",
|
"admin.config.share.max-expiration": "Échéance",
|
||||||
"admin.config.share.max-expiration.description": "Échéance du partage en heures. Indiquez 0 pour qu’il n’expire jamais.",
|
"admin.config.share.max-expiration.description": "Échéance du partage en heures. Indiquez 0 pour qu’il n’expire jamais.",
|
||||||
|
"admin.config.share.share-id-length": "Taille de l'identifiant généré",
|
||||||
|
"admin.config.share.share-id-length.description":
|
||||||
|
"Taille par défaut de l'identifiant généré pour un partage. Cette valeur est aussi utilisée pour générer les liens des partages inverses. Une valeur inférieure à 8 n'est pas considérée sûre.",
|
||||||
"admin.config.share.max-size": "Taille max",
|
"admin.config.share.max-size": "Taille max",
|
||||||
"admin.config.share.max-size.description": "Taille maximale du partage en octets",
|
"admin.config.share.max-size.description": "Taille maximale du partage en octets",
|
||||||
"admin.config.share.zip-compression-level": "Niveau de compression",
|
"admin.config.share.zip-compression-level": "Niveau de compression",
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ const Upload = ({
|
|||||||
),
|
),
|
||||||
enableEmailRecepients: config.get("email.enableShareEmailRecipients"),
|
enableEmailRecepients: config.get("email.enableShareEmailRecipients"),
|
||||||
maxExpirationInHours: config.get("share.maxExpiration"),
|
maxExpirationInHours: config.get("share.maxExpiration"),
|
||||||
|
shareIdLength: config.get("share.shareIdLength"),
|
||||||
simplified,
|
simplified,
|
||||||
},
|
},
|
||||||
files,
|
files,
|
||||||
|
|||||||
Reference in New Issue
Block a user