+++ app/actions/GuestProhibitAction.java
... | ... | @@ -0,0 +1,41 @@ |
1 | +/** | |
2 | + * Yona, 21st Century Project Hosting SW | |
3 | + * <p> | |
4 | + * Copyright Yona & Yobi Authors & NAVER Corp. | |
5 | + * https://yona.io | |
6 | + **/ | |
7 | +package actions; | |
8 | + | |
9 | +import controllers.UserApp; | |
10 | +import controllers.annotation.GuestProhibit; | |
11 | +import controllers.routes; | |
12 | +import play.libs.F.Promise; | |
13 | +import play.mvc.Action; | |
14 | +import play.mvc.Http.Context; | |
15 | +import play.mvc.Result; | |
16 | +import utils.AccessControl; | |
17 | +import utils.AccessLogger; | |
18 | +import utils.Constants; | |
19 | + | |
20 | +/** | |
21 | + * After execute {@link AbstractProjectCheckAction}, | |
22 | + * If current user is anonymous, redirect to the login page. | |
23 | + * | |
24 | + * @author Wansoon Park, Keesun Beak | |
25 | + * | |
26 | + */ | |
27 | +public class GuestProhibitAction extends Action<GuestProhibit> { | |
28 | + | |
29 | + @Override | |
30 | + public Promise<Result> call(Context context) throws Throwable { | |
31 | + if (UserApp.currentUser().isGuest) { | |
32 | + if (configuration.displaysFlashMessage()) { | |
33 | + play.mvc.Controller.flash(Constants.WARNING, "error.forbidden.or.not.allowed"); | |
34 | + } | |
35 | + Promise<Result> promise = Promise.pure(redirect(routes.Application.index())); | |
36 | + AccessLogger.log(context.request(), promise, null); | |
37 | + return promise; | |
38 | + } | |
39 | + return delegate.call(context); | |
40 | + } | |
41 | +} |
--- app/controllers/OrganizationApp.java
+++ app/controllers/OrganizationApp.java
... | ... | @@ -8,6 +8,7 @@ |
8 | 8 |
import com.avaje.ebean.ExpressionList; |
9 | 9 |
import com.avaje.ebean.Page; |
10 | 10 |
import controllers.annotation.AnonymousCheck; |
11 |
+import controllers.annotation.GuestProhibit; |
|
11 | 12 |
import models.*; |
12 | 13 |
import models.enumeration.Operation; |
13 | 14 |
import models.enumeration.RequestState; |
... | ... | @@ -59,6 +60,7 @@ |
59 | 60 |
* @throws Exception |
60 | 61 |
*/ |
61 | 62 |
@AnonymousCheck(requiresLogin = true, displaysFlashMessage = true) |
63 |
+ @GuestProhibit |
|
62 | 64 |
public static Result newOrganization() throws Exception { |
63 | 65 |
Form<Organization> newOrgForm = form(Organization.class).bindFromRequest(); |
64 | 66 |
if (newOrgForm.hasErrors()) { |
... | ... | @@ -120,11 +122,11 @@ |
120 | 122 |
return result; |
121 | 123 |
} |
122 | 124 |
|
123 |
- User user = User.findByLoginId(addMemberForm.get().loginId); |
|
125 |
+ User targetUser = User.findByLoginId(addMemberForm.get().loginId); |
|
124 | 126 |
Organization organization = Organization.findByName(organizationName); |
125 |
- OrganizationUser.assignRole(user.id, organization.id, RoleType.ORG_MEMBER.roleType()); |
|
127 |
+ OrganizationUser.assignRole(targetUser.id, organization.id, RoleType.ORG_MEMBER.roleType()); |
|
126 | 128 |
organization.cleanEnrolledUsers(); |
127 |
- NotificationEvent.afterOrganizationMemberRequest(organization, user, RequestState.ACCEPT); |
|
129 |
+ NotificationEvent.afterOrganizationMemberRequest(organization, targetUser, RequestState.ACCEPT); |
|
128 | 130 |
|
129 | 131 |
return redirect(routes.OrganizationApp.members(organizationName)); |
130 | 132 |
} |
... | ... | @@ -135,6 +137,11 @@ |
135 | 137 |
|
136 | 138 |
if (addMemberForm.hasErrors() || userToBeAdded.isAnonymous()) { |
137 | 139 |
flash(Constants.WARNING, "organization.member.unknownUser"); |
140 |
+ return redirect(routes.OrganizationApp.members(organizationName)); |
|
141 |
+ } |
|
142 |
+ |
|
143 |
+ if (userToBeAdded.isGuest) { |
|
144 |
+ flash(Constants.WARNING, "error.forbidden.to.guest.user"); |
|
138 | 145 |
return redirect(routes.OrganizationApp.members(organizationName)); |
139 | 146 |
} |
140 | 147 |
|
... | ... | @@ -447,6 +454,7 @@ |
447 | 454 |
return new ValidationResult(okWithLocation(routes.OrganizationApp.organization(organization.name).url()), false); |
448 | 455 |
} |
449 | 456 |
|
457 |
+ @GuestProhibit |
|
450 | 458 |
public static Result orgList(String query, int pageNum){ |
451 | 459 |
if(Application.HIDE_PROJECT_LISTING){ |
452 | 460 |
return forbidden(ErrorViews.Forbidden.render("error.auth.unauthorized.waringMessage")); |
--- app/controllers/ProjectApp.java
+++ app/controllers/ProjectApp.java
... | ... | @@ -10,6 +10,7 @@ |
10 | 10 |
import com.avaje.ebean.*; |
11 | 11 |
|
12 | 12 |
import controllers.annotation.AnonymousCheck; |
13 |
+import controllers.annotation.GuestProhibit; |
|
13 | 14 |
import controllers.annotation.IsAllowed; |
14 | 15 |
import info.schleichardt.play2.mailplugin.Mailer; |
15 | 16 |
import models.*; |
... | ... | @@ -956,6 +957,7 @@ |
956 | 957 |
* @param pageNum the page num |
957 | 958 |
* @return |
958 | 959 |
*/ |
960 |
+ @GuestProhibit |
|
959 | 961 |
public static Result projects(String query, int pageNum) { |
960 | 962 |
if(Application.HIDE_PROJECT_LISTING){ |
961 | 963 |
return forbidden(ErrorViews.Forbidden.render("error.auth.unauthorized.waringMessage")); |
+++ app/controllers/annotation/GuestProhibit.java
... | ... | @@ -0,0 +1,22 @@ |
1 | +/** | |
2 | + * Yona, 21st Century Project Hosting SW | |
3 | + * <p> | |
4 | + * Copyright Yona & Yobi Authors & NAVER Corp. | |
5 | + * https://yona.io | |
6 | + **/ | |
7 | +package controllers.annotation; | |
8 | + | |
9 | +import actions.GuestProhibitAction; | |
10 | +import play.mvc.With; | |
11 | + | |
12 | +import java.lang.annotation.ElementType; | |
13 | +import java.lang.annotation.Retention; | |
14 | +import java.lang.annotation.RetentionPolicy; | |
15 | +import java.lang.annotation.Target; | |
16 | + | |
17 | +@With(GuestProhibitAction.class) | |
18 | +@Target({ElementType.TYPE, ElementType.METHOD}) | |
19 | +@Retention(RetentionPolicy.RUNTIME) | |
20 | +public @interface GuestProhibit { | |
21 | + boolean displaysFlashMessage() default true; | |
22 | +} |
--- app/models/Organization.java
+++ app/models/Organization.java
... | ... | @@ -24,6 +24,7 @@ |
24 | 24 |
import com.avaje.ebean.Page; |
25 | 25 |
import com.avaje.ebean.PagingList; |
26 | 26 |
import controllers.Application; |
27 |
+import controllers.UserApp; |
|
27 | 28 |
import models.enumeration.RequestState; |
28 | 29 |
import models.enumeration.ResourceType; |
29 | 30 |
import models.resource.GlobalResource; |
... | ... | @@ -125,7 +126,7 @@ |
125 | 126 |
} else { |
126 | 127 |
if(!Application.HIDE_PROJECT_LISTING){ |
127 | 128 |
for(Project project : this.projects) { |
128 |
- if(project.isPublic() || user.isMemberOf(project)) { |
|
129 |
+ if(project.isPublic() && !user.isGuest || user.isMemberOf(project)) { |
|
129 | 130 |
result.add(project); |
130 | 131 |
} |
131 | 132 |
} |
--- app/models/User.java
+++ app/models/User.java
... | ... | @@ -534,11 +534,8 @@ |
534 | 534 |
} |
535 | 535 |
|
536 | 536 |
public boolean isMemberOf(Project project) { |
537 |
- if (!projectMembersMemo.containsKey(project.id)) { |
|
538 |
- projectMembersMemo.put(project.id, ProjectUser.isMember(id, project.id)); |
|
539 |
- } |
|
540 |
- |
|
541 |
- return projectMembersMemo.get(project.id); |
|
537 |
+ // TODO: Performance! Removed cache. If performance problem is occurred, fix it! |
|
538 |
+ return ProjectUser.isMember(id, project.id); |
|
542 | 539 |
} |
543 | 540 |
|
544 | 541 |
public List<Project> getEnrolledProjects() { |
--- app/utils/AccessControl.java
+++ app/utils/AccessControl.java
... | ... | @@ -67,7 +67,7 @@ |
67 | 67 |
// Site manager, Group admin, Project members can create anything. |
68 | 68 |
if (user.isSiteManager() |
69 | 69 |
|| OrganizationUser.isAdmin(project.organization, user) |
70 |
- || ProjectUser.isManager(user.id, project.id) |
|
70 |
+ || ProjectUser.isMember(user.id, project.id) |
|
71 | 71 |
|| isAllowedIfGroupMember(project, user)) { |
72 | 72 |
return true; |
73 | 73 |
} |
... | ... | @@ -161,7 +161,7 @@ |
161 | 161 |
if (project == null) { |
162 | 162 |
return false; |
163 | 163 |
} |
164 |
- return project.isPublic() |
|
164 |
+ return project.isPublic() && !user.isGuest |
|
165 | 165 |
|| user.isMemberOf(project) |
166 | 166 |
|| isAdmin(project.organization, user) |
167 | 167 |
|| isAllowedIfGroupMember(project, user); |
... | ... | @@ -283,7 +283,7 @@ |
283 | 283 |
// See docs/technical/access-control.md for more information. |
284 | 284 |
switch(operation) { |
285 | 285 |
case READ: |
286 |
- return project.isPublic() |
|
286 |
+ return project.isPublic() && !user.isGuest |
|
287 | 287 |
|| user.isMemberOf(project) |
288 | 288 |
|| isAllowedIfGroupMember(project, user); |
289 | 289 |
case UPDATE: |
--- app/views/organization/header.scala.html
+++ app/views/organization/header.scala.html
... | ... | @@ -45,7 +45,7 @@ |
45 | 45 |
<div class="project-util-wrap"> |
46 | 46 |
<ul class="project-util"> |
47 | 47 |
<button class="ybtn ybtn-small @if(User.enrolled(org)) { ybtn-info } dropdown-toggle" type="button" data-toggle="dropdown"> |
48 |
- <i class="yobicon-addfriend"> @Messages("organization.member.enrollment.title") </i> |
|
48 |
+ <i class="yobicon-addfriend"></i> @Messages("organization.member.enrollment.title") |
|
49 | 49 |
</button> |
50 | 50 |
<div class="dropdown-menu flat right title"> |
51 | 51 |
<div class="pop-title">@getPopupTitle</div> |
--- app/views/project/header.scala.html
+++ app/views/project/header.scala.html
... | ... | @@ -93,7 +93,7 @@ |
93 | 93 |
</div> |
94 | 94 |
} else { |
95 | 95 |
<button class="ybtn ybtn-small dropdown-toggle" type="button" data-toggle="dropdown"> |
96 |
- <i class="yobicon-addfriend"> @Messages("organization.member.enrollment.title")</i> |
|
96 |
+ <i class="yobicon-addfriend"></i>@Messages("organization.member.enrollment.title") |
|
97 | 97 |
</button> |
98 | 98 |
<div class="dropdown-menu flat right title"> |
99 | 99 |
<div class="pop-title">@Messages("project.you.may.want.to.be.a.member", project)</div> |
--- app/views/user/view.scala.html
+++ app/views/user/view.scala.html
... | ... | @@ -89,6 +89,7 @@ |
89 | 89 |
</div> |
90 | 90 |
|
91 | 91 |
<div class="user-stream-box"> |
92 |
+ @if(!UserApp.currentUser().isGuest){ |
|
92 | 93 |
<div class="pull-right"> |
93 | 94 |
@Messages("userinfo.daysAgo.prefix")<input id="daysAgoBtn" name="daysAgo" type="number" min="1" max="99" class="input-mini-min" value="@daysAgo" style="margin:0px 5px; vertical-align:bottom;">@Messages("userinfo.daysAgo.suffix") |
94 | 95 |
</div> |
... | ... | @@ -191,6 +192,7 @@ |
191 | 192 |
} |
192 | 193 |
</div> |
193 | 194 |
</div> |
195 |
+ } |
|
194 | 196 |
</section> |
195 | 197 |
</div> |
196 | 198 |
</div> |
--- conf/application.conf.default
+++ conf/application.conf.default
... | ... | @@ -21,6 +21,21 @@ |
21 | 21 |
# want to allow that, set signup.require.confirm to true. |
22 | 22 |
application.allowsAnonymousAccess=true |
23 | 23 |
|
24 |
+# Guest User Id Rule |
|
25 |
+# ~~~~~~~~~~~~~ |
|
26 |
+# If login id is created with following prefixes, |
|
27 |
+# Yona treat that user is Guest User. |
|
28 |
+# Guest user is extremely restricted in use of Yona. |
|
29 |
+# They can not see any project listing of instance and |
|
30 |
+# only create own account's projects. |
|
31 |
+# In other words, they cannot create organization. |
|
32 |
+# If multiple prefixes are needed, user , (comma) |
|
33 |
+# |
|
34 |
+# eg. |
|
35 |
+# "PT_, GUEST_" |
|
36 |
+ |
|
37 |
+application.guest.user.login.id.prefix = "" |
|
38 |
+ |
|
24 | 39 |
# |
25 | 40 |
# Signup options |
26 | 41 |
# ~~~~~~~~~~~~~~ |
--- conf/messages
+++ conf/messages
... | ... | @@ -207,6 +207,7 @@ |
207 | 207 |
error.forbidden = You are not authorized |
208 | 208 |
error.forbidden.or.notfound = Project not exists or unauthorized user |
209 | 209 |
error.forbidden.or.not.allowed = Forbidden or not allowed request |
210 |
+error.forbidden.to.guest.user = This is a request that is not allowed for guest user |
|
210 | 211 |
error.timeout = Your request could not be processed because of server timeout |
211 | 212 |
error.internalServerError = Server error has occurred; service is not available |
212 | 213 |
error.notfound = Page not found |
--- conf/messages.ko-KR
+++ conf/messages.ko-KR
... | ... | @@ -211,6 +211,7 @@ |
211 | 211 |
error.forbidden = 권한이 없습니다 |
212 | 212 |
error.forbidden.or.notfound = 권한이 없거나 존재하지 않는 프로젝트입니다. |
213 | 213 |
error.forbidden.or.not.allowed = 권한이 없거나 허용하지 않는 요청입니다. |
214 |
+error.forbidden.to.guest.user = 게스트 유저에게는 허용하지 않는 요청입니다 |
|
214 | 215 |
error.timeout = 요청 처리가 너무 오래 걸려 중단되었습니다 |
215 | 216 |
error.internalServerError = 서버 오류가 발생하여 서비스를 이용할 수 없습니다 |
216 | 217 |
error.notfound = 페이지를 찾을 수 없습니다 |
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?