[Notice] Announcing the End of Demo Server [Read me]

Rewrite Access Controller
* Simplify AccessControl's interface. * Reduce isAllowed's parameters 5 to 3. * Add Add isCreatable() instead of Operation.CREATE * Reanme Operation.EDIT to Opeartion.UPDATE * Remove unnecessary Permission entity. * Add SiteAdmin entity to classify who is Site Admin.
@b7f4fafe2fa83ebefac7095cf044158483c75602
--- RUNNING_PID
... | ... | @@ -1,1 +0,0 @@ |
1 | -5728(No newline at end of file) |
--- app/Global.java
+++ app/Global.java
... | ... | @@ -30,12 +30,8 @@ |
30 | 30 |
Ebean.save(all.get("issueComments")); |
31 | 31 |
Ebean.save(all.get("posts")); |
32 | 32 |
Ebean.save(all.get("comments")); |
33 |
- Ebean.save(all.get("permissions")); |
|
34 | 33 |
|
35 | 34 |
Ebean.save(all.get("roles")); |
36 |
- for (Object role : all.get("roles")) { |
|
37 |
- Ebean.saveManyToManyAssociations(role, "permissions"); |
|
38 |
- } |
|
39 | 35 |
Ebean.save(all.get("projectUsers")); |
40 | 36 |
|
41 | 37 |
Ebean.save(all.get("taskBoards")); |
... | ... | @@ -43,6 +39,8 @@ |
43 | 39 |
Ebean.save(all.get("cards")); |
44 | 40 |
Ebean.save(all.get("labels")); |
45 | 41 |
Ebean.save(all.get("checkLists")); |
42 |
+ |
|
43 |
+ Ebean.save(all.get("siteAdmins")); |
|
46 | 44 |
} |
47 | 45 |
} |
48 | 46 |
|
--- app/controllers/AttachmentApp.java
+++ app/controllers/AttachmentApp.java
... | ... | @@ -11,6 +11,7 @@ |
11 | 11 |
import java.util.Map; |
12 | 12 |
|
13 | 13 |
import models.Attachment; |
14 |
+import models.Project; |
|
14 | 15 |
import models.enumeration.Operation; |
15 | 16 |
import models.enumeration.Resource; |
16 | 17 |
|
... | ... | @@ -99,8 +100,7 @@ |
99 | 100 |
return notFound(); |
100 | 101 |
} |
101 | 102 |
|
102 |
- if (!AccessControl.isAllowed(UserApp.currentUser().id, attachment.projectId, |
|
103 |
- attachment.containerType, Operation.READ, attachment.containerId)) { |
|
103 |
+ if (!AccessControl.isAllowed(UserApp.currentUser(), attachment.getContainerAsResource(), Operation.READ)) { |
|
104 | 104 |
return forbidden(); |
105 | 105 |
} |
106 | 106 |
|
... | ... | @@ -131,8 +131,7 @@ |
131 | 131 |
return notFound(); |
132 | 132 |
} |
133 | 133 |
|
134 |
- if (!AccessControl.isAllowed(UserApp.currentUser().id, attach.projectId, |
|
135 |
- attach.containerType, Operation.DELETE, attach.containerId)) { |
|
134 |
+ if (!AccessControl.isAllowed(UserApp.currentUser(), attach.getContainerAsResource(), Operation.DELETE)) { |
|
136 | 135 |
return forbidden(); |
137 | 136 |
} |
138 | 137 |
|
... | ... | @@ -188,8 +187,8 @@ |
188 | 187 |
List<Map<String, String>> attachments = new ArrayList<Map<String, String>>(); |
189 | 188 |
for (Attachment attach : Attachment.findByContainer(Resource.valueOf(containerType), |
190 | 189 |
Long.parseLong(containerId))) { |
191 |
- if (!AccessControl.isAllowed(UserApp.currentUser().id, attach.projectId, |
|
192 |
- attach.containerType, Operation.READ, attach.containerId)) { |
|
190 |
+ if (!AccessControl.isAllowed(UserApp.currentUser(), |
|
191 |
+ attach.getContainerAsResource(), Operation.READ)) { |
|
193 | 192 |
return forbidden(); |
194 | 193 |
} |
195 | 194 |
attachments.add(fileAsMap(attach)); |
--- app/controllers/BoardApp.java
+++ app/controllers/BoardApp.java
... | ... | @@ -10,6 +10,7 @@ |
10 | 10 |
import models.Comment; |
11 | 11 |
import models.Post; |
12 | 12 |
import models.Project; |
13 |
+import models.User; |
|
13 | 14 |
import models.enumeration.Direction; |
14 | 15 |
import models.enumeration.Operation; |
15 | 16 |
import models.enumeration.Resource; |
... | ... | @@ -27,14 +28,14 @@ |
27 | 28 |
import views.html.board.postList; |
28 | 29 |
|
29 | 30 |
public class BoardApp extends Controller { |
30 |
- |
|
31 |
+ |
|
31 | 32 |
//TODO 이 클래스는 원래 따로 존재해야 함. |
32 | 33 |
public static class SearchCondition{ |
33 | 34 |
public final static String ORDERING_KEY_ID = "id"; |
34 | 35 |
public final static String ORDERING_KEY_TITLE = "title"; |
35 | 36 |
public final static String ORDERING_KEY_AGE = "date"; |
36 | 37 |
public final static String ORDERING_KEY_AUTHOR = "authorName"; |
37 |
- |
|
38 |
+ |
|
38 | 39 |
public SearchCondition() { |
39 | 40 |
this.order = Direction.DESC.direction(); |
40 | 41 |
this.key = ORDERING_KEY_ID; |
... | ... | @@ -47,14 +48,14 @@ |
47 | 48 |
public String filter; |
48 | 49 |
public int pageNum; |
49 | 50 |
} |
50 |
- |
|
51 |
+ |
|
51 | 52 |
|
52 | 53 |
public static Result posts(String userName, String projectName) { |
53 |
- |
|
54 | 54 |
Form<SearchCondition> postParamForm = new Form<SearchCondition>(SearchCondition.class); |
55 | 55 |
SearchCondition postSearchCondition = postParamForm.bindFromRequest().get(); |
56 | 56 |
Project project = ProjectApp.getProject(userName, projectName); |
57 |
- if (!AccessControl.isAllowed(session().get("userId"), project)) { |
|
57 |
+ |
|
58 |
+ if (!AccessControl.isCreatable(User.findByLoginId(session().get("loginId")), project, Resource.BOARD_POST)) { |
|
58 | 59 |
return unauthorized(views.html.project.unauthorized.render(project)); |
59 | 60 |
} |
60 | 61 |
|
... | ... | @@ -68,7 +69,7 @@ |
68 | 69 |
|
69 | 70 |
public static Result newPostForm(String userName, String projectName) { |
70 | 71 |
Project project = ProjectApp.getProject(userName, projectName); |
71 |
- if (!AccessControl.isAllowed(session().get("userId"), project)) { |
|
72 |
+ if (UserApp.currentUser() == UserApp.anonymous) { |
|
72 | 73 |
return unauthorized(views.html.project.unauthorized.render(project)); |
73 | 74 |
} |
74 | 75 |
|
... | ... | @@ -102,7 +103,7 @@ |
102 | 103 |
public static Result post(String userName, String projectName, Long postId) { |
103 | 104 |
Post post = Post.findById(postId); |
104 | 105 |
Project project = ProjectApp.getProject(userName, projectName); |
105 |
- if (!AccessControl.isAllowed(session().get("userId"), project)) { |
|
106 |
+ if (!AccessControl.isCreatable(User.findByLoginId(session().get("loginId")), project, Resource.BOARD_POST)) { |
|
106 | 107 |
return unauthorized(views.html.project.unauthorized.render(project)); |
107 | 108 |
} |
108 | 109 |
|
... | ... | @@ -152,8 +153,7 @@ |
152 | 153 |
Form<Post> editForm = new Form<Post>(Post.class).fill(existPost); |
153 | 154 |
Project project = ProjectApp.getProject(userName, projectName); |
154 | 155 |
|
155 |
- |
|
156 |
- if (AccessControl.isAllowed(UserApp.currentUser().id, project.id, Resource.BOARD_POST, Operation.EDIT, postId)) { |
|
156 |
+ if (AccessControl.isAllowed(UserApp.currentUser(), existPost.asResource(), Operation.UPDATE)) { |
|
157 | 157 |
return ok(editPost.render("board.post.modify", editForm, postId, project)); |
158 | 158 |
} else { |
159 | 159 |
flash(Constants.WARNING, "board.notAuthor"); |
--- app/controllers/CodeHistoryApp.java
+++ app/controllers/CodeHistoryApp.java
... | ... | @@ -6,6 +6,7 @@ |
6 | 6 |
import javax.servlet.ServletException; |
7 | 7 |
|
8 | 8 |
import models.Project; |
9 |
+import models.enumeration.Operation; |
|
9 | 10 |
|
10 | 11 |
import org.eclipse.jgit.api.errors.GitAPIException; |
11 | 12 |
import org.eclipse.jgit.api.errors.NoHeadException; |
... | ... | @@ -14,6 +15,7 @@ |
14 | 15 |
import play.mvc.Controller; |
15 | 16 |
import play.mvc.Result; |
16 | 17 |
import playRepository.Commit; |
18 |
+import playRepository.PlayRepository; |
|
17 | 19 |
import playRepository.RepositoryService; |
18 | 20 |
import utils.AccessControl; |
19 | 21 |
import utils.HttpUtil; |
... | ... | @@ -35,7 +37,9 @@ |
35 | 37 |
UnsupportedOperationException, ServletException, GitAPIException, |
36 | 38 |
SVNException { |
37 | 39 |
Project project = Project.findByNameAndOwner(userName, projectName); |
38 |
- if (!AccessControl.isAllowed(session().get("userId"), project)) { |
|
40 |
+ PlayRepository repository = RepositoryService.getRepository(project); |
|
41 |
+ |
|
42 |
+ if (!AccessControl.isAllowed(UserApp.currentUser(), project.asResource(), Operation.READ)) { |
|
39 | 43 |
return unauthorized(views.html.project.unauthorized.render(project)); |
40 | 44 |
} |
41 | 45 |
|
... | ... | @@ -46,8 +50,7 @@ |
46 | 50 |
} |
47 | 51 |
|
48 | 52 |
try { |
49 |
- List<Commit> commits = RepositoryService.getRepository(project).getHistory(page, |
|
50 |
- HISTORY_ITEM_LIMIT, branch); |
|
53 |
+ List<Commit> commits = repository.getHistory(page, HISTORY_ITEM_LIMIT, branch); |
|
51 | 54 |
return ok(history.render(project, commits, page, branch)); |
52 | 55 |
} catch (NoHeadException e) { |
53 | 56 |
return ok(nohead.render(project)); |
... | ... | @@ -58,7 +61,7 @@ |
58 | 61 |
throws IOException, UnsupportedOperationException, ServletException, GitAPIException, |
59 | 62 |
SVNException { |
60 | 63 |
Project project = Project.findByNameAndOwner(userName, projectName); |
61 |
- if (!AccessControl.isAllowed(session().get("userId"), project)) { |
|
64 |
+ if (!AccessControl.isAllowed(UserApp.currentUser(), project.asResource(), Operation.READ)) { |
|
62 | 65 |
return unauthorized(views.html.project.unauthorized.render(project)); |
63 | 66 |
} |
64 | 67 |
String patch = RepositoryService.getRepository(project).getPatch(commitId); |
--- app/controllers/GitApp.java
+++ app/controllers/GitApp.java
... | ... | @@ -14,6 +14,7 @@ |
14 | 14 |
import play.mvc.Controller; |
15 | 15 |
import play.mvc.Result; |
16 | 16 |
import play.mvc.With; |
17 |
+import playRepository.PlayRepository; |
|
17 | 18 |
import playRepository.RepositoryService; |
18 | 19 |
import utils.AccessControl; |
19 | 20 |
import utils.BasicAuthAction; |
... | ... | @@ -26,16 +27,17 @@ |
26 | 27 |
&& (service.equals("git-upload-pack") || service.equals("git-receive-pack")); |
27 | 28 |
} |
28 | 29 |
|
29 |
- public static boolean isAllowed(String userName, String projectName, String service) { |
|
30 |
+ public static boolean isAllowed(String userName, String projectName, String service) throws UnsupportedOperationException, IOException, ServletException { |
|
30 | 31 |
Project project = ProjectApp.getProject(userName, projectName); |
31 | 32 |
|
32 |
- Operation operation = Operation.WRITE; |
|
33 |
+ Operation operation = Operation.UPDATE; |
|
33 | 34 |
if (service.equals("git-upload-pack")) { |
34 | 35 |
operation = Operation.READ; |
35 | 36 |
} |
36 | 37 |
|
37 |
- if (AccessControl.isAllowed( |
|
38 |
- UserApp.currentUser().id, project.id, Resource.CODE, operation, null)) { |
|
38 |
+ PlayRepository repository = RepositoryService.getRepository(project); |
|
39 |
+ if (AccessControl |
|
40 |
+ .isAllowed(UserApp.currentUser(), repository.asResource(), operation)) { |
|
39 | 41 |
return true; |
40 | 42 |
} |
41 | 43 |
|
--- app/controllers/IssueApp.java
+++ app/controllers/IssueApp.java
... | ... | @@ -8,6 +8,7 @@ |
8 | 8 |
import java.io.IOException; |
9 | 9 |
import java.io.UnsupportedEncodingException; |
10 | 10 |
import java.util.List; |
11 |
+import play.Logger; |
|
11 | 12 |
|
12 | 13 |
import jxl.write.WriteException; |
13 | 14 |
|
... | ... | @@ -18,6 +19,7 @@ |
18 | 19 |
import models.IssueLabel; |
19 | 20 |
import models.Project; |
20 | 21 |
import models.enumeration.Direction; |
22 |
+import models.enumeration.Operation; |
|
21 | 23 |
import models.enumeration.Resource; |
22 | 24 |
import models.enumeration.State; |
23 | 25 |
import models.support.FinderTemplate; |
... | ... | @@ -58,7 +60,7 @@ |
58 | 60 |
@Cached(key = "issues") |
59 | 61 |
public static Result issues(String userName, String projectName, String state, String format) throws WriteException, IOException { |
60 | 62 |
Project project = ProjectApp.getProject(userName, projectName); |
61 |
- if (!AccessControl.isAllowed(session().get("userId"), project)) { |
|
63 |
+ if (!AccessControl.isAllowed(UserApp.currentUser(), project.asResource(), Operation.READ)) { |
|
62 | 64 |
return unauthorized(views.html.project.unauthorized.render(project)); |
63 | 65 |
} |
64 | 66 |
|
... | ... | @@ -119,7 +121,7 @@ |
119 | 121 |
|
120 | 122 |
public static Result newIssueForm(String userName, String projectName) { |
121 | 123 |
Project project = ProjectApp.getProject(userName, projectName); |
122 |
- if (!AccessControl.isAllowed(session().get("userId"), project)) { |
|
124 |
+ if (UserApp.currentUser() == UserApp.anonymous) { |
|
123 | 125 |
return unauthorized(views.html.project.unauthorized.render(project)); |
124 | 126 |
} |
125 | 127 |
|
... | ... | @@ -165,7 +167,7 @@ |
165 | 167 |
Issue targetIssue = Issue.findById(id); |
166 | 168 |
Form<Issue> editForm = new Form<Issue>(Issue.class).fill(targetIssue); |
167 | 169 |
Project project = ProjectApp.getProject(userName, projectName); |
168 |
- if (!AccessControl.isAllowed(session().get("userId"), project)) { |
|
170 |
+ if (!AccessControl.isAllowed(UserApp.currentUser(), targetIssue.asResource(), Operation.UPDATE)) { |
|
169 | 171 |
return unauthorized(views.html.project.unauthorized.render(project)); |
170 | 172 |
} |
171 | 173 |
|
... | ... | @@ -250,4 +252,4 @@ |
250 | 252 |
return redirect(routes.IssueApp.issue(project.owner, project.name, issueId)); |
251 | 253 |
} |
252 | 254 |
|
253 |
-}(No newline at end of file) |
|
255 |
+} |
--- app/controllers/IssueLabelApp.java
+++ app/controllers/IssueLabelApp.java
... | ... | @@ -77,8 +77,7 @@ |
77 | 77 |
return notFound(); |
78 | 78 |
} |
79 | 79 |
|
80 |
- if (!AccessControl.isAllowed(UserApp.currentUser().id, label.project.id, |
|
81 |
- Resource.ISSUE_LABEL, Operation.DELETE, label.id)) { |
|
80 |
+ if (!AccessControl.isAllowed(UserApp.currentUser(), label.asResource(), Operation.DELETE)) { |
|
82 | 81 |
return forbidden(); |
83 | 82 |
} |
84 | 83 |
|
--- app/controllers/ProjectApp.java
+++ app/controllers/ProjectApp.java
... | ... | @@ -6,6 +6,7 @@ |
6 | 6 |
import models.ProjectUser; |
7 | 7 |
import models.Role; |
8 | 8 |
import models.User; |
9 |
+import models.enumeration.Operation; |
|
9 | 10 |
import models.enumeration.RoleType; |
10 | 11 |
import models.task.CardAssignee; |
11 | 12 |
import play.cache.Cached; |
... | ... | @@ -42,7 +43,7 @@ |
42 | 43 |
// @Cached(key = "project") |
43 | 44 |
public static Result project(String userName, String projectName) { |
44 | 45 |
Project project = ProjectApp.getProject(userName, projectName); |
45 |
- if (!AccessControl.isAllowed(session().get("userId"), project)) { |
|
46 |
+ if (!AccessControl.isAllowed(UserApp.currentUser(), project.asResource(), Operation.READ)) { |
|
46 | 47 |
return unauthorized(views.html.project.unauthorized.render(project)); |
47 | 48 |
} |
48 | 49 |
|
... | ... | @@ -62,7 +63,7 @@ |
62 | 63 |
|
63 | 64 |
public static Result settingForm(String userName, String projectName) { |
64 | 65 |
Project project = getProject(userName, projectName); |
65 |
- if (!AccessControl.isAllowed(session().get("userId"), project)) { |
|
66 |
+ if (!AccessControl.isAllowed(UserApp.currentUser(), project.asResource(), Operation.UPDATE)) { |
|
66 | 67 |
return unauthorized(views.html.project.unauthorized.render(project)); |
67 | 68 |
} |
68 | 69 |
|
... | ... | @@ -108,7 +109,7 @@ |
108 | 109 |
Form<Project> filledUpdatedProjectForm = form(Project.class) |
109 | 110 |
.bindFromRequest(); |
110 | 111 |
Project project = filledUpdatedProjectForm.get(); |
111 |
- |
|
112 |
+ |
|
112 | 113 |
if(!ProjectUser.isManager(UserApp.currentUser().id, project.id)){ |
113 | 114 |
flash(Constants.WARNING, "project.member.isManager"); |
114 | 115 |
return redirect(routes.ProjectApp.settingForm(userName, project.name)); |
... | ... | @@ -121,7 +122,7 @@ |
121 | 122 |
|
122 | 123 |
MultipartFormData body = request().body().asMultipartFormData(); |
123 | 124 |
FilePart filePart = body.getFile("logoPath"); |
124 |
- |
|
125 |
+ |
|
125 | 126 |
if (filePart != null) { |
126 | 127 |
if(!isImageFile(filePart.getFilename())) { |
127 | 128 |
flash(Constants.WARNING, "project.logo.alert"); |
... | ... | @@ -154,14 +155,14 @@ |
154 | 155 |
} |
155 | 156 |
|
156 | 157 |
public static boolean isImageFile(String filename) { |
157 |
- boolean isImageFile = false; |
|
158 |
- for(String suffix : LOGO_TYPE){ |
|
159 |
- if(filename.toLowerCase().endsWith(suffix)) |
|
160 |
- isImageFile=true; |
|
161 |
- } |
|
162 |
- return isImageFile; |
|
158 |
+ boolean isImageFile = false; |
|
159 |
+ for(String suffix : LOGO_TYPE) { |
|
160 |
+ if(filename.toLowerCase().endsWith(suffix)) |
|
161 |
+ isImageFile=true; |
|
162 |
+ } |
|
163 |
+ return isImageFile; |
|
163 | 164 |
} |
164 |
- |
|
165 |
+ |
|
165 | 166 |
public static Result deleteProject(String userName, String projectName) throws Exception { |
166 | 167 |
Project project = getProject(userName, projectName); |
167 | 168 |
if (ProjectUser.isManager(UserApp.currentUser().id, project.id)) { |
--- app/controllers/SvnApp.java
+++ app/controllers/SvnApp.java
... | ... | @@ -18,6 +18,7 @@ |
18 | 18 |
import play.mvc.Controller; |
19 | 19 |
import play.mvc.Result; |
20 | 20 |
import play.mvc.With; |
21 |
+import playRepository.PlayRepository; |
|
21 | 22 |
import playRepository.RepositoryService; |
22 | 23 |
import utils.AccessControl; |
23 | 24 |
import utils.BasicAuthAction; |
... | ... | @@ -28,7 +29,7 @@ |
28 | 29 |
|
29 | 30 |
public class SvnApp extends Controller { |
30 | 31 |
static DAVServlet davServlet; |
31 |
- |
|
32 |
+ |
|
32 | 33 |
@With(BasicAuthAction.class) |
33 | 34 |
@BodyParser.Of(BodyParser.Raw.class) |
34 | 35 |
public static Result serviceWithPath(String path) throws ServletException, IOException { |
... | ... | @@ -68,9 +69,10 @@ |
68 | 69 |
User currentUser = UserApp.currentUser(); |
69 | 70 |
// Check the user has a permission to access this repository. |
70 | 71 |
Project project = Project.findByNameAndOwner(userName, projectName); |
71 |
- |
|
72 |
- if (!AccessControl.isAllowed(currentUser.id, project.id, |
|
73 |
- Resource.CODE, getRequestedOperation(request().method()), null)) { |
|
72 |
+ |
|
73 |
+ PlayRepository repository = RepositoryService.getRepository(project); |
|
74 |
+ if (!AccessControl.isAllowed(currentUser, repository.asResource(), |
|
75 |
+ getRequestedOperation(request().method()))) { |
|
74 | 76 |
if (currentUser.id == UserApp.anonymous.id) { |
75 | 77 |
return BasicAuthAction.unauthorized(response()); |
76 | 78 |
} else { |
... | ... | @@ -99,7 +101,7 @@ |
99 | 101 |
|| DAVHandlerFactory.METHOD_REPORT.equals(method)) { |
100 | 102 |
return Operation.READ; |
101 | 103 |
} else { |
102 |
- return Operation.WRITE; |
|
104 |
+ return Operation.UPDATE; |
|
103 | 105 |
} |
104 | 106 |
} |
105 | 107 |
|
--- app/controllers/TaskApp.java
+++ app/controllers/TaskApp.java
... | ... | @@ -3,6 +3,7 @@ |
3 | 3 |
import java.util.Map; |
4 | 4 |
|
5 | 5 |
import models.Project; |
6 |
+import models.enumeration.Operation; |
|
6 | 7 |
import models.task.Card; |
7 | 8 |
import models.task.Line; |
8 | 9 |
import models.task.TaskBoard; |
... | ... | @@ -22,7 +23,7 @@ |
22 | 23 |
public class TaskApp extends Controller { |
23 | 24 |
public static Result index(String userName, String projectName) { |
24 | 25 |
Project project = ProjectApp.getProject(userName, projectName); |
25 |
- if (!AccessControl.isAllowed(session().get("userId"), project)) { |
|
26 |
+ if (!AccessControl.isAllowed(UserApp.currentUser(), project.asResource(), Operation.READ)) { |
|
26 | 27 |
return unauthorized(views.html.project.unauthorized.render(project)); |
27 | 28 |
} |
28 | 29 |
return ok(taskView.render(project)); |
... | ... | @@ -30,7 +31,7 @@ |
30 | 31 |
|
31 | 32 |
public static Result card(String userName, String projectName, Long cardId) { |
32 | 33 |
Project project = ProjectApp.getProject(userName, projectName); |
33 |
- if (!AccessControl.isAllowed(session().get("userId"), project)) { |
|
34 |
+ if (!AccessControl.isAllowed(UserApp.currentUser(), project.asResource(), Operation.READ)) { |
|
34 | 35 |
return unauthorized(views.html.project.unauthorized.render(project)); |
35 | 36 |
} |
36 | 37 |
|
... | ... | @@ -39,7 +40,7 @@ |
39 | 40 |
|
40 | 41 |
public static Result getLabels(String userName, String projectName) { |
41 | 42 |
Project project = ProjectApp.getProject(userName, projectName); |
42 |
- if (!AccessControl.isAllowed(session().get("userId"), project)) { |
|
43 |
+ if (!AccessControl.isAllowed(UserApp.currentUser(), project.asResource(), Operation.READ)) { |
|
43 | 44 |
return unauthorized(views.html.project.unauthorized.render(project)); |
44 | 45 |
} |
45 | 46 |
TaskBoard taskBoard = TaskBoard.findByProject(project); |
... | ... | @@ -48,7 +49,7 @@ |
48 | 49 |
|
49 | 50 |
public static Result getMember(String userName, String projectName) { |
50 | 51 |
Project project = ProjectApp.getProject(userName, projectName); |
51 |
- if (!AccessControl.isAllowed(session().get("userId"), project)) { |
|
52 |
+ if (!AccessControl.isAllowed(UserApp.currentUser(), project.asResource(), Operation.READ)) { |
|
52 | 53 |
return unauthorized(views.html.project.unauthorized.render(project)); |
53 | 54 |
} |
54 | 55 |
TaskBoard taskBoard = TaskBoard.findByProject(project); |
... | ... | @@ -58,7 +59,7 @@ |
58 | 59 |
// TestCode 나중에 전부 웹소켓으로 바꾼다. 당연히 그걸 고려해서 짜야한다. |
59 | 60 |
public static Result cardView(String userName, String projectName) { |
60 | 61 |
Project project = ProjectApp.getProject(userName, projectName); |
61 |
- if (!AccessControl.isAllowed(session().get("userId"), project)) { |
|
62 |
+ if (!AccessControl.isAllowed(UserApp.currentUser(), project.asResource(), Operation.READ)) { |
|
62 | 63 |
return unauthorized(views.html.project.unauthorized.render(project)); |
63 | 64 |
} |
64 | 65 |
return ok(cardView.render(project)); |
... | ... | @@ -100,9 +101,9 @@ |
100 | 101 |
} |
101 | 102 |
|
102 | 103 |
// TestCode End |
103 |
- |
|
104 |
+ |
|
104 | 105 |
public static WebSocket<String> connect(String userName, String projectName) { |
105 | 106 |
return WebSocketServer.handelWebSocket(userName, projectName); |
106 | 107 |
} |
107 | 108 |
|
108 |
-} |
|
109 |
+}(No newline at end of file) |
--- app/models/Assignee.java
+++ app/models/Assignee.java
... | ... | @@ -8,12 +8,8 @@ |
8 | 8 |
import javax.persistence.ManyToOne; |
9 | 9 |
import javax.persistence.OneToMany; |
10 | 10 |
|
11 |
-import org.apache.commons.lang.builder.HashCodeBuilder; |
|
12 |
- |
|
13 |
-import play.Logger; |
|
14 | 11 |
import play.data.validation.Constraints.Required; |
15 | 12 |
import play.db.ebean.Model; |
16 |
-import play.db.ebean.Model.Finder; |
|
17 | 13 |
|
18 | 14 |
@Entity |
19 | 15 |
public class Assignee extends Model { |
... | ... | @@ -37,7 +33,7 @@ |
37 | 33 |
@OneToMany(mappedBy = "assignee", cascade = CascadeType.ALL) |
38 | 34 |
public Set<Issue> issues; |
39 | 35 |
|
40 |
- public static Finder<Long, Assignee> finder = new Finder<Long, Assignee>(Long.class, |
|
36 |
+ public static Model.Finder<Long, Assignee> finder = new Finder<Long, Assignee>(Long.class, |
|
41 | 37 |
Assignee.class); |
42 | 38 |
|
43 | 39 |
public Assignee(Long userId, Long projectId) { |
--- app/models/Attachment.java
+++ app/models/Attachment.java
... | ... | @@ -18,7 +18,11 @@ |
18 | 18 |
import org.apache.commons.io.FileUtils; |
19 | 19 |
import org.apache.tika.Tika; |
20 | 20 |
|
21 |
+import controllers.ProjectApp; |
|
22 |
+ |
|
21 | 23 |
import models.enumeration.Resource; |
24 |
+import models.resource.GlobalResource; |
|
25 |
+import models.resource.ProjectResource; |
|
22 | 26 |
|
23 | 27 |
import play.data.validation.*; |
24 | 28 |
|
... | ... | @@ -55,8 +59,8 @@ |
55 | 59 |
public Long containerId; |
56 | 60 |
|
57 | 61 |
/** |
58 |
- * 모든 임시파일은 컨테이너 타입 Resource.USER 에 해당한다. |
|
59 |
- * |
|
62 |
+ * 모든 임시파일은 컨테이너 타입 Resource.USER 에 해당한다. |
|
63 |
+ * |
|
60 | 64 |
* @param userId |
61 | 65 |
* @return |
62 | 66 |
*/ |
... | ... | @@ -204,4 +208,23 @@ |
204 | 208 |
attachment.delete(); |
205 | 209 |
} |
206 | 210 |
} |
211 |
+ |
|
212 |
+ public ProjectResource getContainerAsResource() { |
|
213 |
+ return new ProjectResource() { |
|
214 |
+ @Override |
|
215 |
+ public Long getId() { |
|
216 |
+ return containerId; |
|
217 |
+ } |
|
218 |
+ |
|
219 |
+ @Override |
|
220 |
+ public Project getProject() { |
|
221 |
+ return Project.find.byId(projectId); |
|
222 |
+ } |
|
223 |
+ |
|
224 |
+ @Override |
|
225 |
+ public Resource getType() { |
|
226 |
+ return containerType; |
|
227 |
+ } |
|
228 |
+ }; |
|
229 |
+ } |
|
207 | 230 |
}(No newline at end of file) |
--- app/models/Comment.java
+++ app/models/Comment.java
... | ... | @@ -1,5 +1,8 @@ |
1 | 1 |
package models; |
2 | 2 |
|
3 |
+import models.enumeration.Resource; |
|
4 |
+import models.resource.ProjectResource; |
|
5 |
+ |
|
3 | 6 |
import org.joda.time.*; |
4 | 7 |
import play.data.validation.*; |
5 | 8 |
import play.db.ebean.*; |
... | ... | @@ -59,4 +62,23 @@ |
59 | 62 |
public static void delete(Long commentId) { |
60 | 63 |
find.byId(commentId).delete(); |
61 | 64 |
} |
65 |
+ |
|
66 |
+ public ProjectResource asResource() { |
|
67 |
+ return new ProjectResource() { |
|
68 |
+ @Override |
|
69 |
+ public Long getId() { |
|
70 |
+ return id; |
|
71 |
+ } |
|
72 |
+ |
|
73 |
+ @Override |
|
74 |
+ public Project getProject() { |
|
75 |
+ return post.project; |
|
76 |
+ } |
|
77 |
+ |
|
78 |
+ @Override |
|
79 |
+ public Resource getType() { |
|
80 |
+ return Resource.BOARD_COMMENT; |
|
81 |
+ } |
|
82 |
+ }; |
|
83 |
+ } |
|
62 | 84 |
} |
--- app/models/Issue.java
+++ app/models/Issue.java
... | ... | @@ -10,6 +10,7 @@ |
10 | 10 |
import jxl.format.Alignment; |
11 | 11 |
import jxl.write.*; |
12 | 12 |
import models.enumeration.*; |
13 |
+import models.resource.ProjectResource; |
|
13 | 14 |
import models.support.*; |
14 | 15 |
import org.joda.time.*; |
15 | 16 |
import play.data.format.*; |
... | ... | @@ -493,4 +494,54 @@ |
493 | 494 |
public boolean isClosed() { |
494 | 495 |
return this.state == State.CLOSED; |
495 | 496 |
} |
496 |
-} |
|
497 |
+ |
|
498 |
+ public ProjectResource asResource() { |
|
499 |
+ return new ProjectResource() { |
|
500 |
+ @Override |
|
501 |
+ public Long getId() { |
|
502 |
+ return null; |
|
503 |
+ } |
|
504 |
+ |
|
505 |
+ @Override |
|
506 |
+ public Project getProject() { |
|
507 |
+ return project; |
|
508 |
+ } |
|
509 |
+ |
|
510 |
+ @Override |
|
511 |
+ public Resource getType() { |
|
512 |
+ return Resource.ISSUE_POST; |
|
513 |
+ } |
|
514 |
+ }; |
|
515 |
+ } |
|
516 |
+ |
|
517 |
+ public ProjectResource fieldAsResource(final Resource resourceType) { |
|
518 |
+ return new ProjectResource() { |
|
519 |
+ @Override |
|
520 |
+ public Long getId() { |
|
521 |
+ return id; |
|
522 |
+ } |
|
523 |
+ |
|
524 |
+ @Override |
|
525 |
+ public Project getProject() { |
|
526 |
+ return project; |
|
527 |
+ } |
|
528 |
+ |
|
529 |
+ @Override |
|
530 |
+ public Resource getType() { |
|
531 |
+ return resourceType; |
|
532 |
+ } |
|
533 |
+ }; |
|
534 |
+ } |
|
535 |
+ |
|
536 |
+ public ProjectResource stateAsResource() { |
|
537 |
+ return fieldAsResource(Resource.ISSUE_STATE); |
|
538 |
+ } |
|
539 |
+ |
|
540 |
+ public ProjectResource milestoneAsResource() { |
|
541 |
+ return fieldAsResource(Resource.ISSUE_MILESTONE); |
|
542 |
+ } |
|
543 |
+ |
|
544 |
+ public ProjectResource assigneeAsResource() { |
|
545 |
+ return fieldAsResource(Resource.ISSUE_ASSIGNEE); |
|
546 |
+ } |
|
547 |
+}(No newline at end of file) |
--- app/models/IssueComment.java
+++ app/models/IssueComment.java
... | ... | @@ -4,6 +4,9 @@ |
4 | 4 |
|
5 | 5 |
package models; |
6 | 6 |
|
7 |
+import models.enumeration.Resource; |
|
8 |
+import models.resource.ProjectResource; |
|
9 |
+ |
|
7 | 10 |
import org.joda.time.*; |
8 | 11 |
import play.data.validation.*; |
9 | 12 |
import play.db.ebean.*; |
... | ... | @@ -62,4 +65,25 @@ |
62 | 65 |
public Duration ago() { |
63 | 66 |
return JodaDateUtil.ago(this.date); |
64 | 67 |
} |
68 |
+ |
|
69 |
+ public ProjectResource asResource() { |
|
70 |
+ |
|
71 |
+ return new ProjectResource() { |
|
72 |
+ @Override |
|
73 |
+ public Long getId() { |
|
74 |
+ return id; |
|
75 |
+ } |
|
76 |
+ |
|
77 |
+ @Override |
|
78 |
+ public Project getProject() { |
|
79 |
+ return issue.project; |
|
80 |
+ } |
|
81 |
+ |
|
82 |
+ @Override |
|
83 |
+ public Resource getType() { |
|
84 |
+ return Resource.ISSUE_COMMENT; |
|
85 |
+ } |
|
86 |
+ }; |
|
87 |
+ } |
|
65 | 88 |
} |
89 |
+ |
--- app/models/IssueLabel.java
+++ app/models/IssueLabel.java
... | ... | @@ -14,6 +14,10 @@ |
14 | 14 |
import play.db.ebean.*; |
15 | 15 |
|
16 | 16 |
import javax.persistence.*; |
17 |
+ |
|
18 |
+import models.enumeration.Resource; |
|
19 |
+import models.resource.ProjectResource; |
|
20 |
+ |
|
17 | 21 |
import java.util.*; |
18 | 22 |
|
19 | 23 |
@Entity |
... | ... | @@ -64,4 +68,23 @@ |
64 | 68 |
} |
65 | 69 |
super.delete(); |
66 | 70 |
} |
71 |
+ |
|
72 |
+ public ProjectResource asResource() { |
|
73 |
+ return new ProjectResource() { |
|
74 |
+ @Override |
|
75 |
+ public Long getId() { |
|
76 |
+ return id; |
|
77 |
+ } |
|
78 |
+ |
|
79 |
+ @Override |
|
80 |
+ public Project getProject() { |
|
81 |
+ return project; |
|
82 |
+ } |
|
83 |
+ |
|
84 |
+ @Override |
|
85 |
+ public Resource getType() { |
|
86 |
+ return Resource.ISSUE_LABEL; |
|
87 |
+ } |
|
88 |
+ }; |
|
89 |
+ } |
|
67 | 90 |
}(No newline at end of file) |
--- app/models/Permission.java
... | ... | @@ -1,70 +0,0 @@ |
1 | -package models; | |
2 | - | |
3 | -import java.util.List; | |
4 | - | |
5 | -import javax.persistence.Entity; | |
6 | -import javax.persistence.Id; | |
7 | -import javax.persistence.ManyToMany; | |
8 | - | |
9 | -import models.enumeration.Operation; | |
10 | -import models.enumeration.Resource; | |
11 | -import models.enumeration.RoleType; | |
12 | -import play.db.ebean.Model; | |
13 | - | |
14 | -/** | |
15 | - * @author "Hwi Ahn" | |
16 | - */ | |
17 | -@Entity | |
18 | -public class Permission extends Model { | |
19 | - private static final long serialVersionUID = 1L; | |
20 | - private static Finder<Long, Permission> find = new Finder<Long, Permission>( | |
21 | - Long.class, Permission.class); | |
22 | - | |
23 | - @Id | |
24 | - public Long id; | |
25 | - | |
26 | - public String resource; | |
27 | - public String operation; | |
28 | - | |
29 | - @ManyToMany(targetEntity = models.Role.class, mappedBy = "permissions") | |
30 | - public List<Role> roles; | |
31 | - | |
32 | - /** | |
33 | - * 해당 유저가 해당 프로젝트에서 해당 리소스와 오퍼레이션을 위한 퍼미션을 가지고 있는지 확인합니다. | |
34 | - * | |
35 | - * @param userId | |
36 | - * @param projectId | |
37 | - * @param resource | |
38 | - * @param operation | |
39 | - * @return | |
40 | - */ | |
41 | - public static boolean hasPermission(Long userId, Long projectId, | |
42 | - Resource resource, Operation operation) { | |
43 | - int findRowCount = find.where() | |
44 | - .eq("roles.projectUsers.user.id", userId) | |
45 | - .eq("roles.projectUsers.project.id", projectId) | |
46 | - .eq("resource", resource.resource()) | |
47 | - .eq("operation", operation.operation()) | |
48 | - .findRowCount(); | |
49 | - return (findRowCount != 0) ? true : false; | |
50 | - } | |
51 | - | |
52 | - public static boolean hasPermission(RoleType roleType, Resource resource, Operation operation) { | |
53 | - int findRowCount = find.where() | |
54 | - .eq("roles.id", roleType.roleType()) | |
55 | - .eq("resource", resource.resource()) | |
56 | - .eq("operation", operation.operation()) | |
57 | - .findRowCount(); | |
58 | - return (findRowCount != 0) ? true : false; | |
59 | - } | |
60 | - | |
61 | - /** | |
62 | - * 해당 롤이 가지고 있는 퍼미션들의 리스트를 반환합니다. | |
63 | - * | |
64 | - * @param roleId | |
65 | - * @return | |
66 | - */ | |
67 | - public static List<Permission> findPermissionsByRole(Long roleId) { | |
68 | - return find.where().eq("roles.id", roleId).findList(); | |
69 | - } | |
70 | -} |
--- app/models/Post.java
+++ app/models/Post.java
... | ... | @@ -7,6 +7,7 @@ |
7 | 7 |
import com.avaje.ebean.*; |
8 | 8 |
import controllers.*; |
9 | 9 |
import models.enumeration.*; |
10 |
+import models.resource.ProjectResource; |
|
10 | 11 |
import models.support.*; |
11 | 12 |
import org.joda.time.*; |
12 | 13 |
import play.data.format.*; |
... | ... | @@ -32,7 +33,7 @@ |
32 | 33 |
|
33 | 34 |
@Constraints.Required |
34 | 35 |
public String contents; |
35 |
- |
|
36 |
+ |
|
36 | 37 |
@Constraints.Required |
37 | 38 |
@Formats.DateTime(pattern = "YYYY/MM/DD/hh/mm/ss") |
38 | 39 |
public Date date; |
... | ... | @@ -114,7 +115,7 @@ |
114 | 115 |
} |
115 | 116 |
post.update(); |
116 | 117 |
} |
117 |
- |
|
118 |
+ |
|
118 | 119 |
public static boolean isAuthor(Long currentUserId, Long id) { |
119 | 120 |
int findRowCount = finder.where().eq("authorId", currentUserId).eq("id", id).findRowCount(); |
120 | 121 |
return (findRowCount != 0) ? true : false; |
... | ... | @@ -140,4 +141,23 @@ |
140 | 141 |
post.commentCount--; |
141 | 142 |
post.update(); |
142 | 143 |
} |
144 |
+ |
|
145 |
+ public ProjectResource asResource() { |
|
146 |
+ return new ProjectResource() { |
|
147 |
+ @Override |
|
148 |
+ public Long getId() { |
|
149 |
+ return id; |
|
150 |
+ } |
|
151 |
+ |
|
152 |
+ @Override |
|
153 |
+ public Project getProject() { |
|
154 |
+ return project; |
|
155 |
+ } |
|
156 |
+ |
|
157 |
+ @Override |
|
158 |
+ public Resource getType() { |
|
159 |
+ return Resource.BOARD_POST; |
|
160 |
+ } |
|
161 |
+ }; |
|
162 |
+ } |
|
143 | 163 |
} |
--- app/models/Project.java
+++ app/models/Project.java
... | ... | @@ -12,14 +12,15 @@ |
12 | 12 |
import javax.persistence.OneToOne; |
13 | 13 |
import javax.servlet.ServletException; |
14 | 14 |
|
15 |
+import models.enumeration.Resource; |
|
15 | 16 |
import models.enumeration.RoleType; |
17 |
+import models.resource.GlobalResource; |
|
16 | 18 |
import models.task.TaskBoard; |
17 | 19 |
|
18 | 20 |
import org.eclipse.jgit.api.errors.GitAPIException; |
19 | 21 |
import org.eclipse.jgit.api.errors.NoHeadException; |
20 | 22 |
import org.joda.time.Duration; |
21 | 23 |
|
22 |
-import play.Logger; |
|
23 | 24 |
import play.data.validation.Constraints; |
24 | 25 |
import play.db.ebean.Model; |
25 | 26 |
import playRepository.Commit; |
... | ... | @@ -30,7 +31,7 @@ |
30 | 31 |
import com.avaje.ebean.Page; |
31 | 32 |
|
32 | 33 |
/** |
33 |
- * |
|
34 |
+ * |
|
34 | 35 |
* @author "Hwi Ahn" |
35 | 36 |
*/ |
36 | 37 |
|
... | ... | @@ -99,7 +100,7 @@ |
99 | 100 |
/** |
100 | 101 |
* 해당 프로젝트가 존재하는지 여부를 검사합니다. 해당 파라미터에 대하여 프로젝트가 존재하면 true, 존재하지 않으면 false를 |
101 | 102 |
* 반환합니다. |
102 |
- * |
|
103 |
+ * |
|
103 | 104 |
* @param userName |
104 | 105 |
* @param projectName |
105 | 106 |
* @return |
... | ... | @@ -112,7 +113,7 @@ |
112 | 113 |
|
113 | 114 |
/** |
114 | 115 |
* 프로젝트 이름을 해당 이름(projectName)으로 변경이 가능한지 검사합니다. |
115 |
- * |
|
116 |
+ * |
|
116 | 117 |
* @param id |
117 | 118 |
* @param userName |
118 | 119 |
* @param projectName |
... | ... | @@ -128,7 +129,7 @@ |
128 | 129 |
/** |
129 | 130 |
* 해당 유저가 속해있는 프로젝트들 중에서 해당 유저가 유일한 Manager인 프로젝트가 있는지 검사하고, 있다면 그 프로젝트들의 |
130 | 131 |
* 리스트를 반환합니다. |
131 |
- * |
|
132 |
+ * |
|
132 | 133 |
* @param userId |
133 | 134 |
* @return |
134 | 135 |
*/ |
... | ... | @@ -150,7 +151,7 @@ |
150 | 151 |
|
151 | 152 |
/** |
152 | 153 |
* 해당 유저가 속해있는 프로젝트들의 리스트를 제공합니다. |
153 |
- * |
|
154 |
+ * |
|
154 | 155 |
* @param ownerId |
155 | 156 |
* @return |
156 | 157 |
*/ |
... | ... | @@ -209,4 +210,21 @@ |
209 | 210 |
return null; |
210 | 211 |
} |
211 | 212 |
} |
212 |
-} |
|
213 |
+ |
|
214 |
+ public GlobalResource asResource() { |
|
215 |
+ return new GlobalResource() { |
|
216 |
+ |
|
217 |
+ @Override |
|
218 |
+ public Long getId() { |
|
219 |
+ return id; |
|
220 |
+ } |
|
221 |
+ |
|
222 |
+ @Override |
|
223 |
+ public Resource getType() { |
|
224 |
+ return Resource.PROJECT; |
|
225 |
+ } |
|
226 |
+ |
|
227 |
+ }; |
|
228 |
+ } |
|
229 |
+ |
|
230 |
+}(No newline at end of file) |
--- app/models/Role.java
+++ app/models/Role.java
... | ... | @@ -20,16 +20,11 @@ |
20 | 20 |
private static Finder<Long, Role> find = new Finder<Long, Role>(Long.class, |
21 | 21 |
Role.class); |
22 | 22 |
|
23 |
- |
|
24 |
- |
|
25 | 23 |
@Id |
26 | 24 |
public Long id; |
27 | 25 |
|
28 | 26 |
public String name; |
29 | 27 |
public boolean active; |
30 |
- |
|
31 |
- @ManyToMany |
|
32 |
- public List<Permission> permissions; |
|
33 | 28 |
|
34 | 29 |
@OneToMany(mappedBy = "role", cascade = CascadeType.ALL) |
35 | 30 |
public List<ProjectUser> projectUsers; |
+++ app/models/SiteAdmin.java
... | ... | @@ -0,0 +1,28 @@ |
1 | +package models; | |
2 | + | |
3 | +import javax.persistence.Entity; | |
4 | +import javax.persistence.Id; | |
5 | +import javax.persistence.OneToOne; | |
6 | + | |
7 | +import play.db.ebean.Model; | |
8 | + | |
9 | +@Entity | |
10 | +public class SiteAdmin extends Model { | |
11 | + /** | |
12 | + * | |
13 | + */ | |
14 | + private static final long serialVersionUID = 1L; | |
15 | + | |
16 | + @Id | |
17 | + public Long id; | |
18 | + | |
19 | + @OneToOne | |
20 | + public User admin; | |
21 | + | |
22 | + public static Model.Finder<Long, SiteAdmin> find = new Finder<Long, SiteAdmin>(Long.class, | |
23 | + SiteAdmin.class); | |
24 | + | |
25 | + public static boolean exists(User user) { | |
26 | + return user != null && find.where().eq("admin.id", user.id).findRowCount() > 0; | |
27 | + } | |
28 | +}(No newline at end of file) |
--- app/models/User.java
+++ app/models/User.java
... | ... | @@ -16,14 +16,13 @@ |
16 | 16 |
import models.enumeration.Matching; |
17 | 17 |
import models.enumeration.Resource; |
18 | 18 |
import models.enumeration.RoleType; |
19 |
+import models.resource.GlobalResource; |
|
19 | 20 |
import models.support.FinderTemplate; |
20 | 21 |
import models.support.OrderParams; |
21 | 22 |
import models.support.SearchParams; |
22 |
-import play.cache.Cached; |
|
23 | 23 |
import play.data.format.Formats; |
24 | 24 |
import play.data.validation.Constraints.*; |
25 | 25 |
import play.db.ebean.Model; |
26 |
-import play.db.ebean.Model.Finder; |
|
27 | 26 |
import utils.JodaDateUtil; |
28 | 27 |
|
29 | 28 |
import com.avaje.ebean.Page; |
... | ... | @@ -49,13 +48,13 @@ |
49 | 48 |
public String password; |
50 | 49 |
public String passwordSalt; |
51 | 50 |
|
52 |
- @Email(message = "user.wrongEmail.alert") |
|
51 |
+ @Email(message = "user.wrongEmail.alert") |
|
53 | 52 |
public String email; |
54 | 53 |
|
55 | 54 |
public String avatarUrl; |
56 |
- |
|
55 |
+ |
|
57 | 56 |
public boolean rememberMe; |
58 |
- |
|
57 |
+ |
|
59 | 58 |
@Formats.DateTime(pattern = "yyyy-MM-dd") |
60 | 59 |
public Date date; |
61 | 60 |
|
... | ... | @@ -71,7 +70,7 @@ |
71 | 70 |
SimpleDateFormat sdf = new SimpleDateFormat("MMM dd yyyy"); |
72 | 71 |
return sdf.format(this.date); |
73 | 72 |
} |
74 |
- |
|
73 |
+ |
|
75 | 74 |
public List<Project> myProjects(){ |
76 | 75 |
return Project.findProjectsByMember(id); |
77 | 76 |
} |
... | ... | @@ -97,7 +96,7 @@ |
97 | 96 |
|
98 | 97 |
/** |
99 | 98 |
* 존재하는 유저인지를 검사합니다. |
100 |
- * |
|
99 |
+ * |
|
101 | 100 |
* @param loginId |
102 | 101 |
* @return |
103 | 102 |
*/ |
... | ... | @@ -116,7 +115,7 @@ |
116 | 115 |
|
117 | 116 |
/** |
118 | 117 |
* Site manager를 제외한 사이트에 가입된 유저들의 리스트를 Page 형태로 반환합니다. |
119 |
- * |
|
118 |
+ * |
|
120 | 119 |
* @param pageNum |
121 | 120 |
* @param loginId |
122 | 121 |
* @return |
... | ... | @@ -139,7 +138,7 @@ |
139 | 138 |
|
140 | 139 |
/** |
141 | 140 |
* 해당 프로젝트에 속하는 유저들의 리스트를 제공합니다. (Site manager는 hidden role로서 반환되지 않습니다.) |
142 |
- * |
|
141 |
+ * |
|
143 | 142 |
* @param projectId |
144 | 143 |
* @return |
145 | 144 |
*/ |
... | ... | @@ -148,7 +147,7 @@ |
148 | 147 |
.ne("projectUser.role.id", RoleType.SITEMANAGER.roleType()) |
149 | 148 |
.findList(); |
150 | 149 |
} |
151 |
- |
|
150 |
+ |
|
152 | 151 |
public Long avatarId(){ |
153 | 152 |
return Attachment.findByContainer(Resource.USER_AVATAR, id).get(0).id; |
154 | 153 |
} |
... | ... | @@ -157,4 +156,26 @@ |
157 | 156 |
User user = find.where().ieq("email", emailAddress).findUnique(); |
158 | 157 |
return user != null; |
159 | 158 |
} |
159 |
+ |
|
160 |
+ public boolean isAnonymous() { |
|
161 |
+ return this == UserApp.anonymous; |
|
162 |
+ } |
|
163 |
+ |
|
164 |
+ public GlobalResource asResource() { |
|
165 |
+ return new GlobalResource() { |
|
166 |
+ @Override |
|
167 |
+ public Long getId() { |
|
168 |
+ return null; |
|
169 |
+ } |
|
170 |
+ |
|
171 |
+ @Override |
|
172 |
+ public Resource getType() { |
|
173 |
+ return Resource.USER; |
|
174 |
+ } |
|
175 |
+ }; |
|
176 |
+ } |
|
177 |
+ |
|
178 |
+ public boolean isSiteManager() { |
|
179 |
+ return SiteAdmin.exists(this); |
|
180 |
+ } |
|
160 | 181 |
} |
--- app/models/enumeration/Operation.java
+++ app/models/enumeration/Operation.java
... | ... | @@ -1,18 +1,18 @@ |
1 | 1 |
package models.enumeration; |
2 | 2 |
|
3 | 3 |
public enum Operation { |
4 |
- READ("read"), WRITE("write"), EDIT("edit"), DELETE("delete"); |
|
5 |
- |
|
4 |
+ READ("read"), UPDATE("edit"), DELETE("delete"); |
|
5 |
+ |
|
6 | 6 |
private String operation; |
7 |
- |
|
7 |
+ |
|
8 | 8 |
Operation(String operation) { |
9 | 9 |
this.operation = operation; |
10 | 10 |
} |
11 |
- |
|
11 |
+ |
|
12 | 12 |
public String operation() { |
13 | 13 |
return this.operation; |
14 | 14 |
} |
15 |
- |
|
15 |
+ |
|
16 | 16 |
public static Operation getValue(String value) { |
17 | 17 |
for (Operation operation : Operation.values()) { |
18 | 18 |
if (operation.operation().equals(value)) { |
... | ... | @@ -21,4 +21,4 @@ |
21 | 21 |
} |
22 | 22 |
return Operation.READ; |
23 | 23 |
} |
24 |
-} |
|
24 |
+}(No newline at end of file) |
--- app/models/enumeration/Resource.java
+++ app/models/enumeration/Resource.java
... | ... | @@ -40,4 +40,4 @@ |
40 | 40 |
} |
41 | 41 |
return Resource.ISSUE_POST; |
42 | 42 |
} |
43 |
-} |
|
43 |
+}(No newline at end of file) |
+++ app/models/resource/GlobalResource.java
... | ... | @@ -0,0 +1,8 @@ |
1 | +package models.resource; | |
2 | + | |
3 | +import models.enumeration.Resource; | |
4 | + | |
5 | +public interface GlobalResource { | |
6 | + public Long getId(); | |
7 | + public Resource getType(); | |
8 | +} |
+++ app/models/resource/ProjectResource.java
... | ... | @@ -0,0 +1,10 @@ |
1 | +package models.resource; | |
2 | + | |
3 | +import models.Project; | |
4 | +import models.enumeration.Resource; | |
5 | + | |
6 | +public interface ProjectResource { | |
7 | + public Long getId(); | |
8 | + public Project getProject(); | |
9 | + public Resource getType(); | |
10 | +}(No newline at end of file) |
--- app/playRepository/GitRepository.java
+++ app/playRepository/GitRepository.java
... | ... | @@ -6,6 +6,10 @@ |
6 | 6 |
import java.util.LinkedList; |
7 | 7 |
import java.util.List; |
8 | 8 |
|
9 |
+import models.Project; |
|
10 |
+import models.enumeration.Resource; |
|
11 |
+import models.resource.ProjectResource; |
|
12 |
+ |
|
9 | 13 |
import org.codehaus.jackson.node.ObjectNode; |
10 | 14 |
import org.eclipse.jgit.api.Git; |
11 | 15 |
import org.eclipse.jgit.api.LogCommand; |
... | ... | @@ -18,6 +22,8 @@ |
18 | 22 |
import org.eclipse.jgit.treewalk.filter.PathFilter; |
19 | 23 |
import org.eclipse.jgit.treewalk.EmptyTreeIterator; |
20 | 24 |
import org.eclipse.jgit.diff.DiffEntry; |
25 |
+ |
|
26 |
+import controllers.ProjectApp; |
|
21 | 27 |
|
22 | 28 |
import play.Logger; |
23 | 29 |
import play.libs.Json; |
... | ... | @@ -35,8 +41,12 @@ |
35 | 41 |
} |
36 | 42 |
|
37 | 43 |
private final Repository repository; |
44 |
+ private final String ownerName; |
|
45 |
+ private final String projectName; |
|
38 | 46 |
|
39 | 47 |
public GitRepository(String userName, String projectName) throws IOException { |
48 |
+ this.ownerName = userName; |
|
49 |
+ this.projectName = projectName; |
|
40 | 50 |
this.repository = createGitRepository(userName, projectName); |
41 | 51 |
} |
42 | 52 |
|
... | ... | @@ -108,7 +118,7 @@ |
108 | 118 |
|
109 | 119 |
RevCommit commit = git.log().addPath(path).call().iterator().next(); |
110 | 120 |
ObjectNode result = Json.newObject(); |
111 |
- |
|
121 |
+ |
|
112 | 122 |
result.put("type", "file"); |
113 | 123 |
result.put("msg", commit.getShortMessage()); |
114 | 124 |
result.put("author", commit.getAuthorIdent().getName()); |
... | ... | @@ -168,7 +178,7 @@ |
168 | 178 |
|
169 | 179 |
RevCommit commit = git.log().addPath(path).call().iterator().next(); |
170 | 180 |
ObjectNode result = Json.newObject(); |
171 |
- |
|
181 |
+ |
|
172 | 182 |
result.put("type", "file"); |
173 | 183 |
result.put("msg", commit.getShortMessage()); |
174 | 184 |
result.put("author", commit.getAuthorIdent().getName()); |
... | ... | @@ -190,7 +200,7 @@ |
190 | 200 |
NoHeadException { |
191 | 201 |
ObjectNode result = Json.newObject(); |
192 | 202 |
result.put("type", "folder"); |
193 |
- |
|
203 |
+ |
|
194 | 204 |
ObjectNode listData = Json.newObject(); |
195 | 205 |
|
196 | 206 |
while (treeWalk.next()) { |
... | ... | @@ -284,5 +294,24 @@ |
284 | 294 |
return new ArrayList<String>(repository.getAllRefs().keySet()); |
285 | 295 |
} |
286 | 296 |
|
297 |
+ @Override |
|
298 |
+ public ProjectResource asResource() { |
|
299 |
+ return new ProjectResource() { |
|
300 |
+ @Override |
|
301 |
+ public Long getId() { |
|
302 |
+ return null; |
|
303 |
+ } |
|
287 | 304 |
|
305 |
+ @Override |
|
306 |
+ public Project getProject() { |
|
307 |
+ return ProjectApp.getProject(ownerName, projectName); |
|
308 |
+ } |
|
309 |
+ |
|
310 |
+ @Override |
|
311 |
+ public Resource getType() { |
|
312 |
+ return Resource.CODE; |
|
313 |
+ } |
|
314 |
+ |
|
315 |
+ }; |
|
316 |
+ } |
|
288 | 317 |
} |
--- app/playRepository/PlayRepository.java
+++ app/playRepository/PlayRepository.java
... | ... | @@ -3,6 +3,8 @@ |
3 | 3 |
import java.io.IOException; |
4 | 4 |
import java.util.List; |
5 | 5 |
|
6 |
+import models.resource.ProjectResource; |
|
7 |
+ |
|
6 | 8 |
import org.codehaus.jackson.node.ObjectNode; |
7 | 9 |
import org.eclipse.jgit.api.errors.*; |
8 | 10 |
import org.eclipse.jgit.errors.*; |
... | ... | @@ -11,26 +13,26 @@ |
11 | 13 |
|
12 | 14 |
public interface PlayRepository { |
13 | 15 |
|
14 |
- public void create() throws IOException, ClientException; |
|
16 |
+ public abstract void create() throws IOException, ClientException; |
|
15 | 17 |
|
16 |
- |
|
17 |
- public ObjectNode findFileInfo(String path) throws IOException, NoHeadException, |
|
18 |
+ public abstract ObjectNode findFileInfo(String path) throws IOException, NoHeadException, |
|
18 | 19 |
GitAPIException, SVNException; |
19 | 20 |
|
20 |
- public byte[] getRawFile(String path) throws MissingObjectException, |
|
21 |
+ public abstract byte[] getRawFile(String path) throws MissingObjectException, |
|
21 | 22 |
IncorrectObjectTypeException, AmbiguousObjectException, IOException, SVNException; |
22 | 23 |
|
23 |
- public void delete(); |
|
24 |
+ public abstract void delete(); |
|
24 | 25 |
|
25 |
- public String getPatch(String commitId) throws GitAPIException, MissingObjectException, |
|
26 |
+ public abstract String getPatch(String commitId) throws GitAPIException, MissingObjectException, |
|
26 | 27 |
IncorrectObjectTypeException, IOException, SVNException; |
27 | 28 |
|
28 |
- public List<Commit> getHistory(int page, int limit, String branch) throws AmbiguousObjectException, |
|
29 |
+ public abstract List<Commit> getHistory(int page, int limit, String branch) throws AmbiguousObjectException, |
|
29 | 30 |
IOException, NoHeadException, GitAPIException, SVNException; |
30 | 31 |
|
31 |
- public List<String> getBranches(); |
|
32 |
+ public abstract List<String> getBranches(); |
|
32 | 33 |
|
33 | 34 |
|
34 |
- public ObjectNode findFileInfo(String branch, String path) throws AmbiguousObjectException, IOException, SVNException, NoHeadException, GitAPIException; |
|
35 |
+ public abstract ObjectNode findFileInfo(String branch, String path) throws AmbiguousObjectException, IOException, SVNException, NoHeadException, GitAPIException; |
|
35 | 36 |
|
37 |
+ public abstract ProjectResource asResource(); |
|
36 | 38 |
}(No newline at end of file) |
--- app/playRepository/SVNRepository.java
+++ app/playRepository/SVNRepository.java
... | ... | @@ -5,6 +5,10 @@ |
5 | 5 |
|
6 | 6 |
import javax.servlet.*; |
7 | 7 |
|
8 |
+import models.Project; |
|
9 |
+import models.enumeration.Resource; |
|
10 |
+import models.resource.ProjectResource; |
|
11 |
+ |
|
8 | 12 |
import org.codehaus.jackson.node.ObjectNode; |
9 | 13 |
|
10 | 14 |
import org.eclipse.jgit.api.errors.GitAPIException; |
... | ... | @@ -17,6 +21,8 @@ |
17 | 21 |
import org.tmatesoft.svn.core.wc.SVNClientManager; |
18 | 22 |
import org.tmatesoft.svn.core.wc.SVNDiffClient; |
19 | 23 |
import org.tmatesoft.svn.core.wc.SVNRevision; |
24 |
+ |
|
25 |
+import controllers.ProjectApp; |
|
20 | 26 |
|
21 | 27 |
import play.libs.Json; |
22 | 28 |
|
... | ... | @@ -35,68 +41,69 @@ |
35 | 41 |
SVNRepository.repoPrefix = repoPrefix; |
36 | 42 |
} |
37 | 43 |
|
38 |
- private String projectName; |
|
44 |
+ private final String projectName; |
|
39 | 45 |
|
40 |
- private String userName; |
|
46 |
+ private final String ownerName; |
|
41 | 47 |
|
42 | 48 |
public SVNRepository(final String userName, String projectName) throws ServletException { |
43 |
- this.userName = userName; |
|
49 |
+ this.ownerName = userName; |
|
44 | 50 |
this.projectName = projectName; |
45 | 51 |
} |
46 | 52 |
|
47 |
- |
|
53 |
+ @Override |
|
48 | 54 |
public byte[] getRawFile(String path) throws SVNException { |
49 |
- SVNURL svnURL = SVNURL.fromFile(new File(getRepoPrefix() + userName + "/" + projectName)); |
|
55 |
+ SVNURL svnURL = SVNURL.fromFile(new File(getRepoPrefix() + ownerName + "/" + projectName)); |
|
50 | 56 |
org.tmatesoft.svn.core.io.SVNRepository repository = SVNRepositoryFactory.create(svnURL); |
51 | 57 |
ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
52 | 58 |
repository.getFile(path, -1l, null, baos); |
53 | 59 |
return baos.toByteArray(); |
54 | 60 |
} |
55 | 61 |
|
62 |
+ @Override |
|
56 | 63 |
public ObjectNode findFileInfo(String path) throws SVNException { |
57 |
- SVNURL svnURL = SVNURL.fromFile(new File(getRepoPrefix() + userName + "/" + projectName)); |
|
64 |
+ SVNURL svnURL = SVNURL.fromFile(new File(getRepoPrefix() + ownerName + "/" + projectName)); |
|
58 | 65 |
org.tmatesoft.svn.core.io.SVNRepository repository = SVNRepositoryFactory.create(svnURL); |
59 |
- |
|
66 |
+ |
|
60 | 67 |
SVNNodeKind nodeKind = repository.checkPath(path , -1 ); |
61 |
- |
|
68 |
+ |
|
62 | 69 |
if(nodeKind == SVNNodeKind.DIR){ |
63 | 70 |
//폴더 내용 출력 |
64 | 71 |
ObjectNode result = Json.newObject(); |
65 |
- |
|
72 |
+ |
|
66 | 73 |
result.put("type", "folder"); |
67 | 74 |
ObjectNode listData = Json.newObject(); |
68 |
- |
|
75 |
+ |
|
69 | 76 |
SVNProperties prop = new SVNProperties(); |
70 |
- |
|
77 |
+ |
|
71 | 78 |
Collection entries = repository.getDir(path, -1, prop, (Collection)null); |
72 |
- |
|
79 |
+ |
|
73 | 80 |
Iterator iterator = entries.iterator( ); |
74 | 81 |
while ( iterator.hasNext( ) ) { |
75 | 82 |
SVNDirEntry entry = ( SVNDirEntry ) iterator.next( ); |
76 |
- |
|
83 |
+ |
|
77 | 84 |
ObjectNode data = Json.newObject(); |
78 | 85 |
data.put("type", entry.getKind() == SVNNodeKind.DIR ? "folder" : "file"); |
79 | 86 |
data.put("msg", entry.getCommitMessage()); |
80 | 87 |
data.put("author", entry.getAuthor()); |
81 | 88 |
data.put("date", entry.getDate().toString()); |
82 |
- |
|
89 |
+ |
|
83 | 90 |
listData.put(entry.getName(), data); |
84 | 91 |
} |
85 | 92 |
result.put("data", listData); |
86 |
- |
|
93 |
+ |
|
87 | 94 |
return result; |
88 |
- |
|
95 |
+ |
|
89 | 96 |
} else if(nodeKind == SVNNodeKind.FILE) { |
90 | 97 |
//파일 내용 출력 |
91 | 98 |
ObjectNode result = Json.newObject(); |
92 |
- |
|
93 |
- |
|
99 |
+ |
|
100 |
+ |
|
94 | 101 |
ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
95 | 102 |
SVNProperties prop = new SVNProperties(); |
96 | 103 |
repository.getFile(path, -1l, prop, baos); |
97 |
- |
|
104 |
+ |
|
98 | 105 |
result.put("type", "file"); |
99 |
- |
|
106 |
+ |
|
100 | 107 |
result.put("msg", prop.getStringValue(SVNProperty.COMMITTED_REVISION)); |
101 | 108 |
result.put("author", prop.getStringValue(SVNProperty.LAST_AUTHOR)); |
102 | 109 |
|
... | ... | @@ -111,49 +118,49 @@ |
111 | 118 |
@Override |
112 | 119 |
public ObjectNode findFileInfo(String branch, String path) throws AmbiguousObjectException, |
113 | 120 |
IOException, SVNException { |
114 |
- SVNURL svnURL = SVNURL.fromFile(new File(getRepoPrefix() + userName + "/" + projectName)); |
|
121 |
+ SVNURL svnURL = SVNURL.fromFile(new File(getRepoPrefix() + ownerName + "/" + projectName)); |
|
115 | 122 |
org.tmatesoft.svn.core.io.SVNRepository repository = SVNRepositoryFactory.create(svnURL); |
116 |
- |
|
123 |
+ |
|
117 | 124 |
SVNNodeKind nodeKind = repository.checkPath(path , -1 ); |
118 |
- |
|
125 |
+ |
|
119 | 126 |
if(nodeKind == SVNNodeKind.DIR){ |
120 | 127 |
//폴더 내용 출력 |
121 | 128 |
ObjectNode result = Json.newObject(); |
122 |
- |
|
129 |
+ |
|
123 | 130 |
result.put("type", "folder"); |
124 | 131 |
ObjectNode listData = Json.newObject(); |
125 |
- |
|
132 |
+ |
|
126 | 133 |
SVNProperties prop = new SVNProperties(); |
127 |
- |
|
134 |
+ |
|
128 | 135 |
Collection entries = repository.getDir(path, -1, prop, (Collection)null); |
129 |
- |
|
136 |
+ |
|
130 | 137 |
Iterator iterator = entries.iterator( ); |
131 | 138 |
while ( iterator.hasNext( ) ) { |
132 | 139 |
SVNDirEntry entry = ( SVNDirEntry ) iterator.next( ); |
133 |
- |
|
140 |
+ |
|
134 | 141 |
ObjectNode data = Json.newObject(); |
135 | 142 |
data.put("type", entry.getKind() == SVNNodeKind.DIR ? "folder" : "file"); |
136 | 143 |
data.put("msg", entry.getCommitMessage()); |
137 | 144 |
data.put("author", entry.getAuthor()); |
138 | 145 |
data.put("date", entry.getDate().toString()); |
139 |
- |
|
146 |
+ |
|
140 | 147 |
listData.put(entry.getName(), data); |
141 | 148 |
} |
142 | 149 |
result.put("data", listData); |
143 |
- |
|
150 |
+ |
|
144 | 151 |
return result; |
145 |
- |
|
152 |
+ |
|
146 | 153 |
} else if(nodeKind == SVNNodeKind.FILE) { |
147 | 154 |
//파일 내용 출력 |
148 | 155 |
ObjectNode result = Json.newObject(); |
149 |
- |
|
150 |
- |
|
156 |
+ |
|
157 |
+ |
|
151 | 158 |
ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
152 | 159 |
SVNProperties prop = new SVNProperties(); |
153 | 160 |
repository.getFile(path, -1l, prop, baos); |
154 |
- |
|
161 |
+ |
|
155 | 162 |
result.put("type", "file"); |
156 |
- |
|
163 |
+ |
|
157 | 164 |
result.put("msg", prop.getStringValue(SVNProperty.COMMITTED_REVISION)); |
158 | 165 |
result.put("author", prop.getStringValue(SVNProperty.LAST_AUTHOR)); |
159 | 166 |
|
... | ... | @@ -168,20 +175,20 @@ |
168 | 175 |
|
169 | 176 |
@Override |
170 | 177 |
public void create() throws ClientException { |
171 |
- String svnPath = new File(SVNRepository.getRepoPrefix() + userName + "/" + projectName) |
|
178 |
+ String svnPath = new File(SVNRepository.getRepoPrefix() + ownerName + "/" + projectName) |
|
172 | 179 |
.getAbsolutePath(); |
173 | 180 |
new org.tigris.subversion.javahl.SVNAdmin().create(svnPath, false, false, null, "fsfs"); |
174 | 181 |
} |
175 | 182 |
|
176 | 183 |
@Override |
177 | 184 |
public void delete() { |
178 |
- FileUtil.rm_rf(new File(getRepoPrefix() + userName + "/" + projectName)); |
|
185 |
+ FileUtil.rm_rf(new File(getRepoPrefix() + ownerName + "/" + projectName)); |
|
179 | 186 |
} |
180 | 187 |
|
181 | 188 |
@Override |
182 | 189 |
public String getPatch(String commitId) throws SVNException { |
183 | 190 |
// Prepare required arguments. |
184 |
- SVNURL svnURL = SVNURL.fromFile(new File(getRepoPrefix() + userName + "/" + projectName)); |
|
191 |
+ SVNURL svnURL = SVNURL.fromFile(new File(getRepoPrefix() + ownerName + "/" + projectName)); |
|
185 | 192 |
long rev = Integer.parseInt(commitId); |
186 | 193 |
|
187 | 194 |
// Get diffClient. |
... | ... | @@ -201,7 +208,7 @@ |
201 | 208 |
public List<Commit> getHistory(int page, int limit, String until) throws AmbiguousObjectException, |
202 | 209 |
IOException, NoHeadException, GitAPIException, SVNException { |
203 | 210 |
// Get the repository |
204 |
- SVNURL svnURL = SVNURL.fromFile(new File(repoPrefix + userName + "/" + projectName)); |
|
211 |
+ SVNURL svnURL = SVNURL.fromFile(new File(repoPrefix + ownerName + "/" + projectName)); |
|
205 | 212 |
org.tmatesoft.svn.core.io.SVNRepository repository = SVNRepositoryFactory.create(svnURL); |
206 | 213 |
|
207 | 214 |
// path to get log |
... | ... | @@ -228,5 +235,24 @@ |
228 | 235 |
return new ArrayList<String>(); |
229 | 236 |
} |
230 | 237 |
|
231 |
- |
|
238 |
+ @Override |
|
239 |
+ public ProjectResource asResource() { |
|
240 |
+ return new ProjectResource() { |
|
241 |
+ @Override |
|
242 |
+ public Long getId() { |
|
243 |
+ return null; |
|
244 |
+ } |
|
245 |
+ |
|
246 |
+ @Override |
|
247 |
+ public Project getProject() { |
|
248 |
+ return ProjectApp.getProject(ownerName, projectName); |
|
249 |
+ } |
|
250 |
+ |
|
251 |
+ @Override |
|
252 |
+ public Resource getType() { |
|
253 |
+ return Resource.CODE; |
|
254 |
+ } |
|
255 |
+ |
|
256 |
+ }; |
|
257 |
+ } |
|
232 | 258 |
} |
--- app/utils/AccessControl.java
+++ app/utils/AccessControl.java
... | ... | @@ -3,101 +3,146 @@ |
3 | 3 |
import models.Comment; |
4 | 4 |
import models.Issue; |
5 | 5 |
import models.IssueComment; |
6 |
-import models.Permission; |
|
7 | 6 |
import models.Post; |
8 | 7 |
import models.Project; |
9 | 8 |
import models.ProjectUser; |
9 |
+import models.User; |
|
10 | 10 |
import models.enumeration.Operation; |
11 | 11 |
import models.enumeration.Resource; |
12 |
-import models.enumeration.RoleType; |
|
13 |
- |
|
14 |
-import org.h2.util.StringUtils; |
|
12 |
+import models.resource.GlobalResource; |
|
13 |
+import models.resource.ProjectResource; |
|
15 | 14 |
|
16 | 15 |
import play.db.ebean.Model; |
17 | 16 |
import play.db.ebean.Model.Finder; |
18 | 17 |
|
19 | 18 |
/** |
20 | 19 |
* @author "Hwi Ahn" |
20 |
+ * @author "Yi EungJun" |
|
21 | 21 |
*/ |
22 | 22 |
public class AccessControl { |
23 | 23 |
|
24 |
- /** |
|
25 |
- * |
|
26 |
- * @param userId |
|
27 |
- * @param projectId |
|
28 |
- * @param resource |
|
29 |
- * @param operation |
|
30 |
- * @param resourceId |
|
31 |
- * @return |
|
32 |
- */ |
|
33 |
- public static boolean isAllowed(Object userSessionId, Long projectId, |
|
34 |
- Resource resource, Operation operation, Long resourceId) { |
|
35 |
- Long userId = 0l; |
|
36 |
- if (userSessionId instanceof String) { |
|
37 |
- if (StringUtils.isNumber(userSessionId.toString())) { |
|
38 |
- userId = Long.parseLong((String) userSessionId); |
|
39 |
- } |
|
40 |
- } else { |
|
41 |
- userId = (Long) userSessionId; |
|
42 |
- } |
|
24 |
+ public static boolean isCreatable(User user, Resource resourceType) { |
|
25 |
+ // Only login users can create a resource. |
|
26 |
+ return !user.isAnonymous(); |
|
27 |
+ } |
|
43 | 28 |
|
44 |
- // Site administrator is all-mighty. |
|
45 |
- /* |
|
46 |
- * if (Permission.hasPermission(userId, 0l, Resource.SITE_SETTING, |
|
47 |
- * Operation.WRITE)) { return true; } |
|
48 |
- */ |
|
29 |
+ public static boolean isCreatable(User user, Project project, Resource resourceType) { |
|
30 |
+ if (user.isSiteManager()) { |
|
31 |
+ return true; |
|
32 |
+ } |
|
49 | 33 |
|
50 |
- boolean isAuthorEditible; |
|
34 |
+ if (ProjectUser.isMember(user.id, project.id)) { |
|
35 |
+ // Project members can create anything. |
|
36 |
+ return true; |
|
37 |
+ } else { |
|
38 |
+ // If the project is private, nonmembers cannot create anything. |
|
39 |
+ if (!project.share_option) { |
|
40 |
+ return false; |
|
41 |
+ } |
|
51 | 42 |
|
52 |
- switch (resource) { |
|
53 |
- case ISSUE_POST: |
|
54 |
- isAuthorEditible = isAuthor(userId, resourceId, |
|
55 |
- new Finder<Long, Issue>(Long.class, Issue.class)) |
|
56 |
- && Project.find.byId(projectId).isAuthorEditable; |
|
57 |
- break; |
|
58 |
- case ISSUE_COMMENT: |
|
59 |
- isAuthorEditible = isAuthor(userId, resourceId, |
|
60 |
- new Finder<Long, IssueComment>(Long.class, |
|
61 |
- IssueComment.class)); |
|
62 |
- break; |
|
63 |
- case BOARD_POST: |
|
64 |
- isAuthorEditible = isAuthor(userId, resourceId, |
|
65 |
- new Finder<Long, Post>(Long.class, Post.class)); |
|
66 |
- break; |
|
67 |
- case BOARD_COMMENT: |
|
68 |
- isAuthorEditible = isAuthor(userId, resourceId, |
|
69 |
- new Finder<Long, Comment>(Long.class, Comment.class)); |
|
70 |
- break; |
|
71 |
- case USER: |
|
72 |
- return userId == resourceId; |
|
73 |
- case ISSUE_LABEL: |
|
74 |
- return ProjectUser.isMember(userId, projectId); |
|
75 |
- case PROJECT: |
|
76 |
- return Project.find.byId(projectId).share_option |
|
77 |
- || ProjectUser.isMember(userId, projectId); |
|
78 |
- case USER_AVATAR: // Public Acess |
|
79 |
- return true; |
|
80 |
- default: |
|
81 |
- isAuthorEditible = false; |
|
82 |
- break; |
|
83 |
- } |
|
84 |
- |
|
85 |
- if (ProjectUser.isMember(userId, projectId)) { |
|
86 |
- return isAuthorEditible |
|
87 |
- || Permission.hasPermission(userId, projectId, resource, |
|
88 |
- operation); |
|
89 |
- } else { // Anonymous |
|
90 |
- if (!Project.find.byId(projectId).share_option) { |
|
91 |
- return false; |
|
92 |
- } |
|
93 |
- return isAuthorEditible |
|
94 |
- || Permission.hasPermission(RoleType.ANONYMOUS, resource, |
|
95 |
- operation); |
|
96 |
- } |
|
97 |
- } |
|
43 |
+ // If the project is public, login users can create issues and posts. |
|
44 |
+ if (project.share_option && !user.isAnonymous()) { |
|
45 |
+ switch(resourceType){ |
|
46 |
+ case ISSUE_POST: |
|
47 |
+ case BOARD_POST: |
|
48 |
+ return true; |
|
49 |
+ default: |
|
50 |
+ return false; |
|
51 |
+ } |
|
52 |
+ } |
|
53 |
+ |
|
54 |
+ return false; |
|
55 |
+ } |
|
56 |
+ } |
|
57 |
+ |
|
58 |
+ public static boolean isAllowed(User user, GlobalResource resource, Operation operation) { |
|
59 |
+ if (user.isSiteManager()) { |
|
60 |
+ return true; |
|
61 |
+ } |
|
62 |
+ |
|
63 |
+ if (operation.equals(Operation.READ)) { |
|
64 |
+ if (resource.getType().equals(Resource.PROJECT)) { |
|
65 |
+ Project project = Project.find.byId(resource.getId()); |
|
66 |
+ return project.share_option || ProjectUser.isMember(user.id, project.id); |
|
67 |
+ } |
|
68 |
+ |
|
69 |
+ // anyone can read all resources which doesn't belong to a project. |
|
70 |
+ return true; |
|
71 |
+ } |
|
72 |
+ |
|
73 |
+ // UPDATE, DELETE |
|
74 |
+ switch(resource.getType()){ |
|
75 |
+ case USER: |
|
76 |
+ case USER_AVATAR: |
|
77 |
+ return user.id == resource.getId(); |
|
78 |
+ case PROJECT: |
|
79 |
+ return ProjectUser.isManager(user.id, resource.getId()); |
|
80 |
+ default: |
|
81 |
+ // undefined |
|
82 |
+ return false; |
|
83 |
+ } |
|
84 |
+ } |
|
85 |
+ |
|
86 |
+ public static boolean isAllowed(User user, ProjectResource resource, Operation operation) { |
|
87 |
+ if (user.isSiteManager()) { |
|
88 |
+ return true; |
|
89 |
+ } |
|
90 |
+ |
|
91 |
+ Project project = resource.getProject(); |
|
92 |
+ |
|
93 |
+ if (ProjectUser.isManager(user.id, project.id)) { |
|
94 |
+ return true; |
|
95 |
+ } |
|
96 |
+ |
|
97 |
+ switch(operation) { |
|
98 |
+ case READ: |
|
99 |
+ // If the project is public anyone can read its resources else only members can do that. |
|
100 |
+ return project.share_option || ProjectUser.isMember(user.id, project.id); |
|
101 |
+ case UPDATE: |
|
102 |
+ // Any members can update repository |
|
103 |
+ if (resource.getType() == Resource.CODE) { |
|
104 |
+ return ProjectUser.isMember(user.id, project.id); |
|
105 |
+ } else { |
|
106 |
+ return isEditableAsAuthor(user, project, resource); |
|
107 |
+ } |
|
108 |
+ case DELETE: |
|
109 |
+ return isEditableAsAuthor(user, project, resource); |
|
110 |
+ default: |
|
111 |
+ // undefined |
|
112 |
+ return false; |
|
113 |
+ } |
|
114 |
+ } |
|
115 |
+ |
|
116 |
+ private static boolean isEditableAsAuthor(User user, Project project, ProjectResource resource) { |
|
117 |
+ // author can update or delete her/his own article. |
|
118 |
+ if (!project.isAuthorEditable) { |
|
119 |
+ return false; |
|
120 |
+ } |
|
121 |
+ |
|
122 |
+ Finder<Long, ?> finder; |
|
123 |
+ |
|
124 |
+ switch (resource.getType()) { |
|
125 |
+ case ISSUE_POST: |
|
126 |
+ finder = new Finder<Long, Issue>(Long.class, Issue.class); |
|
127 |
+ break; |
|
128 |
+ case ISSUE_COMMENT: |
|
129 |
+ finder = new Finder<Long, IssueComment>(Long.class, IssueComment.class); |
|
130 |
+ break; |
|
131 |
+ case BOARD_POST: |
|
132 |
+ finder = new Finder<Long, Post>(Long.class, Post.class); |
|
133 |
+ break; |
|
134 |
+ case BOARD_COMMENT: |
|
135 |
+ finder = new Finder<Long, Comment>(Long.class, Comment.class); |
|
136 |
+ break; |
|
137 |
+ default: |
|
138 |
+ return false; |
|
139 |
+ } |
|
140 |
+ |
|
141 |
+ return isAuthor(user.id, resource.getId(), finder); |
|
142 |
+ } |
|
98 | 143 |
|
99 | 144 |
/** |
100 |
- * |
|
145 |
+ * |
|
101 | 146 |
* @param userId |
102 | 147 |
* @param resourceId |
103 | 148 |
* @param finder |
... | ... | @@ -108,14 +153,5 @@ |
108 | 153 |
int findRowCount = finder.where().eq("authorId", userId) |
109 | 154 |
.eq("id", resourceId).findRowCount(); |
110 | 155 |
return (findRowCount != 0) ? true : false; |
111 |
- } |
|
112 |
- |
|
113 |
- public static boolean isAllowed(Object userSessionId, Project project) { |
|
114 |
- if (isAllowed(userSessionId, project.id, Resource.PROJECT, |
|
115 |
- Operation.READ, project.id)) { |
|
116 |
- return true; |
|
117 |
- } else { |
|
118 |
- return false; |
|
119 |
- } |
|
120 | 156 |
} |
121 | 157 |
}(No newline at end of file) |
--- app/views/board/post.scala.html
+++ app/views/board/post.scala.html
... | ... | @@ -1,6 +1,7 @@ |
1 | 1 |
@(post:Post, commentForm:Form[Comment], project:Project) |
2 | 2 |
|
3 | 3 |
@import utils.TemplateHelper._ |
4 |
+@import utils.AccessControl._ |
|
4 | 5 |
@import models.enumeration.Resource |
5 | 6 |
@implicitField = @{ helper.FieldConstructor(simpleForm) } |
6 | 7 |
|
... | ... | @@ -42,7 +43,7 @@ |
42 | 43 |
<a href="@routes.UserApp.userInfo(comment.authorLoginId)" class="pull-left img-rounded"> |
43 | 44 |
<img class="user-picture" src="@User.findByLoginId(comment.authorLoginId).avatarUrl" width="34" height="34" alt="@comment.authorName"> |
44 | 45 |
<div class="media-body"> |
45 |
- @roleCheck(session.get("userId"), project.id, Resource.BOARD_COMMENT, Operation.DELETE, comment.id){ |
|
46 |
+ @if(isAllowed(UserApp.currentUser(), comment.asResource(), Operation.DELETE)){ |
|
46 | 47 |
<a class="pull-right close" href="@routes.BoardApp.deleteComment(project.owner, project.name, post.id, comment.id)">×</a> |
47 | 48 |
} |
48 | 49 |
<p class="commenter"><a href="@routes.UserApp.userInfo(comment.authorLoginId)"><strong>@comment.authorName</strong></a><!-- <span class="name">(Sam sstephenson)</span> --> <span class="date">@utils.TemplateHelper.agoString(post.ago())</span></p> |
... | ... | @@ -56,7 +57,7 @@ |
56 | 57 |
</li> |
57 | 58 |
} |
58 | 59 |
</ul> |
59 |
- @roleCheck(session.get("userId"), project.id, Resource.BOARD_COMMENT, Operation.WRITE){ |
|
60 |
+ @if(isCreatable(User.findByLoginId(session.get("loginId")), project, models.enumeration.Resource.BOARD_POST)){ |
|
60 | 61 |
<div class="write-comment-box"> |
61 | 62 |
@helper.form(routes.BoardApp.newComment(project.owner, project.name, post.id), 'class->"nm", 'enctype -> "multipart/form-data"){ |
62 | 63 |
<div class="write-comment-wrap"> |
... | ... | @@ -120,12 +121,12 @@ |
120 | 121 |
<div class="board-footer"> |
121 | 122 |
<!--<a href="/add-notification" class="add-btn"><i class="ico ico-plus-blue"></i>자동알림추가</a>--> |
122 | 123 |
<a href="@routes.BoardApp.posts(project.owner, project.name)" class="n-btn gray small">LIST</a> |
123 |
- @roleCheck(session.get("userId"), project.id, Resource.BOARD_POST, Operation.DELETE){ |
|
124 |
+ @isAllowed(UserApp.currentUser(), post.asResource(), Operation.DELETE)){ |
|
124 | 125 |
<a data-toggle="modal" href="#deleteConfirm" class="n-btn gray small">DELETE</a> |
125 |
- } |
|
126 |
- @roleCheck(session.get("userId"), project.id, Resource.BOARD_POST, Operation.EDIT){ |
|
126 |
+ } |
|
127 |
+ @isAllowed(UserApp.currentUser(), post.asResource(), Operation.UPDATE)){ |
|
127 | 128 |
<a href="@routes.BoardApp.editPost(project.owner, project.name, post.id)" class="n-btn blue small">EDIT</a> |
128 |
- } |
|
129 |
+ } |
|
129 | 130 |
</div> |
130 | 131 |
|
131 | 132 |
<!--삭제확인상자--> |
--- app/views/board/postList.scala.html
+++ app/views/board/postList.scala.html
... | ... | @@ -1,6 +1,7 @@ |
1 | 1 |
@(title:String, project:Project, page:com.avaje.ebean.Page[Post], param:BoardApp.SearchCondition) |
2 | 2 |
|
3 | 3 |
@import utils.TemplateHelper._ |
4 |
+@import utils.AccessControl._ |
|
4 | 5 |
|
5 | 6 |
@header(label:String, key:String) = { |
6 | 7 |
<th> |
... | ... | @@ -72,7 +73,7 @@ |
72 | 73 |
} |
73 | 74 |
</ul> |
74 | 75 |
} |
75 |
- @roleCheck(session.get("userId"), project.id, models.enumeration.Resource.BOARD_POST, models.enumeration.Operation.WRITE){ |
|
76 |
+ @if(isCreatable(User.findByLoginId(session.get("loginId")), project, models.enumeration.Resource.BOARD_POST)){ |
|
76 | 77 |
<div class="write-btn-wrap"> |
77 | 78 |
<a href="@routes.BoardApp.newPostForm(project.owner, project.name)" class="n-btn blue small">WRITE</a> |
78 | 79 |
</div> |
--- app/views/issue/editIssue.scala.html
+++ app/views/issue/editIssue.scala.html
... | ... | @@ -3,12 +3,8 @@ |
3 | 3 |
@import scala.collection.mutable.Map |
4 | 4 |
@implicitFieldConstructor = @{ FieldConstructor(twitterBootstrapInput.render) } |
5 | 5 |
@import models.enumeration.Resource |
6 |
- |
|
7 |
-@isVisible(resource: models.enumeration.Resource)(content: => Html) = @{ |
|
8 |
- roleCheck(session.get("userId"), project.id, resource, models.enumeration.Operation.EDIT){ |
|
9 |
- content |
|
10 |
- } |
|
11 |
-} |
|
6 |
+@import models.enumeration.Operation |
|
7 |
+@import utils.AccessControl._ |
|
12 | 8 |
|
13 | 9 |
@main(Messages(title), project, utils.MenuType.ISSUE) { |
14 | 10 |
<div class="page"> |
... | ... | @@ -46,14 +42,14 @@ |
46 | 42 |
|
47 | 43 |
<!-- issue.label js module appends a label selector here. --> |
48 | 44 |
<fieldset class="labels"> |
49 |
- @isVisible(models.enumeration.Resource.ISSUE_STATE) { |
|
45 |
+ @if(isAllowed(UserApp.currentUser(), issue.stateAsResource(), Operation.UPDATE)){ |
|
50 | 46 |
@select( |
51 | 47 |
issueForm("state"), |
52 | 48 |
options(State.OPEN.name -> "Open", State.CLOSED.name -> "Closed"), |
53 | 49 |
'_label-> Messages("issue.state"), |
54 | 50 |
'_showConstraints -> false) |
55 | 51 |
} |
56 |
- @isVisible(models.enumeration.Resource.ISSUE_ASSIGNEE) { |
|
52 |
+ @if(isAllowed(UserApp.currentUser(), issue.assigneeAsResource(), Operation.UPDATE)){ |
|
57 | 53 |
@select( |
58 | 54 |
issueForm("assignee.user.id"), |
59 | 55 |
options(ProjectUser.options(project.id)), |
... | ... | @@ -61,7 +57,7 @@ |
61 | 57 |
'_default -> Messages("issue.new.selectDefault.assignee"), |
62 | 58 |
'_showConstraints -> false) |
63 | 59 |
} |
64 |
- @isVisible(models.enumeration.Resource.ISSUE_MILESTONE) { |
|
60 |
+ @if(isAllowed(UserApp.currentUser(), issue.milestoneAsResource(), Operation.UPDATE)){ |
|
65 | 61 |
@select( |
66 | 62 |
issueForm("milestoneId"), |
67 | 63 |
options(Milestone.options(project.id)), |
--- app/views/issue/issue.scala.html
+++ app/views/issue/issue.scala.html
... | ... | @@ -2,18 +2,15 @@ |
2 | 2 |
@import helper._ |
3 | 3 |
@import scala.collection.mutable.Map |
4 | 4 |
@import models.enumeration.Resource |
5 |
+@import models.enumeration.Operation |
|
5 | 6 |
@import models.Milestone |
6 | 7 |
@import java.text.SimpleDateFormat |
7 | 8 |
@import java.security.MessageDigest |
8 | 9 |
@import utils.TemplateHelper._ |
10 |
+@import utils.AccessControl._ |
|
9 | 11 |
|
10 | 12 |
@implicitFieldConstructor = @{ FieldConstructor(twitterBootstrapInput.render) } |
11 | 13 |
|
12 |
-@isVisible(resource: models.enumeration.Resource)(content: => Html) = @{ |
|
13 |
- roleCheck(session.get("userId"), project.id, resource, models.enumeration.Operation.EDIT, issue.id){ |
|
14 |
- content |
|
15 |
- } |
|
16 |
-} |
|
17 | 14 |
@main(Messages(title),project, utils.MenuType.ISSUE) { |
18 | 15 |
<div class="page"> |
19 | 16 |
<style> |
... | ... | @@ -87,7 +84,7 @@ |
87 | 84 |
<span class="author">@comment.authorName</span> |
88 | 85 |
<span class="date">@comment.date</span> |
89 | 86 |
</div> |
90 |
- @roleCheck(session.get("userId"), project.id, models.enumeration.Resource.ISSUE_COMMENT, models.enumeration.Operation.EDIT, comment.id) { |
|
87 |
+ @if(isAllowed(UserApp.currentUser(), comment.asResource(), Operation.DELETE)) { |
|
91 | 88 |
<div class="pull-right"> |
92 | 89 |
<a data-toggle="modal" href='#deleteCommentConfirm'><i class="icon-remove"> </i> |
93 | 90 |
</div> |
... | ... | @@ -127,7 +124,7 @@ |
127 | 124 |
} |
128 | 125 |
</div> |
129 | 126 |
|
130 |
-@isVisible(models.enumeration.Resource.ISSUE_POST) { |
|
127 |
+@if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.DELETE)) { |
|
131 | 128 |
<div class="pull-left"> |
132 | 129 |
<a data-toggle="modal" href="#deleteIssueConfirm" class="btn">@Messages("button.delete")</a> |
133 | 130 |
</div> |
... | ... | @@ -136,7 +133,7 @@ |
136 | 133 |
<div class="pull-right"> |
137 | 134 |
<a class="btn" href=""><i class="icon-ok"></i>@Messages("button.autoNotification")</a> |
138 | 135 |
<a class="btn" href="@routes.IssueApp.issues(project.owner, project.name,"open")">@Messages("button.list")</a> |
139 |
- @isVisible(models.enumeration.Resource.ISSUE_POST) { |
|
136 |
+ @if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.UPDATE)) { |
|
140 | 137 |
<a class="btn btn-primary" href="@routes.IssueApp.editIssueForm(project.owner, project.name, issue.id)">@Messages("button.edit")</a> |
141 | 138 |
} |
142 | 139 |
</div> |
--- app/views/issue/newIssue.scala.html
+++ app/views/issue/newIssue.scala.html
... | ... | @@ -4,12 +4,9 @@ |
4 | 4 |
@import scala.collection.mutable.Map |
5 | 5 |
@implicitFieldConstructor = @{ FieldConstructor(twitterBootstrapInput.render) } |
6 | 6 |
@import controllers.UserApp; |
7 |
- |
|
8 |
-@isVisible(resource: models.enumeration.Resource)(content: => Html) = @{ |
|
9 |
- roleCheck(session.get("userId"), project.id, resource, models.enumeration.Operation.EDIT){ |
|
10 |
- content |
|
11 |
- } |
|
12 |
-} |
|
7 |
+@import models.enumeration.Resource |
|
8 |
+@import models.enumeration.Operation |
|
9 |
+@import utils.AccessControl._ |
|
13 | 10 |
|
14 | 11 |
@main(Messages(title), project, utils.MenuType.ISSUE) { |
15 | 12 |
<div class="page"> |
... | ... | @@ -49,7 +46,7 @@ |
49 | 46 |
|
50 | 47 |
<!-- issue.label js module appends a label selector here. --> |
51 | 48 |
<fieldset class="labels"> |
52 |
- @isVisible(models.enumeration.Resource.ISSUE_ASSIGNEE) { |
|
49 |
+ @if(isCreatable(UserApp.currentUser(), project, Resource.ISSUE_ASSIGNEE)) { |
|
53 | 50 |
@select( |
54 | 51 |
issueForm("assignee.user.id"), |
55 | 52 |
options(ProjectUser.options(project.id)), |
... | ... | @@ -57,7 +54,7 @@ |
57 | 54 |
'_default -> Messages("issue.new.selectDefault.assignee"), |
58 | 55 |
'_showConstraints -> false) |
59 | 56 |
} |
60 |
- @isVisible(models.enumeration.Resource.ISSUE_MILESTONE) { |
|
57 |
+ @if(isCreatable(UserApp.currentUser(), project, Resource.ISSUE_MILESTONE)) { |
|
61 | 58 |
@select( |
62 | 59 |
issueForm("milestoneId"), |
63 | 60 |
options(Milestone.options(project.id)), |
--- app/views/roleCheck.scala.html
... | ... | @@ -1,5 +0,0 @@ |
1 | -@(userId: String, projectId: Long, resource: models.enumeration.Resource, operation: models.enumeration.Operation, resourceId: Long = null)(content: => Html) | |
2 | - | |
3 | -@if(utils.AccessControl.isAllowed(userId, projectId, resource, operation, resourceId)){ | |
4 | - @content | |
5 | -} |
--- conf/initial-data.yml
+++ conf/initial-data.yml
... | ... | @@ -40,14 +40,12 @@ |
40 | 40 |
email: ejlee@nhn.com |
41 | 41 |
avatarUrl: /assets/images/default-avatar-128.png |
42 | 42 |
date: 2012-05-01 08:00:00 |
43 |
- - !!models.User |
|
44 |
- name: anonymous |
|
45 |
- loginId: anonymous |
|
46 |
- password: |
|
47 |
- email: anonymous@nhn.com |
|
48 |
- avatarUrl: /assets/images/default-avatar-128.png |
|
49 |
- date: 2012-02-01 08:00:00 |
|
50 |
- |
|
43 |
+ |
|
44 |
+siteAdmins: |
|
45 |
+ - !!models.SiteAdmin |
|
46 |
+ admin: !!models.User |
|
47 |
+ id: 1 |
|
48 |
+ |
|
51 | 49 |
# Projects |
52 | 50 |
projects: |
53 | 51 |
- !!models.Project |
... | ... | @@ -362,576 +360,14 @@ |
362 | 360 |
- !!models.Role |
363 | 361 |
name: manager |
364 | 362 |
active: true |
365 |
- permissions: |
|
366 |
- - !!models.Permission |
|
367 |
- id: 1 |
|
368 |
- - !!models.Permission |
|
369 |
- id: 2 |
|
370 |
- - !!models.Permission |
|
371 |
- id: 3 |
|
372 |
- - !!models.Permission |
|
373 |
- id: 4 |
|
374 |
- - !!models.Permission |
|
375 |
- id: 5 |
|
376 |
- - !!models.Permission |
|
377 |
- id: 6 |
|
378 |
- - !!models.Permission |
|
379 |
- id: 7 |
|
380 |
- - !!models.Permission |
|
381 |
- id: 8 |
|
382 |
- - !!models.Permission |
|
383 |
- id: 12 |
|
384 |
- - !!models.Permission |
|
385 |
- id: 13 |
|
386 |
- - !!models.Permission |
|
387 |
- id: 14 |
|
388 |
- - !!models.Permission |
|
389 |
- id: 15 |
|
390 |
- - !!models.Permission |
|
391 |
- id: 16 |
|
392 |
- - !!models.Permission |
|
393 |
- id: 17 |
|
394 |
- - !!models.Permission |
|
395 |
- id: 21 |
|
396 |
- - !!models.Permission |
|
397 |
- id: 22 |
|
398 |
- - !!models.Permission |
|
399 |
- id: 23 |
|
400 |
- - !!models.Permission |
|
401 |
- id: 24 |
|
402 |
- - !!models.Permission |
|
403 |
- id: 25 |
|
404 |
- - !!models.Permission |
|
405 |
- id: 26 |
|
406 |
- - !!models.Permission |
|
407 |
- id: 33 |
|
408 |
- - !!models.Permission |
|
409 |
- id: 34 |
|
410 |
- - !!models.Permission |
|
411 |
- id: 35 |
|
412 |
- - !!models.Permission |
|
413 |
- id: 36 |
|
414 |
- - !!models.Permission |
|
415 |
- id: 37 |
|