auto commit

This commit is contained in:
bangdk 2024-11-15 15:26:23 +09:00
parent 35e2a31442
commit f03a9209ea
4 changed files with 165 additions and 22 deletions

View File

@ -34,4 +34,33 @@ router.post("/logout", authMiddleware, async (req, res, next) => {
}
});
router.post(
"/edge-login",
[
body("username").notEmpty().withMessage("Username is required"),
body("password").notEmpty().withMessage("Password is required"),
body("businessNumber")
.notEmpty()
.withMessage("Business number is required")
.matches(/^[0-9-]{10,20}$/)
.withMessage("Invalid business number format"),
validate,
],
async (req, res, next) => {
try {
const { username, password, businessNumber } = req.body;
const result = await authService.edgeLogin(
username,
password,
businessNumber,
req.ip,
req.headers["user-agent"]
);
res.json(result);
} catch (error) {
next(error);
}
}
);
module.exports = router;

View File

@ -79,6 +79,111 @@ class AuthService {
};
}
async edgeLogin(username, password, businessNumber, ipAddress, userAgent) {
// 1. 먼저 회사 검증
const company = await Company.findOne({
where: {
businessNumber,
isActive: true,
},
});
if (!company) {
throw new Error("Invalid business number or company not found");
}
// 2. 사용자 검증
const userWithPassword = await User.findOne({
where: {
username,
companyId: company.id,
isActive: true,
},
attributes: ["id", "password", "isActive"],
});
if (!userWithPassword) {
await this._logAuthAttempt(
null,
"edge_failed_login",
ipAddress,
userAgent,
businessNumber
);
throw new Error("Invalid credentials");
}
// 3. 비밀번호 검증
const isValidPassword = await userWithPassword.validatePassword(password);
if (!isValidPassword) {
await this._logAuthAttempt(
userWithPassword.id,
"edge_failed_login",
ipAddress,
userAgent,
businessNumber
);
throw new Error("Invalid credentials");
}
// 4. 사용자 정보 조회 (상세 정보)
const user = await User.findOne({
where: { id: userWithPassword.id },
include: [
{
model: Company,
attributes: ["id", "name", "businessNumber"],
where: { isActive: true }, // 활성화된 회사만
},
{
model: Branch,
attributes: ["id", "name"],
},
{
model: Role,
through: { attributes: [] },
attributes: ["id", "name", "permissions"],
required: false,
},
],
attributes: {
exclude: ["password"],
},
});
// 5. 마지막 로그인 시간 업데이트
await User.update({ lastLoginAt: new Date() }, { where: { id: user.id } });
// 6. 로그인 기록
await this._logAuthAttempt(
user.id,
"edge_login",
ipAddress,
userAgent,
businessNumber
);
// 7. 권한 정보 처리
const permissions = this._processPermissions(user.Roles || []);
// 8. 응답 데이터 구성
const userData = user.toJSON();
delete userData.Roles;
const userInfo = {
...userData,
permissions,
isEdgeLogin: true, // Edge 로그인 여부 표시
};
const token = this._generateToken(userInfo);
return {
token,
user: userInfo,
};
}
_processPermissions(roles) {
// 기본 권한 설정 (모든 사용자가 가져야 할 기본 권한)
const permissions = {
@ -101,7 +206,13 @@ class AuthService {
return true;
}
async _logAuthAttempt(userId, action, ipAddress, userAgent) {
async _logAuthAttempt(
userId,
action,
ipAddress,
userAgent,
businessNumber = null
) {
if (userId) {
// userId가 있을 때만 로그 생성
await AuthLog.create({
@ -109,6 +220,7 @@ class AuthService {
action,
ipAddress,
userAgent,
metadata: businessNumber ? { businessNumber } : null,
});
}
}

View File

@ -8,6 +8,7 @@ const AdminLayout = ({ children }: { children: React.ReactNode }) => {
return (
<AdminGuard>
<div className="h-screen flex">
{/* 왼쪽 사이드바 */}
<aside className="w-64 h-screen flex-shrink-0 bg-gray-800">
<SideNav />
</aside>

View File

@ -1,7 +1,8 @@
// src/middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { decodeToken, hasPermission, hasAnyPermission } from "@/lib/jwt";
// import { decodeToken, hasPermission, hasAnyPermission } from "@/lib/jwt";
import { decodeToken, hasPermission } from "@/lib/jwt";
export function middleware(request: NextRequest) {
const token = request.cookies.get("token")?.value;
@ -37,29 +38,29 @@ export function middleware(request: NextRequest) {
}
}
// 사용자 관리 페이지
if (request.nextUrl.pathname.startsWith("/users")) {
const hasUserManageAccess = hasAnyPermission(tokenData, [
"users:manage",
"users:view",
]);
// // 사용자 관리 페이지
// if (request.nextUrl.pathname.startsWith("/users")) {
// const hasUserManageAccess = hasAnyPermission(tokenData, [
// "users:manage",
// "users:view",
// ]);
if (!hasUserManageAccess) {
return NextResponse.redirect(new URL("/dashboard/overview", request.url));
}
}
// if (!hasUserManageAccess) {
// return NextResponse.redirect(new URL("/dashboard/overview", request.url));
// }
// }
// 부서 관리 페이지
if (request.nextUrl.pathname.startsWith("/departments")) {
const hasDepartmentAccess = hasAnyPermission(tokenData, [
"departments:manage",
"departments:view",
]);
// // 부서 관리 페이지
// if (request.nextUrl.pathname.startsWith("/departments")) {
// const hasDepartmentAccess = hasAnyPermission(tokenData, [
// "departments:manage",
// "departments:view",
// ]);
if (!hasDepartmentAccess) {
return NextResponse.redirect(new URL("/dashboard/overview", request.url));
}
}
// if (!hasDepartmentAccess) {
// return NextResponse.redirect(new URL("/dashboard/overview", request.url));
// }
// }
// 응답 헤더에 사용자 권한 정보 추가 (옵션)
const response = NextResponse.next();