
Merge branch 'master' into feature/organization-working to sync
Conflicts: app/utils/AccessControl.java test/controllers/IssueAppTest.java
@c02a5f7ed9f1cba58d7f40eaaacfe8dc86d942bb
--- app/controllers/UserApp.java
+++ app/controllers/UserApp.java
... | ... | @@ -86,7 +86,8 @@ |
86 | 86 |
return status(Http.Status.NOT_ACCEPTABLE); |
87 | 87 |
} |
88 | 88 |
|
89 |
- ExpressionList<User> el = User.find.select("loginId, name").where().disjunction(); |
|
89 |
+ ExpressionList<User> el = User.find.select("loginId, name").where() |
|
90 |
+ .ne("state", UserState.DELETED).disjunction(); |
|
90 | 91 |
el.icontains("loginId", query); |
91 | 92 |
el.icontains("name", query); |
92 | 93 |
el.endJunction(); |
--- app/models/Label.java
+++ app/models/Label.java
... | ... | @@ -49,21 +49,6 @@ |
49 | 49 |
} |
50 | 50 |
|
51 | 51 |
/** |
52 |
- * 현재 인스턴스와 같은 라벨가 있는지의 여부를 반환한다. |
|
53 |
- * |
|
54 |
- * when: 사용자가 라벨를 추가하려고 했을 때, 중복 여부를 검사하고자 할 때 사용하고 있다. |
|
55 |
- * |
|
56 |
- * 이 인스턴스의 {@link Label#category}와 {@link Label#name}가 모두 같은 라벨가 DB에 존재하는지 확인한다. |
|
57 |
- * |
|
58 |
- * @return 같은 것이 존재하면 {@code true}, 아니면 {@code false} |
|
59 |
- */ |
|
60 |
- @Transient |
|
61 |
- public boolean exists() { |
|
62 |
- return find.where().eq("category", category).eq("name", name) |
|
63 |
- .findRowCount() > 0; |
|
64 |
- } |
|
65 |
- |
|
66 |
- /** |
|
67 | 52 |
* 라벨를 삭제한다. |
68 | 53 |
* |
69 | 54 |
* 모든 프로젝트에서 이 라벨를 제거한 뒤, 라벨를 삭제한다. |
--- app/models/User.java
+++ app/models/User.java
... | ... | @@ -216,22 +216,29 @@ |
216 | 216 |
} |
217 | 217 |
|
218 | 218 |
/** |
219 |
- * email로 사용자를 조회한다. |
|
220 | 219 |
* |
221 |
- * 사용자가 없으면 {@link #anonymous}객체에 email을 할당하고 반환한다. |
|
220 |
+ * Find a user by email account. |
|
221 |
+ * - find a user with a given email account or who has the email account as one of sub email accounts. |
|
222 |
+ * - If no user matched up with the given email account, then return new {@link models.NullUser} |
|
223 |
+ * after setting the email account to the object. |
|
222 | 224 |
* |
223 | 225 |
* @param email |
224 | 226 |
* @return |
225 | 227 |
*/ |
226 | 228 |
public static User findByEmail(String email) { |
227 | 229 |
User user = find.where().eq("email", email).findUnique(); |
228 |
- if (user == null) { |
|
229 |
- anonymous.email = email; |
|
230 |
- return anonymous; |
|
231 |
- } |
|
232 |
- else { |
|
230 |
+ if (user != null) { |
|
233 | 231 |
return user; |
234 | 232 |
} |
233 |
+ |
|
234 |
+ Email subEmail = Email.findByEmail(email, true); |
|
235 |
+ if (subEmail != null) { |
|
236 |
+ return subEmail.user; |
|
237 |
+ } |
|
238 |
+ |
|
239 |
+ User anonymous = new NullUser(); |
|
240 |
+ anonymous.email = email; |
|
241 |
+ return anonymous; |
|
235 | 242 |
} |
236 | 243 |
|
237 | 244 |
/** |
... | ... | @@ -479,6 +486,10 @@ |
479 | 486 |
enrolledProjects.clear(); |
480 | 487 |
notificationEvents.clear(); |
481 | 488 |
for (Assignee assignee : Assignee.finder.where().eq("user.id", id).findList()) { |
489 |
+ for (Issue issue : assignee.issues) { |
|
490 |
+ issue.assignee = null; |
|
491 |
+ issue.update(); |
|
492 |
+ } |
|
482 | 493 |
assignee.delete(); |
483 | 494 |
} |
484 | 495 |
} |
... | ... | @@ -572,26 +583,6 @@ |
572 | 583 |
public void removeEmail(Email email) { |
573 | 584 |
emails.remove(email); |
574 | 585 |
email.delete(); |
575 |
- } |
|
576 |
- |
|
577 |
- /** |
|
578 |
- * {@code committerEmail}에 해당하는 User를 찾아 반환한다. |
|
579 |
- * |
|
580 |
- * @param committerEmail |
|
581 |
- * @return |
|
582 |
- */ |
|
583 |
- public static User findByCommitterEmail(String committerEmail) { |
|
584 |
- User user = find.where().eq("email", committerEmail).findUnique(); |
|
585 |
- if (user != null) { |
|
586 |
- return user; |
|
587 |
- } |
|
588 |
- |
|
589 |
- Email email = Email.findByEmail(committerEmail, true); |
|
590 |
- if (email != null) { |
|
591 |
- return email.user; |
|
592 |
- } |
|
593 |
- |
|
594 |
- return anonymous; |
|
595 | 586 |
} |
596 | 587 |
|
597 | 588 |
public void visits(Project project) { |
--- app/playRepository/GitBranch.java
+++ app/playRepository/GitBranch.java
... | ... | @@ -44,7 +44,7 @@ |
44 | 44 |
this.name = name; |
45 | 45 |
this.shortName = StringUtils.removeStart(name, Constants.R_HEADS); |
46 | 46 |
this.headCommit = headCommit; |
47 |
- this.user = User.findByCommitterEmail(headCommit.getCommitterEmail()); |
|
47 |
+ this.user = User.findByEmail(headCommit.getCommitterEmail()); |
|
48 | 48 |
} |
49 | 49 |
|
50 | 50 |
public String getName() { |
--- app/playRepository/GitRepository.java
+++ app/playRepository/GitRepository.java
... | ... | @@ -1150,7 +1150,7 @@ |
1150 | 1150 |
if (personIdent == null) { |
1151 | 1151 |
return User.anonymous; |
1152 | 1152 |
} |
1153 |
- return User.findByCommitterEmail(personIdent.getEmailAddress()); |
|
1153 |
+ return User.findByEmail(personIdent.getEmailAddress()); |
|
1154 | 1154 |
} |
1155 | 1155 |
|
1156 | 1156 |
/** |
--- app/utils/AccessControl.java
+++ app/utils/AccessControl.java
... | ... | @@ -66,7 +66,7 @@ |
66 | 66 |
} |
67 | 67 |
|
68 | 68 |
public static boolean isResourceCreatable(User user, Resource container, ResourceType resourceType) { |
69 |
- if (isAllowedIfAuthor(user, container)) { |
|
69 |
+ if (isAllowedIfAuthor(user, container) || isAllowedIfAssignee(user, container)) { |
|
70 | 70 |
return true; |
71 | 71 |
} |
72 | 72 |
|
... | ... | @@ -191,7 +191,8 @@ |
191 | 191 |
|
192 | 192 |
if (user.isSiteManager() |
193 | 193 |
|| ProjectUser.isManager(user.id, project.id) |
194 |
- || isAllowedIfAuthor(user, resource)) { |
|
194 |
+ || isAllowedIfAuthor(user, resource) |
|
195 |
+ || isAllowedIfAssignee(user, resource)) { |
|
195 | 196 |
return true; |
196 | 197 |
} |
197 | 198 |
|
... | ... | @@ -335,4 +336,26 @@ |
335 | 336 |
return false; |
336 | 337 |
} |
337 | 338 |
} |
339 |
+ |
|
340 |
+ /** |
|
341 |
+ * Checks if an user has a permission to do something to the given |
|
342 |
+ * resource as an assignee. |
|
343 |
+ * |
|
344 |
+ * Returns true if and only if these are all true: |
|
345 |
+ * - {@code resource} gives permission to read, modify and delete to its assignee. |
|
346 |
+ * - {@code user} is an assignee of the resource. |
|
347 |
+ * |
|
348 |
+ * @param user |
|
349 |
+ * @param resource |
|
350 |
+ * @return true if the user has the permission |
|
351 |
+ */ |
|
352 |
+ private static boolean isAllowedIfAssignee(User user, Resource resource) { |
|
353 |
+ switch (resource.getType()) { |
|
354 |
+ case ISSUE_POST: |
|
355 |
+ Assignee assignee = Issue.finder.byId(Long.valueOf(resource.getId())).assignee; |
|
356 |
+ return assignee != null && assignee.user.id.equals(user.id); |
|
357 |
+ default: |
|
358 |
+ return false; |
|
359 |
+ } |
|
360 |
+ } |
|
338 | 361 |
} |
--- conf/evolutions/default/72.sql
+++ conf/evolutions/default/72.sql
... | ... | @@ -148,7 +148,7 @@ |
148 | 148 |
WHERE comment_thread.id IN ( |
149 | 149 |
SELECT t.id |
150 | 150 |
FROM comment_thread t, pull_request pr |
151 |
- WHERE t.pull_request_id = pr.id AND pr.state IN (2, 3)); |
|
151 |
+ WHERE t.pull_request_id = pr.id AND pr.state IN (2, 3, 6)); |
|
152 | 152 |
|
153 | 153 |
DROP TABLE IF EXISTS pull_request_comment; |
154 | 154 |
|
--- docs/technical/access-control.md
+++ docs/technical/access-control.md
... | ... | @@ -4,6 +4,8 @@ |
4 | 4 |
* 사이트 관리자: 모든 권한 |
5 | 5 |
* 저자: 자신이 만든 이슈, 게시물, 댓글에 대한 모든 권한 |
6 | 6 |
* 다만 자신이 만든 이슈나 게시물에 다른 사람이 댓글을 단 경우, 그 댓글에 대한 수정/삭제 권한은 갖지 않는다. |
7 |
+* 담당자: 자신이 담당한 이슈, 게시물, 댓글에 대한 모든 권한 |
|
8 |
+ * 다만 자신이 담당한 이슈나 게시물에 다른 사람이 댓글을 단 경우, 그 댓글에 대한 수정/삭제 권한은 갖지 않는다. |
|
7 | 9 |
|
8 | 10 |
공개 프로젝트 |
9 | 11 |
============= |
--- test/controllers/IssueAppTest.java
+++ test/controllers/IssueAppTest.java
... | ... | @@ -24,6 +24,7 @@ |
24 | 24 |
private User manager; |
25 | 25 |
private User member; |
26 | 26 |
private User author; |
27 |
+ private User assignee; |
|
27 | 28 |
private User nonmember; |
28 | 29 |
private User anonymous; |
29 | 30 |
private Issue issue; |
... | ... | @@ -51,6 +52,7 @@ |
51 | 52 |
manager = User.findByLoginId("yobi"); |
52 | 53 |
member = User.findByLoginId("laziel"); |
53 | 54 |
author = User.findByLoginId("nori"); |
55 |
+ assignee = User.findByLoginId("alecsiel"); |
|
54 | 56 |
nonmember = User.findByLoginId("doortts"); |
55 | 57 |
anonymous = new NullUser(); |
56 | 58 |
|
... | ... | @@ -59,6 +61,7 @@ |
59 | 61 |
issue.setTitle("hello"); |
60 | 62 |
issue.setBody("world"); |
61 | 63 |
issue.setAuthor(author); |
64 |
+ issue.setAssignee(Assignee.add(assignee.id, project.id)); |
|
62 | 65 |
issue.save(); |
63 | 66 |
|
64 | 67 |
assertThat(this.admin.isSiteManager()).describedAs("admin is Site Admin.").isTrue(); |
... | ... | @@ -66,7 +69,8 @@ |
66 | 69 |
assertThat(ProjectUser.isManager(member.id, project.id)).describedAs("member is a manager").isFalse(); |
67 | 70 |
assertThat(ProjectUser.isMember(member.id, project.id)).describedAs("member is a member").isTrue(); |
68 | 71 |
assertThat(ProjectUser.isMember(author.id, project.id)).describedAs("author is a member").isFalse(); |
69 |
- assertThat(project.projectScope).describedAs("project is public").isNotEqualTo(ProjectScope.PUBLIC); |
|
72 |
+ assertThat(project.isPublic).describedAs("project is public").isFalse(); |
|
73 |
+ assertThat(ProjectUser.isMember(assignee.id, project.id)).describedAs("assignee is a member").isFalse(); |
|
70 | 74 |
} |
71 | 75 |
|
72 | 76 |
@After |
... | ... | @@ -152,6 +156,14 @@ |
152 | 156 |
assertThat(status(result)).describedAs("Author can edit own issue.").isEqualTo(SEE_OTHER); |
153 | 157 |
} |
154 | 158 |
|
159 |
+ @Test |
|
160 |
+ public void editByAssignee() { |
|
161 |
+ // When |
|
162 |
+ Result result = editBy(assignee); |
|
163 |
+ |
|
164 |
+ // Then |
|
165 |
+ assertThat(status(result)).describedAs("Assignee can edit own issue.").isEqualTo(SEE_OTHER); |
|
166 |
+ } |
|
155 | 167 |
|
156 | 168 |
@Test |
157 | 169 |
public void editByAdmin() { |
... | ... | @@ -203,6 +215,14 @@ |
203 | 215 |
assertThat(status(result)).describedAs("Author can delete own issue.").isEqualTo(SEE_OTHER); |
204 | 216 |
} |
205 | 217 |
|
218 |
+ @Test |
|
219 |
+ public void deleteByAssignee() { |
|
220 |
+ // When |
|
221 |
+ Result result = deleteBy(assignee); |
|
222 |
+ |
|
223 |
+ // Then |
|
224 |
+ assertThat(status(result)).describedAs("Assignee can delete own issue.").isEqualTo(SEE_OTHER); |
|
225 |
+ } |
|
206 | 226 |
|
207 | 227 |
@Test |
208 | 228 |
public void deleteByAdmin() { |
... | ... | @@ -383,6 +403,23 @@ |
383 | 403 |
} |
384 | 404 |
|
385 | 405 |
@Test |
406 |
+ public void watchByAssignee() { |
|
407 |
+ // Given |
|
408 |
+ Resource resource = issue.asResource(); |
|
409 |
+ |
|
410 |
+ // When |
|
411 |
+ Result result = callAction( |
|
412 |
+ controllers.routes.ref.WatchApp.watch(resource.asParameter()), |
|
413 |
+ fakeRequest() |
|
414 |
+ .withSession(UserApp.SESSION_USERID, assignee.id.toString()) |
|
415 |
+ ); |
|
416 |
+ |
|
417 |
+ // Then |
|
418 |
+ issue.refresh(); |
|
419 |
+ assertThat(status(result)).isEqualTo(OK); |
|
420 |
+ } |
|
421 |
+ |
|
422 |
+ @Test |
|
386 | 423 |
public void unwatch() { |
387 | 424 |
// Given |
388 | 425 |
Resource resource = issue.asResource(); |
... | ... | @@ -420,4 +457,22 @@ |
420 | 457 |
issue.refresh(); |
421 | 458 |
assertThat(status(result)).isEqualTo(OK); |
422 | 459 |
} |
460 |
+ |
|
461 |
+ @Test |
|
462 |
+ public void unwatchByAssignee() { |
|
463 |
+ // Given |
|
464 |
+ Resource resource = issue.asResource(); |
|
465 |
+ |
|
466 |
+ // When |
|
467 |
+ Result result = callAction( |
|
468 |
+ controllers.routes.ref.WatchApp.unwatch(resource.asParameter()), |
|
469 |
+ fakeRequest() |
|
470 |
+ .withSession(UserApp.SESSION_USERID, assignee.id.toString()) |
|
471 |
+ ); |
|
472 |
+ |
|
473 |
+ // Then |
|
474 |
+ issue.refresh(); |
|
475 |
+ assertThat(status(result)).isEqualTo(OK); |
|
476 |
+ } |
|
477 |
+ |
|
423 | 478 |
} |
--- test/models/UserTest.java
+++ test/models/UserTest.java
... | ... | @@ -158,4 +158,24 @@ |
158 | 158 |
assertThat(project.getWatchingCount()).isEqualTo(0); |
159 | 159 |
} |
160 | 160 |
|
161 |
+ @Test |
|
162 |
+ public void changeState() { |
|
163 |
+ // Given |
|
164 |
+ User user = new User(); |
|
165 |
+ user.loginId = "foo"; |
|
166 |
+ user.save(); |
|
167 |
+ Project project = new Project(); |
|
168 |
+ project.save(); |
|
169 |
+ Issue issue = new Issue(); |
|
170 |
+ issue.project = project; |
|
171 |
+ issue.assignee = new Assignee(user.id, project.id); |
|
172 |
+ issue.save(); |
|
173 |
+ |
|
174 |
+ // When |
|
175 |
+ user.changeState(UserState.DELETED); |
|
176 |
+ |
|
177 |
+ // Then |
|
178 |
+ issue.refresh(); |
|
179 |
+ assertThat(issue.assignee).isNull(); |
|
180 |
+ } |
|
161 | 181 |
} |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?