Django-guardian: ์—ญํ•  ์ถ”๊ฐ€

์— ๋งŒ๋“  2011๋…„ 01์›” 16์ผ  ยท  14์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: django-guardian/django-guardian

์šฐ์„ , ์ฒ˜์Œ๋ถ€ํ„ฐ ์—ญํ• ์„ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์€ ์ด์œ ๋ฅผ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํžˆ ๋งํ•ด์„œ Django์˜ ์ •์ฑ…์€ ๊ฐ„๋‹จํ•˜์ง€๋งŒ ์ž˜ ๋งŒ๋“ค์–ด์ง„ ๋ฐฐํ„ฐ๋ฆฌ๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. django.contrib.auth๊ฐ€ ๊ฐ€์žฅ ์ข‹์€ ์˜ˆ์ž…๋‹ˆ๋‹ค. ์‚ฌ๋žŒ๋“ค์ด ์ž์ฒด ๊ตฌํ˜„์œผ๋กœ ์ธ์ฆ์„ ๋ช‡ ๋ฒˆ์ด๋‚˜ ๊ตํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ? ๋‚˜๋Š” ๊ทธ๋ ‡๊ฒŒ ๋งŽ์ง€ ์•Š๋‹ค๊ณ  ๋ฏฟ์Šต๋‹ˆ๋‹ค. ๊ฐ€๋””์–ธ์€ ์ œ์ •์‹ ์ด๊ณ  ๊ฐ„๋‹จํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ธ์ฆ ์•ฑ์— ๋Œ€ํ•œ ์ผ์ข…์˜ "ํ™•์žฅ"์„ ์œ„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ๊ฒŒ ๋‹ค์•ผ. ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์ด ๋งŽ์ด ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค(์ผ๋ถ€ ๋ฐ”๋กœ ๊ฐ€๊ธฐ ๊ธฐ๋Šฅ ์ œ์™ธ - "์ •์ƒ" ๋ถ€๋ถ„ ์ฐธ์กฐ).

์ด์ œ ์—ญํ• ์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ? ํ•ญ์ƒ ํ•„์š”ํ•œ ๋ชจ๋“  ๊ฒƒ? ์‚ฌ์‹ค, ๋‚˜๋Š” ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๊ณ  ๋ฏฟ๋Š”๋‹ค. ๊ทธ๋ฃน๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ๋Œ€๋ถ€๋ถ„์˜ ๋ชจ๋“  ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ์—”ํ„ฐํ‹ฐ์ด๋ฏ€๋กœ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ž, ์ด์ œ ๊ฑฐ์˜ v1.0์ด ๋‚˜์™”์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ v1.1์˜ ๊ฒฝ์šฐ ์—ญํ•  ์ง€์›์ด ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ธฐ๋Šฅ์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜๋„ ์ œ ์ƒ๊ฐ์—๋Š” ์–ด๋–ค ์‹์œผ๋กœ๋“  "์„ ํƒ์ "์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

API change Enhancement

๋ชจ๋“  14 ๋Œ“๊ธ€

+1 ํ•˜์ง€๋งŒ ์—ญํ• ์€ django๋กœ ์ž‘์„ฑ๋œ ๋Œ€๋ถ€๋ถ„์˜ ์•ฑ์—์„œ ๊ฑฐ์˜ ํ•„์š”ํ•˜์ง€ ์•Š์€ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
๊ฐ€๋” ์šฐ๋ฆฌ ๊ฐ™์€ ์‚ฌ๋žŒ์ด django๊ฐ€ ์‹ค์ œ๋กœ ๋งŒ๋“ค์–ด์ง€์ง€ ์•Š์€ ์•ฑ(์‚ฌ์šฉ์ž ์ง€์ • ๋ฌธ์„œ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ)์„ ๊ตฌํ˜„ํ•˜๋ ค๊ณ  ํ•˜๊ณ , ํŒŒ๋งˆ๊ฐ€ ๋งŽ์„ ๋•Œ ์—ญํ• ์— ๋Œ€ํ•ด ์ƒ๊ฐํ•˜๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์—ญํ• ๊ณผ ์ƒ์† v1.1๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š” ๋‘ ๊ฐ€์ง€

์˜ต์…˜ ๋ถ€๋ถ„์€ ์‰ฝ์Šต๋‹ˆ๋‹ค. django๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๊ทธ๋ฃน์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์‚ฌ๋žŒ๋“ค์ด ์—ญํ• ์„ ์ •์˜ํ•˜๋„๋ก ๋งŒ๋“ค์ง€ ๋งˆ์‹ญ์‹œ์˜ค.

