Compare commits

..

4 Commits

Author SHA1 Message Date
Elias Schneider
fccc4cbc02 release: 0.6.1 2023-01-11 13:08:09 +01:00
Elias Schneider
f1b44f87fa fix: shareUrl uses wrong origin 2023-01-11 13:06:38 +01:00
Elias Schneider
02e41e2437 feat: delete all sessions if password was changed 2023-01-10 13:32:37 +01:00
Elias Schneider
74e8956106 fix: update password doesn't work 2023-01-10 12:29:38 +01:00
14 changed files with 104 additions and 37 deletions

View File

@@ -1,3 +1,16 @@
### [0.6.1](https://github.com/stonith404/pingvin-share/compare/v0.6.0...v0.6.1) (2023-01-11)
### Features
* delete all sessions if password was changed ([02e41e2](https://github.com/stonith404/pingvin-share/commit/02e41e243768de34de1bdc8833e83f60db530e55))
### Bug Fixes
* shareUrl uses wrong origin ([f1b44f8](https://github.com/stonith404/pingvin-share/commit/f1b44f87fa64d3b21ca92c9068cb352d0ad51bc0))
* update password doesn't work ([74e8956](https://github.com/stonith404/pingvin-share/commit/74e895610642552c98c0015d0f8347735aaed457))
## [0.6.0](https://github.com/stonith404/pingvin-share/compare/v0.5.1...v0.6.0) (2023-01-09) ## [0.6.0](https://github.com/stonith404/pingvin-share/compare/v0.5.1...v0.6.0) (2023-01-09)

View File

@@ -1,12 +1,12 @@
{ {
"name": "pingvin-share-backend", "name": "pingvin-share-backend",
"version": "0.6.0", "version": "0.6.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "pingvin-share-backend", "name": "pingvin-share-backend",
"version": "0.6.0", "version": "0.6.1",
"dependencies": { "dependencies": {
"@nestjs/common": "^9.2.1", "@nestjs/common": "^9.2.1",
"@nestjs/config": "^2.2.0", "@nestjs/config": "^2.2.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "pingvin-share-backend", "name": "pingvin-share-backend",
"version": "0.6.0", "version": "0.6.1",
"scripts": { "scripts": {
"build": "nest build", "build": "nest build",
"dev": "nest start --watch", "dev": "nest start --watch",

View File

@@ -21,6 +21,7 @@ import { AuthRegisterDTO } from "./dto/authRegister.dto";
import { AuthSignInDTO } from "./dto/authSignIn.dto"; import { AuthSignInDTO } from "./dto/authSignIn.dto";
import { AuthSignInTotpDTO } from "./dto/authSignInTotp.dto"; import { AuthSignInTotpDTO } from "./dto/authSignInTotp.dto";
import { EnableTotpDTO } from "./dto/enableTotp.dto"; import { EnableTotpDTO } from "./dto/enableTotp.dto";
import { TokenDTO } from "./dto/token.dto";
import { UpdatePasswordDTO } from "./dto/updatePassword.dto"; import { UpdatePasswordDTO } from "./dto/updatePassword.dto";
import { VerifyTotpDTO } from "./dto/verifyTotp.dto"; import { VerifyTotpDTO } from "./dto/verifyTotp.dto";
import { JwtGuard } from "./guard/jwt.guard"; import { JwtGuard } from "./guard/jwt.guard";
@@ -45,8 +46,8 @@ export class AuthController {
response = this.addTokensToResponse( response = this.addTokensToResponse(
response, response,
result.accessToken, result.refreshToken,
result.refreshToken result.accessToken
); );
return result; return result;
@@ -64,8 +65,8 @@ export class AuthController {
if (result.accessToken && result.refreshToken) { if (result.accessToken && result.refreshToken) {
response = this.addTokensToResponse( response = this.addTokensToResponse(
response, response,
result.accessToken, result.refreshToken,
result.refreshToken result.accessToken
); );
} }
@@ -83,17 +84,28 @@ export class AuthController {
response = this.addTokensToResponse( response = this.addTokensToResponse(
response, response,
result.accessToken, result.refreshToken,
result.refreshToken result.accessToken
); );
return result; return new TokenDTO().from(result);
} }
@Patch("password") @Patch("password")
@UseGuards(JwtGuard) @UseGuards(JwtGuard)
async updatePassword(@GetUser() user: User, @Body() dto: UpdatePasswordDTO) { async updatePassword(
await this.authService.updatePassword(user, dto.oldPassword, dto.password); @GetUser() user: User,
@Res({ passthrough: true }) response: Response,
@Body() dto: UpdatePasswordDTO
) {
const result = await this.authService.updatePassword(
user,
dto.oldPassword,
dto.password
);
response = this.addTokensToResponse(response, result.refreshToken);
return new TokenDTO().from(result);
} }
@Post("token") @Post("token")
@@ -108,7 +120,7 @@ export class AuthController {
request.cookies.refresh_token request.cookies.refresh_token
); );
response.cookie("access_token", accessToken); response.cookie("access_token", accessToken);
return { accessToken }; return new TokenDTO().from({ accessToken });
} }
@Post("signOut") @Post("signOut")
@@ -146,15 +158,16 @@ export class AuthController {
private addTokensToResponse( private addTokensToResponse(
response: Response, response: Response,
accessToken: string, refreshToken?: string,
refreshToken: string accessToken?: string
) { ) {
response.cookie("access_token", accessToken); if (accessToken) response.cookie("access_token", accessToken);
response.cookie("refresh_token", refreshToken, { if (refreshToken)
path: "/api/auth/token", response.cookie("refresh_token", refreshToken, {
httpOnly: true, path: "/api/auth/token",
maxAge: 1000 * 60 * 60 * 24 * 30 * 3, httpOnly: true,
}); maxAge: 1000 * 60 * 60 * 24 * 30 * 3,
});
return response; return response;
} }

View File

@@ -87,10 +87,16 @@ export class AuthService {
const hash = await argon.hash(newPassword); const hash = await argon.hash(newPassword);
this.prisma.user.update({ await this.prisma.refreshToken.deleteMany({
where: { userId: user.id },
});
await this.prisma.user.update({
where: { id: user.id }, where: { id: user.id },
data: { password: hash }, data: { password: hash },
}); });
return this.createRefreshToken(user.id);
} }
async createAccessToken(user: User, refreshTokenId: string) { async createAccessToken(user: User, refreshTokenId: string) {
@@ -112,7 +118,12 @@ export class AuthService {
refreshTokenId: string; refreshTokenId: string;
}; };
await this.prisma.refreshToken.delete({ where: { id: refreshTokenId } }); await this.prisma.refreshToken
.delete({ where: { id: refreshTokenId } })
.catch((e) => {
// Ignore error if refresh token doesn't exist
if (e.code != "P2025") throw e;
});
} }
async refreshAccessToken(refreshToken: string) { async refreshAccessToken(refreshToken: string) {

View File

@@ -0,0 +1,15 @@
import { Expose, plainToClass } from "class-transformer";
export class TokenDTO {
@Expose()
accessToken: string;
@Expose()
refreshToken: string;
from(partial: Partial<TokenDTO>) {
return plainToClass(TokenDTO, partial, {
excludeExtraneousValues: true,
});
}
}

View File

@@ -1,12 +1,12 @@
{ {
"name": "pingvin-share-frontend", "name": "pingvin-share-frontend",
"version": "0.6.0", "version": "0.6.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "pingvin-share-frontend", "name": "pingvin-share-frontend",
"version": "0.6.0", "version": "0.6.1",
"dependencies": { "dependencies": {
"@emotion/react": "^11.10.5", "@emotion/react": "^11.10.5",
"@emotion/server": "^11.10.0", "@emotion/server": "^11.10.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "pingvin-share-frontend", "name": "pingvin-share-frontend",
"version": "0.6.0", "version": "0.6.1",
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "next build",

View File

@@ -1,8 +1,8 @@
import { Stack, TextInput } from "@mantine/core"; import { Stack, TextInput } from "@mantine/core";
import { ModalsContextProps } from "@mantine/modals/lib/context"; import { ModalsContextProps } from "@mantine/modals/lib/context";
const showShareLinkModal = (modals: ModalsContextProps, shareId: string) => { const showShareLinkModal = (modals: ModalsContextProps, shareId: string, appUrl : string) => {
const link = `${window.location.origin}/share/${shareId}`; const link = `${appUrl}/share/${shareId}`;
return modals.openModal({ return modals.openModal({
title: "Share link", title: "Share link",
children: ( children: (

View File

@@ -15,7 +15,11 @@ import { TbCopy } from "react-icons/tb";
import { Share } from "../../../types/share.type"; import { Share } from "../../../types/share.type";
import toast from "../../../utils/toast.util"; import toast from "../../../utils/toast.util";
const showCompletedUploadModal = (modals: ModalsContextProps, share: Share) => { const showCompletedUploadModal = (
modals: ModalsContextProps,
share: Share,
appUrl: string
) => {
return modals.openModal({ return modals.openModal({
closeOnClickOutside: false, closeOnClickOutside: false,
withCloseButton: false, withCloseButton: false,
@@ -25,15 +29,16 @@ const showCompletedUploadModal = (modals: ModalsContextProps, share: Share) => {
<Title order={4}>Share ready</Title> <Title order={4}>Share ready</Title>
</Stack> </Stack>
), ),
children: <Body share={share} />, children: <Body share={share} appUrl={appUrl} />,
}); });
}; };
const Body = ({ share }: { share: Share }) => { const Body = ({ share, appUrl }: { share: Share; appUrl: string }) => {
const clipboard = useClipboard({ timeout: 500 }); const clipboard = useClipboard({ timeout: 500 });
const modals = useModals(); const modals = useModals();
const router = useRouter(); const router = useRouter();
const link = `${window.location.origin}/share/${share.id}`;
const link = `${appUrl}/share/${share.id}`;
return ( return (
<Stack align="stretch"> <Stack align="stretch">
<TextInput <TextInput

View File

@@ -30,6 +30,7 @@ const showCreateUploadModal = (
modals: ModalsContextProps, modals: ModalsContextProps,
options: { options: {
isUserSignedIn: boolean; isUserSignedIn: boolean;
appUrl: string;
allowUnauthenticatedShares: boolean; allowUnauthenticatedShares: boolean;
enableEmailRecepients: boolean; enableEmailRecepients: boolean;
}, },
@@ -53,6 +54,7 @@ const CreateUploadModalBody = ({
uploadCallback: (createShare: CreateShare) => void; uploadCallback: (createShare: CreateShare) => void;
options: { options: {
isUserSignedIn: boolean; isUserSignedIn: boolean;
appUrl: string;
allowUnauthenticatedShares: boolean; allowUnauthenticatedShares: boolean;
enableEmailRecepients: boolean; enableEmailRecepients: boolean;
}; };
@@ -156,7 +158,7 @@ const CreateUploadModalBody = ({
color: theme.colors.gray[6], color: theme.colors.gray[6],
})} })}
> >
{window.location.origin}/share/ {options.appUrl}/share/
{form.values.link == "" ? "myAwesomeShare" : form.values.link} {form.values.link == "" ? "myAwesomeShare" : form.values.link}
</Text> </Text>
<Grid align={form.errors.link ? "center" : "flex-end"}> <Grid align={form.errors.link ? "center" : "flex-end"}>

View File

@@ -19,6 +19,7 @@ import { useEffect, useState } from "react";
import { TbLink, TbTrash } from "react-icons/tb"; import { TbLink, TbTrash } from "react-icons/tb";
import showShareLinkModal from "../../components/account/showShareLinkModal"; import showShareLinkModal from "../../components/account/showShareLinkModal";
import Meta from "../../components/Meta"; import Meta from "../../components/Meta";
import useConfig from "../../hooks/config.hook";
import useUser from "../../hooks/user.hook"; import useUser from "../../hooks/user.hook";
import shareService from "../../services/share.service"; import shareService from "../../services/share.service";
import { MyShare } from "../../types/share.type"; import { MyShare } from "../../types/share.type";
@@ -28,6 +29,8 @@ const MyShares = () => {
const modals = useModals(); const modals = useModals();
const clipboard = useClipboard(); const clipboard = useClipboard();
const router = useRouter(); const router = useRouter();
const config = useConfig();
const { user } = useUser(); const { user } = useUser();
const [shares, setShares] = useState<MyShare[]>(); const [shares, setShares] = useState<MyShare[]>();
@@ -86,13 +89,17 @@ const MyShares = () => {
onClick={() => { onClick={() => {
if (window.isSecureContext) { if (window.isSecureContext) {
clipboard.copy( clipboard.copy(
`${window.location.origin}/share/${share.id}` `${config.get("APP_URL")}/share/${share.id}`
); );
toast.success( toast.success(
"Your link was copied to the keyboard." "Your link was copied to the keyboard."
); );
} else { } else {
showShareLinkModal(modals, share.id); showShareLinkModal(
modals,
share.id,
config.get("APP_URL")
);
} }
}} }}
> >

View File

@@ -140,7 +140,7 @@ const Upload = () => {
.completeShare(createdShare.id) .completeShare(createdShare.id)
.then(() => { .then(() => {
setisUploading(false); setisUploading(false);
showCompletedUploadModal(modals, createdShare); showCompletedUploadModal(modals, createdShare, config.get("APP_URL"));
setFiles([]); setFiles([]);
}) })
.catch(() => .catch(() =>
@@ -164,6 +164,7 @@ const Upload = () => {
modals, modals,
{ {
isUserSignedIn: user ? true : false, isUserSignedIn: user ? true : false,
appUrl: config.get("APP_URL"),
allowUnauthenticatedShares: config.get( allowUnauthenticatedShares: config.get(
"ALLOW_UNAUTHENTICATED_SHARES" "ALLOW_UNAUTHENTICATED_SHARES"
), ),

View File

@@ -1,6 +1,6 @@
{ {
"name": "pingvin-share", "name": "pingvin-share",
"version": "0.6.0", "version": "0.6.1",
"scripts": { "scripts": {
"format": "cd frontend && npm run format && cd ../backend && npm run format", "format": "cd frontend && npm run format && cd ../backend && npm run format",
"lint": "cd frontend && npm run lint && cd ../backend && npm run lint", "lint": "cd frontend && npm run lint && cd ../backend && npm run lint",