Menus.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. using Microsoft.AspNetCore.Authorization;
  2. using System.Security.Claims;
  3. namespace SharedKernel.Constants
  4. {
  5. public class Menu
  6. {
  7. public int Id { get; set; }
  8. public required string Name { get; set; }
  9. public string? Path { get; set; }
  10. public string? Icon { get; set; }
  11. public List<Menu>? Children { get; set; } = [];
  12. public bool HasChildren => Children != null && Children.Count > 0;
  13. // 이 메뉴를 볼 수 있는 역할 목록 (없으면 모두 허용)
  14. public List<string>? Roles { get; set; }
  15. // 정책 이름으로도 제한 가능하게
  16. public List<string>? Policies { get; set; }
  17. }
  18. public static class Menus
  19. {
  20. public static List<Menu> GetMenus()
  21. {
  22. return new List<Menu>
  23. {
  24. new Menu
  25. {
  26. Id = 100,
  27. Name = "상황판",
  28. Path = "/",
  29. Icon = "<i class=\"bi bi-speedometer\"></i>",
  30. Children = null,
  31. Roles = [ "Admin", "상황판" ],
  32. Policies = null
  33. },
  34. new Menu
  35. {
  36. Id = 200,
  37. Name = "환경",
  38. Path = null,
  39. Icon = "<i class=\"bi bi-gear\"></i>",
  40. Roles = [ "Admin", "환경" ],
  41. Children = new List<Menu>
  42. {
  43. new Menu
  44. {
  45. Id = 201,
  46. Name = "서버 정보",
  47. Path = "/Server/Info",
  48. Roles = [ "Admin", "환경 - 서버 정보" ],
  49. Policies = null
  50. },
  51. new Menu
  52. {
  53. Id = 202,
  54. Name = "환경변수",
  55. Path = "/Server/Env",
  56. Roles = [ "Admin", "환경 - 환경변수" ]
  57. },
  58. new Menu
  59. {
  60. Id = 203,
  61. Name = "기본 설정",
  62. Path = "/Config/Basic",
  63. Roles = [ "Admin", "환경 - 기본 설정" ]
  64. },
  65. new Menu
  66. {
  67. Id = 204,
  68. Name = "메타 태그",
  69. Path = "/Config/Meta",
  70. Roles = [ "Admin", "환경 - 메타 태그" ]
  71. },
  72. new Menu
  73. {
  74. Id = 205,
  75. Name = "회사 정보",
  76. Path = "/Config/Company",
  77. Roles = [ "Admin", "환경 - 회사 정보" ]
  78. },
  79. new Menu
  80. {
  81. Id = 206,
  82. Name = "회원 설정",
  83. Path = "/Config/Register",
  84. Roles = [ "Admin", "환경 - 회원 설정" ]
  85. },
  86. new Menu
  87. {
  88. Id = 207,
  89. Name = "알림 발송 확인",
  90. Path = "/Config/Test/Email",
  91. Roles = [ "Admin", "환경 - 알림 발송 확인" ]
  92. },
  93. new Menu
  94. {
  95. Id = 208,
  96. Name = "알림 발송 양식",
  97. Path = "/Config/Template/Email",
  98. Roles = [ "Admin", "환경 - 알림 발송 양식" ]
  99. },
  100. new Menu
  101. {
  102. Id = 209,
  103. Name = "API 설정",
  104. Path = "/Config/External",
  105. Roles = [ "Admin", "환경 - API 설정" ]
  106. }
  107. }
  108. },
  109. new Menu
  110. {
  111. Id = 250,
  112. Name = "관리자",
  113. Path = null,
  114. Icon = "<i class=\"bi bi-person-fill-gear\"></i>",
  115. Roles = [ "Admin", "관리자" ],
  116. Children = new List<Menu>
  117. {
  118. new Menu
  119. {
  120. Id = 251,
  121. Name = "회원 관리",
  122. Path = "/Director/User",
  123. Roles = [ "Admin", "관리자 회원" ]
  124. },
  125. new Menu
  126. {
  127. Id = 252,
  128. Name = "로그인 내역",
  129. Path = "/Director/LoginLog",
  130. Roles = [ "Admin", "관리자 - 로그인 내역" ]
  131. },
  132. new Menu
  133. {
  134. Id = 253,
  135. Name = "접근 기록",
  136. Path = "/Director/AccessLog",
  137. Roles = [ "Admin", "관리자 - 처리 기록" ]
  138. }
  139. }
  140. },
  141. new Menu
  142. {
  143. Id = 300,
  144. Name = "일반",
  145. Path = null,
  146. Icon = "<i class=\"bi bi-card-heading\"></i>",
  147. Roles = [ "Admin", "일반" ],
  148. Children = new List<Menu>
  149. {
  150. new Menu
  151. {
  152. Id = 301,
  153. Name = "문서 관리",
  154. Path = "/Document",
  155. Roles = [ "Admin", "일반 - 문서 관리" ]
  156. },
  157. new Menu
  158. {
  159. Id = 302,
  160. Name = "팝업 관리",
  161. Path = "/Popup",
  162. Roles = [ "Admin", "일반 - 팝업 관리" ]
  163. },
  164. new Menu
  165. {
  166. Id = 303,
  167. Name = "FAQ 관리",
  168. Path = "/Faq/List",
  169. Roles = [ "Admin", "일반 - FAQ 관리" ]
  170. },
  171. new Menu
  172. {
  173. Id = 304,
  174. Name = "배너 관리",
  175. Path = "/Banner/List",
  176. Roles = [ "Admin", "일반 - 배너 관리" ]
  177. }
  178. }
  179. },
  180. new Menu
  181. {
  182. Id = 350,
  183. Name = "코인",
  184. Path = null,
  185. Icon = "<i class=\"bi bi-currency-bitcoin\"></i>",
  186. Roles = [ "Admin", "코인" ],
  187. Children = new List<Menu>
  188. {
  189. new Menu
  190. {
  191. Id = 351,
  192. Name = "코인 목록",
  193. Path = "/Crypto/List",
  194. Roles = [ "Admin", "코인 - 코인 목록" ]
  195. },
  196. new Menu
  197. {
  198. Id = 352,
  199. Name = "카테고리",
  200. Path = "/Crypto/Category",
  201. Roles = [ "Admin", "코인 - 카테고리" ]
  202. },
  203. new Menu
  204. {
  205. Id = 353,
  206. Name = "시세 설정",
  207. Path = "/Crypto/TickerConfig",
  208. Roles = [ "Admin", "코인 - 시세 설정" ]
  209. },
  210. new Menu
  211. {
  212. Id = 354,
  213. Name = "큐레이션",
  214. Path = "/Crypto/Curation",
  215. Roles = [ "Admin", "코인 - 큐레이션" ]
  216. }
  217. }
  218. },
  219. new Menu
  220. {
  221. Id = 400,
  222. Name = "회원",
  223. Path = null,
  224. Icon = "<i class=\"bi bi-people\"></i>",
  225. Roles = [ "Admin", "회원" ],
  226. Children = new List<Menu>
  227. {
  228. new Menu
  229. {
  230. Id = 401,
  231. Name = "회원 목록",
  232. Path = "/Member/List",
  233. Roles = [ "Admin", "회원 - 회원 목록" ]
  234. },
  235. new Menu
  236. {
  237. Id = 402,
  238. Name = "회원 등급",
  239. Path = "/Member/Grade",
  240. Roles = [ "Admin", "회원 - 회원 등급" ]
  241. },
  242. new Menu
  243. {
  244. Id = 403,
  245. Name = "현재 접속자",
  246. Path = "/Member/Visitor",
  247. Roles = [ "Admin", "회원 - 현재 접속자" ]
  248. },
  249. new Menu
  250. {
  251. Id = 404,
  252. Name = "로그인 내역",
  253. Path = "/Member/Log/Login",
  254. Roles = [ "Admin", "회원 - 로그인 내역" ]
  255. },
  256. new Menu
  257. {
  258. Id = 405,
  259. Name = "이메일 변경 내역",
  260. Path = "/Member/Log/Email",
  261. Roles = [ "Admin", "회원 - 이메일 변경 내역" ]
  262. },
  263. new Menu
  264. {
  265. Id = 406,
  266. Name = "별명 변경 내역",
  267. Path = "/Member/Log/Name",
  268. Roles = [ "Admin", "회원 - 별명 변경 내역" ]
  269. },
  270. new Menu
  271. {
  272. Id = 407,
  273. Name = "한마디 변경 내역",
  274. Path = "/Member/Log/Summary",
  275. Roles = [ "Admin", "회원 - 한마디 변경 내역" ]
  276. },
  277. new Menu
  278. {
  279. Id = 408,
  280. Name = "자기소개 변경 내역",
  281. Path = "/Member/Log/Intro",
  282. Roles = [ "Admin", "회원 - 자기소개 변경 내역" ]
  283. },
  284. new Menu
  285. {
  286. Id = 409,
  287. Name = "지갑 관리",
  288. Path = "/Member/Wallet/List",
  289. Roles = [ "Admin", "회원 - 지갑 관리", "Joblepay" ]
  290. },
  291. new Menu
  292. {
  293. Id = 410,
  294. Name = "거래 장부",
  295. Path = "/Member/Wallet/Transactions",
  296. Roles = [ "Admin", "회원 - 거래 장부" ]
  297. }
  298. }
  299. },
  300. new Menu
  301. {
  302. Id = 500,
  303. Name = "게시판",
  304. Path = null,
  305. Icon = "<i class=\"bi bi-clipboard2\"></i>",
  306. Roles = [ "Admin", "게시판" ],
  307. Children = new List<Menu>
  308. {
  309. new Menu
  310. {
  311. Id = 501,
  312. Name = "분류 관리",
  313. Path = "/Forum/Board/Group",
  314. Roles = [ "Admin", "게시판 - 분류 관리" ]
  315. },
  316. new Menu
  317. {
  318. Id = 502,
  319. Name = "게시판 관리",
  320. Path = "/Forum/Board/List",
  321. Roles = [ "Admin", "게시판 - 게시판 관리" ]
  322. },
  323. new Menu
  324. {
  325. Id = 503,
  326. Name = "게시물 관리",
  327. Path = "/Forum/Posts/List",
  328. Roles = [ "Admin", "게시판 - 게시물 관리" ]
  329. },
  330. new Menu
  331. {
  332. Id = 504,
  333. Name = "휴지통",
  334. Path = "/Forum/Trash/Post",
  335. Roles = [ "Admin", "게시판 - 휴지통" ]
  336. },
  337. new Menu
  338. {
  339. Id = 505,
  340. Name = "첨부파일",
  341. Path = "/Forum/Attachments/PostFile",
  342. Roles = [ "Admin", "게시판 - 첨부파일" ]
  343. },
  344. new Menu
  345. {
  346. Id = 506,
  347. Name = "이미지",
  348. Path = "/Forum/Attachments/PostImage",
  349. Roles = [ "Admin", "게시판 - 이미지" ]
  350. },
  351. new Menu
  352. {
  353. Id = 507,
  354. Name = "반응 관리",
  355. Path = "/Forum/Reactions/Post",
  356. Roles = [ "Admin", "게시판 - 반응 관리" ]
  357. },
  358. new Menu
  359. {
  360. Id = 508,
  361. Name = "신고 관리",
  362. Path = "/Forum/Reports/Post",
  363. Roles = [ "Admin", "게시판 - 신고 관리" ]
  364. }
  365. }
  366. },
  367. new Menu
  368. {
  369. Id = 600,
  370. Name = "댓글",
  371. Path = null,
  372. Icon = "<i class=\"bi bi-chat\"></i>",
  373. Roles = [ "Admin", "댓글" ],
  374. Children = new List<Menu>
  375. {
  376. new Menu
  377. {
  378. Id = 601,
  379. Name = "댓글 관리",
  380. Path = "/Forum/Comments/List",
  381. Roles = [ "Admin", "댓글 - 댓글 관리" ]
  382. },
  383. new Menu
  384. {
  385. Id = 602,
  386. Name = "휴지통",
  387. Path = "/Forum/Trash/Comment",
  388. Roles = [ "Admin", "댓글 - 휴지통" ]
  389. },
  390. new Menu
  391. {
  392. Id = 603,
  393. Name = "첨부파일",
  394. Path = "/Forum/Attachments/CommentFile",
  395. Roles = [ "Admin", "댓글 - 첨부파일" ]
  396. },
  397. new Menu
  398. {
  399. Id = 604,
  400. Name = "이미지",
  401. Path = "/Forum/Attachments/CommentImage",
  402. Roles = [ "Admin", "댓글 - 이미지" ]
  403. },
  404. new Menu
  405. {
  406. Id = 605,
  407. Name = "반응 관리",
  408. Path = "/Forum/Reactions/Comment",
  409. Roles = [ "Admin", "댓글 - 반응 관리" ]
  410. },
  411. new Menu
  412. {
  413. Id = 606,
  414. Name = "신고 관리",
  415. Path = "/Forum/Reports/Comment",
  416. Roles = [ "Admin", "댓글 - 신고 관리" ]
  417. }
  418. }
  419. },
  420. new Menu
  421. {
  422. Id = 700,
  423. Name = "채널",
  424. Path = "/Channel/List",
  425. Icon = "<i class=\"bi bi-person-lines-fill\"></i>",
  426. Children = null,
  427. Roles = [ "Admin", "채널", "채널 - 채널 목록" ]
  428. },
  429. new Menu
  430. {
  431. Id = 800,
  432. Name = "결제",
  433. Path = null,
  434. Icon = "<i class=\"bi bi-wallet2\"></i>",
  435. Roles = [ "Admin", "결제" ],
  436. Children = new List<Menu>
  437. {
  438. new Menu
  439. {
  440. Id = 801,
  441. Name = "다날 결제 내역",
  442. Path = "/Payments/Danal/Confirm",
  443. Roles = [ "Admin", "결제 - 다날 결제 내역" ]
  444. },
  445. new Menu
  446. {
  447. Id = 802,
  448. Name = "다날 취소 내역",
  449. Path = "/Payments/Danal/Cancel",
  450. Roles = [ "Admin", "결제 - 다날 취소 내역" ]
  451. },
  452. new Menu
  453. {
  454. Id = 803,
  455. Name = "다날 오류 내역",
  456. Path = "/Payments/Danal/Error",
  457. Roles = [ "Admin", "결제 - 다날 오류 내역" ]
  458. }
  459. }
  460. },
  461. /*new Menu
  462. {
  463. Id = 900,
  464. Name = "후원",
  465. Path = null,
  466. Icon = "<i class=\"bi bi-currency-exchange\"></i>",
  467. Roles = [ "Admin", "후원" ],
  468. Children = new List<Menu>
  469. {
  470. new Menu
  471. {
  472. Id = 901,
  473. Name = "후원 내역",
  474. Path = "/Donation/List",
  475. Roles = [ "Admin", "후원 - 후원 내역" ]
  476. },
  477. new Menu
  478. {
  479. Id = 902,
  480. Name = "후원 알림",
  481. Path = "/Donation/Alert",
  482. Roles = [ "Admin", "후원 - 후원 알림" ]
  483. }
  484. }
  485. },*/
  486. /*new Menu
  487. {
  488. Id = 1000,
  489. Name = "정산",
  490. Path = null,
  491. Icon = "<i class=\"bi bi-piggy-bank\"></i>",
  492. Roles = [ "Admin", "정산" ],
  493. Children = new List<Menu>
  494. {
  495. new Menu
  496. {
  497. Id = 1001,
  498. Name = "매출 관리",
  499. Path = "/Payout/Sales",
  500. Roles = [ "Admin", "정산 - 매출 관리" ]
  501. },
  502. new Menu
  503. {
  504. Id = 1002,
  505. Name = "통계",
  506. Path = "/Payout/Statistics",
  507. Roles = [ "Admin", "정산 - 통계" ]
  508. },
  509. new Menu
  510. {
  511. Id = 1003,
  512. Name = "회원 정산 내역",
  513. Path = "/Payout/Settlement/List",
  514. Roles = [ "Admin", "정산 - 회원 정산 내역" ]
  515. }
  516. }
  517. }*/
  518. };
  519. }
  520. public static async Task<List<Menu>> FilterForUserAsync(ClaimsPrincipal user, IAuthorizationService authorizationService)
  521. {
  522. var menus = GetMenus();
  523. var result = new List<Menu>();
  524. foreach (var menu in menus)
  525. {
  526. var filtered = await FilterMenuRecursiveAsync(menu, user, authorizationService);
  527. if (filtered != null)
  528. {
  529. result.Add(filtered);
  530. }
  531. }
  532. return result;
  533. }
  534. private static async Task<Menu?> FilterMenuRecursiveAsync(Menu menu, ClaimsPrincipal user, IAuthorizationService authorizationService)
  535. {
  536. // 현재 메뉴에 대한 접근 권한 체크
  537. if (!await IsMenuAllowedAsync(menu, user, authorizationService))
  538. {
  539. // 자식이 있으면, 자식은 볼 수 있지만 부모는 숨기고 싶을 수도 있음
  540. if (menu.Children is { Count: > 0 })
  541. {
  542. var allowedChildren = new List<Menu>();
  543. foreach (var child in menu.Children)
  544. {
  545. var filteredChild = await FilterMenuRecursiveAsync(child, user, authorizationService);
  546. if (filteredChild != null)
  547. {
  548. allowedChildren.Add(filteredChild);
  549. }
  550. }
  551. if (allowedChildren.Count == 0)
  552. {
  553. return null;
  554. }
  555. return new Menu
  556. {
  557. Id = menu.Id,
  558. Name = menu.Name,
  559. Path = menu.Path,
  560. Icon = menu.Icon,
  561. Roles = menu.Roles,
  562. Policies = menu.Policies,
  563. Children = allowedChildren
  564. };
  565. }
  566. return null;
  567. }
  568. // 부모는 허용되지만, 자식 중 제한이 있을 수도 있으니
  569. List<Menu>? filteredChildren = null;
  570. if (menu.Children is { Count: > 0 })
  571. {
  572. filteredChildren = new List<Menu>();
  573. foreach (var child in menu.Children)
  574. {
  575. var filteredChild = await FilterMenuRecursiveAsync(child, user, authorizationService);
  576. if (filteredChild != null)
  577. {
  578. filteredChildren.Add(filteredChild);
  579. }
  580. }
  581. if (filteredChildren.Count == 0)
  582. {
  583. filteredChildren = null;
  584. }
  585. }
  586. return new Menu
  587. {
  588. Id = menu.Id,
  589. Name = menu.Name,
  590. Path = menu.Path,
  591. Icon = menu.Icon,
  592. Roles = menu.Roles,
  593. Policies = menu.Policies,
  594. Children = filteredChildren
  595. };
  596. }
  597. private static async Task<bool> IsMenuAllowedAsync(Menu menu, ClaimsPrincipal user, IAuthorizationService authorizationService)
  598. {
  599. // 로그인 안 한 경우: 익명에게도 보여줄 메뉴만 허용하고 싶으면 분기 추가
  600. if (!user.Identity?.IsAuthenticated ?? true)
  601. {
  602. // 예: 익명은 아무 메뉴도 못 본다고 가정
  603. return false;
  604. }
  605. // Roles 지정되어 있으면 Role 기반 체크
  606. if (menu.Roles is { Count: > 0 })
  607. {
  608. // 하나라도 만족하면 허용
  609. foreach (var role in menu.Roles)
  610. {
  611. if (user.IsInRole(role))
  612. {
  613. return true;
  614. }
  615. }
  616. return false;
  617. }
  618. // Policies 지정되어 있으면 Policy 기반 체크
  619. if (menu.Policies is { Count: > 0 })
  620. {
  621. foreach (var policy in menu.Policies)
  622. {
  623. var authResult = await authorizationService.AuthorizeAsync(user, policy);
  624. if (authResult.Succeeded)
  625. {
  626. return true;
  627. }
  628. }
  629. return false;
  630. }
  631. // 역할/정책 제한이 없으면 모두 허용
  632. return true;
  633. }
  634. }
  635. }