thx ์•ฑ์šฉ btw.
๊ธˆ์—ฐ ๊ฑด๊ฐ• ์ฆ์ง„ ํ˜‘ํšŒ

๊ตฌํ˜„์— ๋”ฐ๋ผ ์—ญํ• ์— ๋Œ€ํ•ด +1.

์—ญํ•  ๋ฐ ์ƒ์†์— ๋Œ€ํ•ด +1

๋‚˜๋Š” ์—ญํ• ์— ๋Œ€ํ•ด "๊ถŒํ•œ ๊ทธ๋ฃน"์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋‹น์‹ ์—๊ฒŒ๋„ ๋™์ผํ•œ์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ์—ญํ•  ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋งŽ์€ ๊ถŒํ•œ์„ ๋” ์‰ฝ๊ฒŒ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•œ ์—ญํ• ์„ ๋‹ค๋ฅธ ์—ญํ• ๋กœ ๋นผ๊ฑฐ๋‚˜ ๊ต์ฐจ์  ๋“ฑ์„ ์–ป๊ธฐ ์œ„ํ•ด Python ์„ธํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๊ฒƒ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

http://docs.python.org/library/stdtypes.html#set -types-set-frozenset
http://en.wikibooks.org/wiki/Python_Programming/Sets

์—ญํ• , ์ƒ์† ๋ฐ ์„ ํƒ ์‚ฌํ•ญ์— ๋Œ€ํ•ด +1

์•ˆ๋…•ํ•˜์„ธ์š” ์—ฌ๋Ÿฌ๋ถ„,
์ €๋Š” ์—ญํ•  ๊ธฐ๋ฐ˜ ๊ฐœ์ฒด ๊ถŒํ•œ์ด ํ•„์š”ํ•˜๊ฑฐ๋‚˜ ๊ฐ–๊ณ  ์‹ถ์€ ์‚ฌ๋žŒ ์ค‘ ํ•œ ๋ช…์ž…๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด ํ† ๋ก ์—์„œ ์•„์ฃผ ์ข‹์€ ์ด์œ ๋กœ django-guardian์˜ ์šฐ์„  ์ˆœ์œ„๊ฐ€ ์•„๋‹˜์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‚˜๋Š” ๋‚˜๋งŒ์˜ ์•ฑ์„ ๋งŒ๋“ค ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ django-guardian์˜ ๋Œ€๋ถ€๋ถ„์˜ ๊ธฐ๋Šฅ๊ณผ ๊ตฌ์„ฑ ์š”์†Œ๋Š” ๊ดœ์ฐฎ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉฐ ๊ฐ€๋Šฅํ•˜๋ฉด ๋ฐ”ํ€ด๋ฅผ ๋‹ค์‹œ ๋ฐœ๋ช…ํ•˜์ง€ ์•Š๊ณ  ์žฌ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ œ ์งˆ๋ฌธ์€ django-guarding์„ ์กฐ๊ธˆ ๋” ์—ฐ๊ฒฐ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค ์˜ํ–ฅ์ด ์žˆ๋Š”์ง€์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด(์ฝ”๋“œ๋ฅผ ์ง„ํ–‰ํ•˜๋Š” ๋™์•ˆ ๋‚ด ๋งˆ์Œ์— ๊ฐ€์žฅ ๋จผ์ € ๋– ์˜ค๋ฅธ ๊ฒƒ): ObjectPermissionChecker์— ๋Œ€ํ•œ ์„ค์ •์„ ์ง€์ •ํ•˜๋ฉด ๋‹ค๋ฅธ ์•ฑ์ด Checker ํด๋ž˜์Šค๋ฅผ ์—ญํ• ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์—ฌ๋Ÿฌ๋ถ„์€ ์–ด๋–ป๊ฒŒ ์ƒ๊ฐํ•˜์„ธ์š”?

๊ฐ์‚ฌ ํ•ด์š”
์œ„๋ฅด๊ฒ

Hej @schacki , ๊ทธ๊ฒƒ์€ ์‹ค์ œ๋กœ ๊ฝค ์ข‹์€ ์ƒ๊ฐ์ž…๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋Š” ๊ธฐ๋ณธ์ด ์•„๋‹Œ ๊ฒ€์‚ฌ๊ธฐ ํด๋ž˜์Šค์— ๋Œ€ํ•ด ์‹คํŒจํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์ง€๋งŒ ์ด๋ฅผ ํ‘œ์‹œํ•˜๊ฑฐ๋‚˜ ๊ธฐ๋ณธ ๊ฒ€์‚ฌ๊ธฐ๋กœ๋งŒ ์‹คํ–‰๋˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠน์ • ์ž‘์—…์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

