Skip to content

Commit

Permalink
fix delete projet and add session protection for admin
Browse files Browse the repository at this point in the history
  • Loading branch information
younes200 committed Oct 10, 2023
1 parent 6ee0f0e commit 96185ca
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 96 deletions.
Binary file modified .yarn/install-state.gz
Binary file not shown.
4 changes: 3 additions & 1 deletion apps/admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@
"@adminjs/themes": "^1.0.1",
"@celluloid/prisma": "*",
"adminjs": "^7.2.2",
"connect-redis": "^7.0.0",
"connect-redis": "^7.1.0",
"cors": "^2.8.5",
"express": "^4.18.2",
"express-formidable": "^1.2.0",
"express-session": "^1.17.3",
"passport": "^0.6.0",
"passport-local": "^1.0.0",
"redis": "^4.6.10"
},
"devDependencies": {
Expand Down
15 changes: 13 additions & 2 deletions apps/admin/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
// import RedisStore from 'connect-redis';
import { UserRole } from '@celluloid/prisma';
import cors from "cors";
import express, { NextFunction, Request, Response } from "express";
import path from "path";
import * as url from 'url'

import passport from "./passport"
import getAdminRouter from "./server.js";
import { createSession } from './session';

const PORT = process.env.PORT || 4000

const start = async () => {
const app = express();
app.enable('trust proxy');
app.use(cors({ credentials: true, origin: true }));
app.use(createSession());
app.use(passport.authenticate("session"));


// Define the CORS middleware function
const corsMiddleware = (req: Request, res: Response, next: NextFunction) => {
Expand All @@ -32,8 +37,14 @@ const start = async () => {
rootPath: "/admin"
});

const isAuthenticated = function (req, res, next) {
if (req.user.role == UserRole.Admin)
return next();
res.redirect('/')
}


app.use('/admin', adminRouter);
app.use('/admin', isAuthenticated, adminRouter);

const __dirname = url.fileURLToPath(new URL('.', import.meta.url))

Expand Down
80 changes: 80 additions & 0 deletions apps/admin/src/passport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@

import { prisma } from "@celluloid/prisma"
import { User } from '@celluloid/prisma';
import bcrypt from 'bcryptjs';
import passport from 'passport';
import {
Strategy as LocalStrategy,
} from "passport-local";

export enum SigninStrategy {
LOGIN = "login",
TEACHER_SIGNUP = "teacher-signup",
STUDENT_SIGNUP = "student-signup",
}


passport.serializeUser((user: User, done) => {
done(null, user.id)
});

passport.deserializeUser(async (id: string, done) => {
const user = await prisma.user.findUnique({ where: { id } })
if (user) {
console.log("here", user)
return done(null, user);
} else {
console.error(
`Deserialize user failed: user with id` + ` ${id} does not exist`
);
return done(new Error("InvalidUser"));
}
});

passport.use(
new LocalStrategy(async (username: string, password: string, done) => {
const user = await prisma.user.findUnique({ where: { username: username } })
if (!user) {
return done(new Error("InvalidUser"));
}
if (!bcrypt.compareSync(password, user.password)) {
return done(new Error("InvalidUser"));
}
if (!user.confirmed && user.role !== "Student") {
return done(new Error("UserNotConfirmed"));
}
return done(null, user);

}),
);


const loginStrategy = new LocalStrategy(
{ usernameField: "login" },
async (login, password, done) => {

const user = await prisma.user.findUnique({
where: {
//OR: [{ email: login }, { username: login, }]
email: login
}
});

if (!user) {
return Promise.resolve(done(new Error("InvalidUser")));
}
if (!bcrypt.compareSync(password, user.password)) {
console.error(`Login failed for user ${user.username}: incorrect password`);
return Promise.resolve(done(new Error("InvalidUser")));
}
if (!user.confirmed && user.role !== "Student") {
console.error(`Login failed: ${user.username} is not confirmed`);
return Promise.resolve(done(new Error("UserNotConfirmed")));
}
return Promise.resolve(done(null, user));
}
);

passport.use(SigninStrategy.LOGIN, loginStrategy);

export default passport;
36 changes: 36 additions & 0 deletions apps/admin/src/session.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import RedisStore from "connect-redis"
import session from "express-session"
import { createClient } from "redis"


export function createSession() {

// Initialize client.
const redisClient = createClient({
url: process.env.REDIS_URL || "redis://localhost"
})
redisClient.connect().catch(console.error)

// Initialize store.
const redisStore = new RedisStore({
client: redisClient,
})

return session({
store: redisStore,
name: process.env.CELLULOID_COOKIE_NAME
? process.env.CELLULOID_COOKIE_NAME
: undefined,
cookie: {
domain: process.env.CELLULOID_COOKIE_DOMAIN
? process.env.CELLULOID_COOKIE_DOMAIN
: undefined,
secure: process.env.CELLULOID_COOKIE_SECURE === "true",
maxAge: 30 * 24 * 3600 * 1000,
httpOnly: true,
},
secret: process.env.CELLULOID_COOKIE_SECRET as string,
resave: false,
saveUninitialized: true,
});
}
4 changes: 1 addition & 3 deletions apps/backend/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import "./passport";

import { appRouter, createRPCContext } from '@celluloid/trpc';
import * as trpcExpress from '@trpc/server/adapters/express';
import cookieParser from 'cookie-parser';
import cors from 'cors';
import express from 'express';
import passport from 'passport';
import swaggerUi from 'swagger-ui-express';
import { createOpenApiExpressMiddleware } from 'trpc-openapi';

import { openApiDocument } from './openapi';
import passport from "./passport";
import { createSession } from './session';

const trpcApiEndpoint = '/trpc'
Expand Down
2 changes: 2 additions & 0 deletions apps/backend/src/passport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,5 @@ const loginStrategy = new LocalStrategy(
);

passport.use(SigninStrategy.LOGIN, loginStrategy);

export default passport;
175 changes: 88 additions & 87 deletions apps/frontend/src/actions/ProjectActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,15 @@ export const failUnshareProject = (project: ProjectGraphRecord) =>

export const listProjectsThunk =
() =>
(dispatch: Dispatch): AsyncAction<ProjectGraphRecord[], string> => {
return ProjectService.list()
.then((projectList) => {
return dispatch(succeedListProjects(projectList));
})
.catch((error) => {
return dispatch(failListProjects(error.message));
});
};
(dispatch: Dispatch): AsyncAction<ProjectGraphRecord[], string> => {
return ProjectService.list()
.then((projectList) => {
return dispatch(succeedListProjects(projectList));
})
.catch((error) => {
return dispatch(failListProjects(error.message));
});
};

export const loadProjectThunk = (projectId: string) => (dispatch: Dispatch) => {
return ProjectService.get(projectId)
Expand All @@ -115,99 +115,100 @@ export const loadProjectThunk = (projectId: string) => (dispatch: Dispatch) => {

export const createProjectThunk =
(data: ProjectCreateData) =>
(dispatch: Dispatch): AsyncAction<ProjectGraphRecord, string> => {
dispatch(triggerUpsertProjectLoading());
return ProjectService.create(data)
.then((project) => {
// dispatch(push(`/projects/${project.id}`));
dispatch(discardNewVideo());
return dispatch(succeedUpsertProject(project));
})
.catch((error) => {
return dispatch(failUpsertProject(error.message));
});
};
(dispatch: Dispatch): AsyncAction<ProjectGraphRecord, string> => {
dispatch(triggerUpsertProjectLoading());
return ProjectService.create(data)
.then((project) => {
// dispatch(push(`/projects/${project.id}`));
dispatch(discardNewVideo());
return dispatch(succeedUpsertProject(project));
})
.catch((error) => {
return dispatch(failUpsertProject(error.message));
});
};

export const updateProjectThunk =
(projectId: string, data: ProjectUpdateData) =>
(dispatch: Dispatch): AsyncAction<ProjectGraphRecord, string> => {
dispatch(triggerUpsertProjectLoading());
return ProjectService.update(projectId, data)
.then((project) => {
return dispatch(succeedUpsertProject(project));
})
.catch((error) => {
return dispatch(failUpsertProject(error.message));
});
};
(dispatch: Dispatch): AsyncAction<ProjectGraphRecord, string> => {
dispatch(triggerUpsertProjectLoading());
return ProjectService.update(projectId, data)
.then((project) => {
return dispatch(succeedUpsertProject(project));
})
.catch((error) => {
return dispatch(failUpsertProject(error.message));
});
};

export const deleteProjectThunk =
(projectId: string) =>
(dispatch: Dispatch): AsyncAction<string, string> => {
dispatch(triggerDeleteProjectLoading());
return ProjectService.delete(projectId)
.then(() => {
dispatch(push("/"));
return dispatch(succeedDeleteProject(projectId));
})
.catch((error) => {
return dispatch(failDeleteProject(error.message));
});
};
(dispatch: Dispatch): AsyncAction<string, string> => {
dispatch(triggerDeleteProjectLoading());
return ProjectService.delete(projectId)
.then(() => {
// dispatch(push("/"));
window.location.assign(`/`);
return dispatch(succeedDeleteProject(projectId));
})
.catch((error) => {
return dispatch(failDeleteProject(error.message));
});
};

export const unshareProjectThunk =
(projectId: string) =>
(dispatch: Dispatch): AsyncAction<ProjectGraphRecord, string> => {
dispatch(triggerUnshareProjectLoading());
return ProjectService.unshare(projectId)
.then((project) => {
return dispatch(succeedUnshareProject(project));
})
.catch((error) => {
return dispatch(failUnshareProject(error.message));
});
};
(dispatch: Dispatch): AsyncAction<ProjectGraphRecord, string> => {
dispatch(triggerUnshareProjectLoading());
return ProjectService.unshare(projectId)
.then((project) => {
return dispatch(succeedUnshareProject(project));
})
.catch((error) => {
return dispatch(failUnshareProject(error.message));
});
};

export const shareProjectThunk =
(projectId: string, data: ProjectShareData) =>
(dispatch: Dispatch): AsyncAction<ProjectGraphRecord, string> => {
dispatch(triggerShareProjectLoading());
return ProjectService.share(projectId, data)
.then((project) => {
dispatch(succeedLoadProject(project));
return dispatch(succeedShareProject(project));
})
.catch((error) => {
return dispatch(failShareProject(error.message));
});
};
(dispatch: Dispatch): AsyncAction<ProjectGraphRecord, string> => {
dispatch(triggerShareProjectLoading());
return ProjectService.share(projectId, data)
.then((project) => {
dispatch(succeedLoadProject(project));
return dispatch(succeedShareProject(project));
})
.catch((error) => {
return dispatch(failShareProject(error.message));
});
};

export const setProjectCollaborativeThunk =
(projectId: string, collaborative: boolean) =>
(dispatch: Dispatch): AsyncAction<ProjectGraphRecord, string> => {
dispatch(triggerSetProjectCollaborativeLoading());
return ProjectService.setAttribute(
projectId,
"collaborative",
collaborative
)
.then((project) => {
return dispatch(succeedSetProjectCollaborative(project));
})
.catch((error) => {
return dispatch(failSetProjectCollaborative(error.message));
});
};
(dispatch: Dispatch): AsyncAction<ProjectGraphRecord, string> => {
dispatch(triggerSetProjectCollaborativeLoading());
return ProjectService.setAttribute(
projectId,
"collaborative",
collaborative
)
.then((project) => {
return dispatch(succeedSetProjectCollaborative(project));
})
.catch((error) => {
return dispatch(failSetProjectCollaborative(error.message));
});
};

export const setProjectPublicThunk =
(projectId: string, _public: boolean) =>
(dispatch: Dispatch): AsyncAction<ProjectGraphRecord, string> => {
dispatch(triggerSetProjectPublicLoading());
return ProjectService.setAttribute(projectId, "public", _public)
.then((project) => {
return dispatch(succeedSetProjectPublic(project));
})
.catch((error) => {
return dispatch(failSetProjectPublic(error.message));
});
};
(dispatch: Dispatch): AsyncAction<ProjectGraphRecord, string> => {
dispatch(triggerSetProjectPublicLoading());
return ProjectService.setAttribute(projectId, "public", _public)
.then((project) => {
return dispatch(succeedSetProjectPublic(project));
})
.catch((error) => {
return dispatch(failSetProjectPublic(error.message));
});
};
Loading

0 comments on commit 96185ca

Please sign in to comment.