๋‚˜๋Š” ๊ธฐ๋ณธ์ด ์•„๋‹Œ ๊ฒ€์‚ฌ๊ธฐ ํด๋ž˜์Šค๋ฅผ django-guardign์˜ ์ผ๋ถ€๊ฐ€ ์•„๋‹ˆ๋ผ "๊ธฐํƒ€" ์•ฑ์˜ ์ผ๋ถ€๋กœ ๋งŒ๋“ค ๊ฒƒ์ด๋ฏ€๋กœ ๊ธฐ๋ณธ์ด ์•„๋‹Œ ๊ฒ€์‚ฌ๊ธฐ ํด๋ž˜์Šค์— ๋Œ€ํ•œ ๋‹ค๋ฅธ ์•ฑ์˜ ์ž‘์—…์ž…๋‹ˆ๋‹ค. ์ž‘์—…์„ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ์กฐ์ • ๋ฐ ๋ณ€๊ฒฝํ•ด์•ผ ํ•  ์‚ฌํ•ญ์— ๋Œ€ํ•œ ๋ณด๋‹ค ์ฒ ์ €ํ•œ ๋ถ„์„์„ ์ˆ˜ํ–‰ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. "์ž‘์—…"์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ณ„๋„์˜ ๋ฌธ์ œ๋ฅผ ์˜๋ฏธํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๊นŒ? btw: ์ด ์ž‘์—…์ด ์ง„ํ–‰๋˜๊ธฐ๊นŒ์ง€ ๋ช‡ ์ฃผ๊ฐ€ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

this[1] ํ”„๋กœ์ ํŠธ์— ๋„์›€์ด ๋˜์…จ๋‚˜์š”?

[1] https://github.com/vintasoftware/django-role-permissions

#330์—์„œ @suriya ๊ฐ€ ๋‹ค์Œ ํ…์ŠคํŠธ๋ฅผ ๋ณต์‚ฌํ–ˆ์Šต๋‹ˆ๋‹ค.

์ง€๋‚œ #23์—์„œ django-guardian์— ์—ญํ•  ์ง€์›์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ๋…ผ์˜๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ฉ€๋ฆฌ ์›€์ง์ด์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์•„๋งˆ๋„ django-guardian์— ์—ญํ• ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ๋งค์šฐ ๋ณต์žกํ•œ ์ž‘์—…์ฒ˜๋Ÿผ ๋ณด์ด๊ธฐ ๋•Œ๋ฌธ์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์•„์ฃผ ๊ฐ„๋‹จํ•œ ์ œ์•ˆ์ด ์žˆ๋Š” ๊ฒƒ ๊ฐ™์•„์š”. ๋‚ด์„ธ์šฐ๊ณ  ๊ด€์‹ฌ์ด ์žˆ๋‹ค๋ฉด ์‹œํ–‰ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ, ๋‚˜๋Š” ์ด ๊ธฐ๋Šฅ์ด ์žˆ๋Š” django-guardian์˜ ๋‚ด ์ž์‹ ์˜ ํฌํฌ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค๋„ ์ด๊ฒƒ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๋ณด๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

๋ทฐ์— ๋Œ€ํ•œ django-guardian์˜ ๊ถŒํ•œ ๊ฒ€์‚ฌ์˜ ํ•ต์‹ฌ์€ get_403_or_None() ํ•จ์ˆ˜์—์„œ ๊ตฌํ˜„๋ฉ๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ•„์š”ํ•œ ๋ชจ๋“  ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. https://github.com/lukaszb/django-guardian/blob/112c373f213a19d93baa81fa4a941a41333115b5/guardian/utils.py#L98

has_permissions = all(request.user.has_perm(perm, obj) for perm in perms)

์ง€์› ์—ญํ• ์€ ์‚ฌ์†Œํ•œ ๋ณ€๊ฒฝ์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž์—๊ฒŒ all ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋Œ€์‹  ์‚ฌ์šฉ์ž๊ฐ€ ํ•˜๋‚˜ ์ด์ƒ์˜ ๊ถŒํ•œ์„ ์ถฉ์กฑํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ ๊ฒƒ

if require_all_permissions:
    has_permissions = all(request.user.has_perm(perm, obj) for perm in perms)
else:
    has_permissions = any(request.user.has_perm(perm, obj) for perm in perms)

๊ทธ๊ฒƒ์„ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ด ์ง€์›์„ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด get_403_or_None() ์— ์ƒˆ ํ”Œ๋ž˜๊ทธ๋ฅผ ์ •์˜ํ•˜๊ณ  ๋ชจ๋“  ๊ถŒํ•œ์„ ์ถฉ์กฑํ• ์ง€ ์•„๋‹ˆ๋ฉด ์ผ๋ถ€ ๊ถŒํ•œ๋งŒ ์ถฉ์กฑํ• ์ง€ ์ง€์ •ํ•˜๋Š” ํ˜ธ์ถœ์ž๋ฅผ ์ •์˜ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” #386์„ ์—ด์—ˆ๊ณ  ์ด๊ฒƒ์ด ์•„๋งˆ ๊ฐ™๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ซ๊ณ  ์žˆ๋‹ค. ๋‚ด๊ฐ€ ํ•„์š”ํ•œ ๊ฒƒ์€ ๊ถŒํ•œ์„ ํ™•์ธํ•˜๋Š” ์ฝ”๋“œ์—์„œ "์ด ์‚ฌ์šฉ์ž์—๊ฒŒ ์ด ๊ฐœ์ฒด์— ๋Œ€ํ•œ ํŽธ์ง‘ ๊ถŒํ•œ์ด ์žˆ์Šต๋‹ˆ๊นŒ?"๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์‚ฌ์šฉ์ž์—๊ฒŒ ๊ถŒํ•œ์„ ํ• ๋‹นํ•  ๋•Œ "์กฐ์ •์ž" ๊ถŒํ•œ์„ ํ• ๋‹นํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. "ํŽธ์ง‘" ๊ถŒํ•œ์„ ํฌํ•จํ•œ ์—ฌ๋Ÿฌ ๊ถŒํ•œ์˜ ์ƒ์œ„ ์ง‘ํ•ฉ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋‚˜์ค‘์— ๋ชจ๋“  ์‚ฌ์šฉ์ž์—๊ฒŒ ์ˆ˜๋™์œผ๋กœ ๊ถŒํ•œ์„ ํ• ๋‹นํ•˜์ง€ ์•Š๊ณ ๋„ "์กฐ์ •์ž"์—๊ฒŒ ๋” ๋งŽ์€ ๊ถŒํ•œ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ถŒํ•œ ๊ณ„์ธต์˜ ์ด๋ฆ„์„ ์—ญํ• ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ญํ• , ์ƒ์† ๋ฐ ์„ ํƒ ์‚ฌํ•ญ์— ๋Œ€ํ•ด +1

๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—ญํ•  ๊ฐœ๋…์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

get_40x_or_None ์‚ฌ์šฉ์ž ์ •์˜

from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.http import HttpResponseForbidden, HttpResponseNotFound
from django.shortcuts import render
from guardian.conf import settings as guardian_settings


def get_40x_or_None(request, perms, global_perms, obj=None, login_url=None,
                    redirect_field_name=None, return_403=False,
                    return_404=False):
    """check if the given perms and global_perms are both granted by the user in combination"""
    login_url = login_url or settings.LOGIN_URL
    redirect_field_name = redirect_field_name or REDIRECT_FIELD_NAME

    # Handles both original and with object provided permission check
    # as ``obj`` defaults to None

    has_permissions = False
    if not has_permissions:
        has_permissions = all(request.user.has_perm(perm, obj) for perm in perms) and \
                          all(request.user.has_perm(perm) for perm in global_perms)

    if not has_permissions:
        if return_403:
            if guardian_settings.RENDER_403:
                response = render(request, guardian_settings.TEMPLATE_403)
                response.status_code = 403
                return response
            elif guardian_settings.RAISE_403:
                raise PermissionDenied
            return HttpResponseForbidden()
        if return_404:
            if guardian_settings.RENDER_404:
                response = render(request, guardian_settings.TEMPLATE_404)
                response.status_code = 404
                return response
            elif guardian_settings.RAISE_404:
                raise ObjectDoesNotExist
            return HttpResponseNotFound()
        else:
            from django.contrib.auth.views import redirect_to_login
            return redirect_to_login(request.get_full_path(),
                                     login_url,
                                     redirect_field_name)

์‚ฌ์šฉ์ž ์ •์˜ GlobalPermissionRequiredMixin ๊ตฌํ˜„

from django.core.exceptions import PermissionDenied, ImproperlyConfigured
from guardian.mixins import PermissionRequiredMixin
from collections.abc import Iterable

from permission.utils import get_40x_or_None


class GlobalPermissionRequiredMixin(PermissionRequiredMixin):
    global_permission_required = None

    def get_global_required_permissions(self, request=None):
        """
        Returns list of permissions in format *<app_label>.<codename>* that
        should be checked against *request.user* and *object*. By default, it
        returns list from ``global_permission_required`` attribute.

        :param request: Original request.
        """
        if isinstance(self.global_permission_required, str):
            perms = [self.global_permission_required]
        elif isinstance(self.global_permission_required, Iterable):
            perms = [p for p in self.global_permission_required]
        else:
            raise ImproperlyConfigured("'GlobalPermissionRequiredMixin' requires "
                                       "'global_permission_required' attribute to be set to "
                                       "'<app_label>.<permission codename>' but is set to '%s' instead"
                                       % self.global_permission_required)
        return perms

    def check_permissions(self, request):
        """
        Checks if *request.user* has all permissions returned by
        *get_required_permissions* method.

        :param request: Original request.
        """
        obj = self.get_permission_object()

        forbidden = get_40x_or_None(request,
                                    perms=self.get_required_permissions(request),
                                    global_perms=self.get_global_required_permissions(request),
                                    obj=obj,
                                    login_url=self.login_url,
                                    redirect_field_name=self.redirect_field_name,
                                    return_403=self.return_403,
                                    return_404=self.return_404,
                                    )
        if forbidden:
            self.on_permission_check_fail(request, forbidden, obj=obj)
        if forbidden and self.raise_exception:
            raise PermissionDenied()
        return forbidden

django ๋‚ด์žฅ crud ๋ณด๊ธฐ์—์„œ global_required_permissions ๋ฐ required_permissions .

class ResourceDeleteView(GlobalPermissionRequiredMixin, DeleteView):
    ...
    global_permission_required = [app.delete_resource]
    permission_required = [app.view_resource]
    ...

๊ฒฐ๋ก 

์ด ์†”๋ฃจ์…˜์€ User ๋ฆฌ์†Œ์Šค ์‚ญ์ œ์— ๋Œ€ํ•œ ์ „์—ญ ๊ถŒํ•œ์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ถŒํ•œ์€ django ์ธ์ฆ ๋ชจ๋ธ์—์„œ Group ์˜ํ•ด ๋ถ€์—ฌ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์ด ๋ฌธ์ œ์˜ ๋งฅ๋ฝ์—์„œ ์—ญํ• ). ๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๋Š” django ์ธ์ฆ ๋ชจ๋ธ์—์„œ view_resource Permission ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋Š” ๊ฐœ์ฒด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฆฌ์†Œ์Šค๋งŒ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด django Guardian์œผ๋กœ ์—ญํ• ์„ ๊ตฌํ˜„ํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ•์ด ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์—„์ง€์†๊ฐ€๋ฝ์„ ์น˜์ผœ์„ธ์šฐ๋ฉด ์œ„ ์ฝ”๋“œ์˜ ์ถฉ๋Œ ์—†๋Š” ์ฝ”๋“œ ๋ฒ„์ „์œผ๋กœ ํ’€ ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ์—ด ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ •๋ง ํฅ๋ฏธ๋กœ์šด ์ ‘๊ทผ @jokiefer. ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์ข‹์•„ํ•œ๋‹ค. ๋‚˜๋Š” ์›๋ž˜ ๊ทธ ๋ผ์ธ์„ ๋”ฐ๋ผ guardian ๋ฅผ ๊ตฌํ˜„ํ•˜๋ ค๊ณ  ํ–ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ์ „์—ญ view ๋˜๋Š” edit ๊ถŒํ•œ์ด ํ•„์š”ํ•˜์ง€๋งŒ ํŠน์ • ๊ฐœ์ฒด์— ์•ก์„ธ์Šคํ•˜๋ ค๋ฉด view ๋˜๋Š” edit ์— ๋Œ€ํ•œ ๊ฐœ๋ณ„ ๊ถŒํ•œ์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ „์—ญ ๊ถŒํ•œ์€ ๊ฐœ์ฒด ๊ถŒํ•œ์ด ์ค‘์š”ํ•˜์ง€ ์•Š์€ ํ™œ์„ฑํ™” ๊ถŒํ•œ๋ณด๋‹ค ๋” ๋งŽ์€ ํ–‰์œ„๋ฅผ ์š”๊ตฌํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ถ„๋ช…ํžˆ ๊ทธ๊ฒƒ์€ ์‹ค์ œ๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ๋‚˜๋Š” ์ผ์„ ์žฌ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

์ƒ˜ํ”Œ์ด ์ด๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๊นŒ? ์ฆ‰, ์ „์—ญ delete ๊ถŒํ•œ์ด ํ•„์š”ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ delete ๊ฐœ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๊ฐœ์ฒด ๊ธฐ๋ฐ˜ delete ๊ถŒํ•œ๋„ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ท€ํ•˜์˜ ์˜ˆ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ธ€๋กœ๋ฒŒ delete ๊ฐ€ ํ•„์š”ํ•˜๊ณ  ๊ฐœ์ฒด๋ณ„ view ? ์•ฝ๊ฐ„์˜ ๋ถˆ์ผ์น˜์ž…๋‹ˆ๊นŒ?

@dwasyl

์ƒ˜ํ”Œ์ด ์ด๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๊นŒ?
์˜ˆ, ์ด๊ฒƒ์€ ๊ท€ํ•˜์˜ ์ ‘๊ทผ ๋ฐฉ์‹์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ์œ„์˜ ์ƒ˜ํ”Œ์— ์ดˆ์ ์„ ๋งž์ถ”์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๊ฐ€๋Šฅํ•œ ํ•œ ๊ฐ„๋‹จํ•œ acl ์•ฑ(AccessControlList)์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ์ด ์•ฑ์—๋Š” cl ์ด๋ผ๋Š” ํ•˜๋‚˜์˜ ํ•ต์‹ฌ ๋ชจ๋ธ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ชจ๋ธ์€ atomic ๋ณดํ˜ธ์ž ๊ถŒํ•œ์˜ ์ถ”์ƒํ™”์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ์ง‘ํ•ฉ, ๊ถŒํ•œ ์ง‘ํ•ฉ, ์•ก์„ธ์Šค ๊ฐ€๋Šฅํ•œ ๊ฐœ์ฒด ์ง‘ํ•ฉ์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ชจ๋ธ ๋’ค์— ์ˆจ๊ฒจ์ง„ ๋งˆ๋ฒ•์€ ์ผ๋ถ€ ์‹ ํ˜ธ์—์„œ ๊ตฌํ˜„๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ownable_models_handling.py ๋Š” ๋ณดํ˜ธ์ž ๊ถŒํ•œ์œผ๋กœ ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค๋ฅธ ๋ชจ๋“  ๋ชจ๋ธ์— ๋™์ ์œผ๋กœ ์‹ ํ˜ธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ์ผ๋ฐ˜ ์‹ ํ˜ธ์ž…๋‹ˆ๋‹ค. ๋” ๋งŽ์€ ๋ชจ๋ธ์„ ํ™•๋ณดํ•˜๋ ค๋ฉด AccessControlList ๋ชจ๋ธ์— ์ƒˆ m2m ํ•„๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ด ํ•„๋“œ๋Š” ์ž๋™์œผ๋กœ ์ž‘๋™ํ•˜๋ ค๋ฉด ์„ ํ–‰ accessible_MODELNAME ์™€ ํ•จ๊ป˜ ํ˜ธ์ถœ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. acl_handling.py ์—๋Š” ์ฃผ์–ด์ง„ AccessControlList ๊ธฐ๋ฐ˜์œผ๋กœ ๋ณดํ˜ธ์ž ๊ถŒํ•œ์„ ์ถ”๊ฐ€/์ œ๊ฑฐํ•˜๋Š” ๊ตฌํ˜„์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์œผ๋กœ ์šฐ๋ฆฌ๋Š” ๊ฐ€๋””์–ธ ์ฝ”์–ด๋ฅผ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

์ด cl ์•ฑ์ด ์œ„์˜ ์ƒ˜ํ”Œ๋ณด๋‹ค ๋” ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. acl ์•ฑ์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€์ง€ ์ถ”๊ฐ€ ์ž‘์—…์œผ๋กœ guadian(๊ฐ€๋””์–ธ์„ ์œ„ํ•œ ์ตœ์ƒ์œ„ ์•ฑ ์†”๋ฃจ์…˜)์— ์—ญํ• ์„ ์ถ”๊ฐ€ํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ•์„ ์œ„ํ•ด ์ฝ”๋“œ์—์„œ ์•„์›ƒ์†Œ์‹ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