doortts doortts 2017-07-21
role: Add guest user feature
@d1eda8db7256f67f2a9d78571576f8e5c57b895e
 
app/actions/GuestProhibitAction.java (added)
+++ app/actions/GuestProhibitAction.java
@@ -0,0 +1,41 @@
+/**
+ * Yona, 21st Century Project Hosting SW
+ * <p>
+ * Copyright Yona & Yobi Authors & NAVER Corp.
+ * https://yona.io
+ **/
+package actions;
+
+import controllers.UserApp;
+import controllers.annotation.GuestProhibit;
+import controllers.routes;
+import play.libs.F.Promise;
+import play.mvc.Action;
+import play.mvc.Http.Context;
+import play.mvc.Result;
+import utils.AccessControl;
+import utils.AccessLogger;
+import utils.Constants;
+
+/**
+ * After execute {@link AbstractProjectCheckAction},
+ * If current user is anonymous, redirect to the login page.
+ *
+ * @author Wansoon Park, Keesun Beak
+ *
+ */
+public class GuestProhibitAction extends Action<GuestProhibit> {
+
+    @Override
+    public Promise<Result> call(Context context) throws Throwable {
+        if (UserApp.currentUser().isGuest) {
+            if (configuration.displaysFlashMessage()) {
+                play.mvc.Controller.flash(Constants.WARNING, "error.forbidden.or.not.allowed");
+            }
+            Promise<Result> promise = Promise.pure(redirect(routes.Application.index()));
+            AccessLogger.log(context.request(), promise, null);
+            return promise;
+        }
+        return delegate.call(context);
+    }
+}
app/controllers/OrganizationApp.java
--- app/controllers/OrganizationApp.java
+++ app/controllers/OrganizationApp.java
@@ -8,6 +8,7 @@
 import com.avaje.ebean.ExpressionList;
 import com.avaje.ebean.Page;
 import controllers.annotation.AnonymousCheck;
+import controllers.annotation.GuestProhibit;
 import models.*;
 import models.enumeration.Operation;
 import models.enumeration.RequestState;
@@ -59,6 +60,7 @@
      * @throws Exception
      */
     @AnonymousCheck(requiresLogin = true, displaysFlashMessage = true)
+    @GuestProhibit
     public static Result newOrganization() throws Exception {
         Form<Organization> newOrgForm = form(Organization.class).bindFromRequest();
         if (newOrgForm.hasErrors()) {
@@ -120,11 +122,11 @@
             return result;
         }
 
-        User user = User.findByLoginId(addMemberForm.get().loginId);
+        User targetUser = User.findByLoginId(addMemberForm.get().loginId);
         Organization organization = Organization.findByName(organizationName);
-        OrganizationUser.assignRole(user.id, organization.id, RoleType.ORG_MEMBER.roleType());
+        OrganizationUser.assignRole(targetUser.id, organization.id, RoleType.ORG_MEMBER.roleType());
         organization.cleanEnrolledUsers();
-        NotificationEvent.afterOrganizationMemberRequest(organization, user, RequestState.ACCEPT);
+        NotificationEvent.afterOrganizationMemberRequest(organization, targetUser, RequestState.ACCEPT);
 
         return redirect(routes.OrganizationApp.members(organizationName));
     }
@@ -135,6 +137,11 @@
 
         if (addMemberForm.hasErrors() || userToBeAdded.isAnonymous()) {
             flash(Constants.WARNING, "organization.member.unknownUser");
+            return redirect(routes.OrganizationApp.members(organizationName));
+        }
+
+        if (userToBeAdded.isGuest) {
+            flash(Constants.WARNING, "error.forbidden.to.guest.user");
             return redirect(routes.OrganizationApp.members(organizationName));
         }
 
@@ -447,6 +454,7 @@
         return new ValidationResult(okWithLocation(routes.OrganizationApp.organization(organization.name).url()), false);
     }
 
+    @GuestProhibit
     public static Result orgList(String query, int pageNum){
         if(Application.HIDE_PROJECT_LISTING){
             return forbidden(ErrorViews.Forbidden.render("error.auth.unauthorized.waringMessage"));
app/controllers/ProjectApp.java
--- app/controllers/ProjectApp.java
+++ app/controllers/ProjectApp.java
@@ -10,6 +10,7 @@
 import com.avaje.ebean.*;
 
 import controllers.annotation.AnonymousCheck;
+import controllers.annotation.GuestProhibit;
 import controllers.annotation.IsAllowed;
 import info.schleichardt.play2.mailplugin.Mailer;
 import models.*;
@@ -956,6 +957,7 @@
      * @param pageNum the page num
      * @return
      */
+    @GuestProhibit
     public static Result projects(String query, int pageNum) {
         if(Application.HIDE_PROJECT_LISTING){
             return forbidden(ErrorViews.Forbidden.render("error.auth.unauthorized.waringMessage"));
 
app/controllers/annotation/GuestProhibit.java (added)
+++ app/controllers/annotation/GuestProhibit.java
@@ -0,0 +1,22 @@
+/**
+ * Yona, 21st Century Project Hosting SW
+ * <p>
+ * Copyright Yona & Yobi Authors & NAVER Corp.
+ * https://yona.io
+ **/
+package controllers.annotation;
+
+import actions.GuestProhibitAction;
+import play.mvc.With;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@With(GuestProhibitAction.class)
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface GuestProhibit {
+    boolean displaysFlashMessage() default true;
+}
app/models/Organization.java
--- app/models/Organization.java
+++ app/models/Organization.java
@@ -24,6 +24,7 @@
 import com.avaje.ebean.Page;
 import com.avaje.ebean.PagingList;
 import controllers.Application;
+import controllers.UserApp;
 import models.enumeration.RequestState;
 import models.enumeration.ResourceType;
 import models.resource.GlobalResource;
@@ -125,7 +126,7 @@
         } else {
             if(!Application.HIDE_PROJECT_LISTING){
                 for(Project project : this.projects) {
-                    if(project.isPublic() || user.isMemberOf(project)) {
+                    if(project.isPublic() && !user.isGuest || user.isMemberOf(project)) {
                         result.add(project);
                     }
                 }
app/models/User.java
--- app/models/User.java
+++ app/models/User.java
@@ -534,11 +534,8 @@
     }
 
     public boolean isMemberOf(Project project) {
-        if (!projectMembersMemo.containsKey(project.id)) {
-            projectMembersMemo.put(project.id, ProjectUser.isMember(id, project.id));
-        }
-
-        return projectMembersMemo.get(project.id);
+        // TODO: Performance! Removed cache. If performance problem is occurred, fix it!
+        return ProjectUser.isMember(id, project.id);
     }
 
     public List<Project> getEnrolledProjects() {
app/utils/AccessControl.java
--- app/utils/AccessControl.java
+++ app/utils/AccessControl.java
@@ -67,7 +67,7 @@
         // Site manager, Group admin, Project members can create anything.
         if (user.isSiteManager()
             || OrganizationUser.isAdmin(project.organization, user)
-            || ProjectUser.isManager(user.id, project.id)
+            || ProjectUser.isMember(user.id, project.id)
             || isAllowedIfGroupMember(project, user)) {
             return true;
         }
@@ -161,7 +161,7 @@
                 if (project == null) {
                     return false;
                 }
-                return project.isPublic()
+                return project.isPublic() && !user.isGuest
                         || user.isMemberOf(project)
                         || isAdmin(project.organization, user)
                         || isAllowedIfGroupMember(project, user);
@@ -283,7 +283,7 @@
         // See docs/technical/access-control.md for more information.
         switch(operation) {
         case READ:
-            return project.isPublic()
+            return project.isPublic() && !user.isGuest
                     || user.isMemberOf(project)
                     || isAllowedIfGroupMember(project, user);
         case UPDATE:
app/views/common/navbar.scala.html
--- app/views/common/navbar.scala.html
+++ app/views/common/navbar.scala.html
@@ -1,23 +1,9 @@
 @**
- * Yobi, Project Hosting SW
- *
- * Copyright 2014 NAVER Corp.
- * http://yobi.io
- *
- * @author Deokhong Kim
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- **@
+* Yona, 21st Century Project Hosting SW
+*
+* Copyright Yona & Yobi Authors & NAVER Corp.
+* https://yona.io
+**@
 @(menuType:utils.MenuType, project:Project, org:Organization)
 
 @import utils._
@@ -54,7 +40,7 @@
             <li>
                 <a href="@routes.Application.index()" class="logo logo-letter">Y</a>
             </li>
-            @if(!Application.HIDE_PROJECT_LISTING){
+            @if(!Application.HIDE_PROJECT_LISTING && !UserApp.currentUser().isGuest){
                 <li class="@isActiveMenu(MenuType.PROJECTS)">
                     <a href="@routes.ProjectApp.projects()" class="show-progress-bar">@Messages("title.list")</a>
                 </li>
@@ -94,7 +80,7 @@
                             }
                             } 
                             @if(org != null) {
-                                @if(Application.HIDE_PROJECT_LISTING) {
+                                @if(Application.HIDE_PROJECT_LISTING || UserApp.currentUser().isGuest) {
                                     @if(UserApp.currentUser().isMemberOf(org) || UserApp.currentUser().isAdminOf(org)){
                                         <li>
                                             <a href="#" data-toggle="search-scope" data-action="@routes.SearchApp.searchInAGroup(org.name)">
@@ -105,7 +91,7 @@
                                 }
 
                             }
-                            @if(!Application.HIDE_PROJECT_LISTING || UserApp.currentUser().isSiteManager) {
+                            @if(!Application.HIDE_PROJECT_LISTING && !UserApp.currentUser().isGuest || UserApp.currentUser().isSiteManager) {
                             <li>
                                 <a href="#" data-toggle="search-scope" data-action="@routes.SearchApp.searchInAll()">
                                     @Messages("search.scope.all")
app/views/common/usermenu.scala.html
--- app/views/common/usermenu.scala.html
+++ app/views/common/usermenu.scala.html
@@ -11,7 +11,12 @@
 @import com.feth.play.module.pa.views.html._
 @import play.mvc.Http._;
 @orderString = @{"createdDate DESC"}
-@if(UserApp.currentUser().isAnonymous){
+
+@currentUser = @{
+    UserApp.currentUser()
+}
+
+@if(currentUser.isAnonymous){
     @currentAuth() { auth => @{
             if(auth == null) {
                 val redirect = Context.current().request().getQueryString("redirectUrl")
@@ -26,7 +31,7 @@
 }
 
 @myOpenIssueCount = @{
-    val count = Issue.countOpenIssuesByUser(UserApp.currentUser())
+    val count = Issue.countOpenIssuesByUser(currentUser)
     if(count > 0) {
         Html("<span class=\"counter-badge\">" + count + "</span>")
     }
@@ -35,7 +40,7 @@
 <div id="mySidenav" class="sidenav">
     <div class="span5 right-menu span-hard-wrap">
         <div class="row-fluid user-menu-wrap">
-            <span class="user-menu"><a href="@routes.UserApp.userInfo(UserApp.currentUser().loginId)">@Messages("userinfo.profile")</a></span>
+            <span class="user-menu"><a href="@routes.UserApp.userInfo(currentUser.loginId)">@Messages("userinfo.profile")</a></span>
             <span class="user-menu"><a href="@routes.UserApp.editUserInfoForm()">@Messages("userinfo.accountSetting")</a></span>
             @currentAuth() { auth =>
                 @if(auth != null) {
@@ -65,7 +70,7 @@
     </div>
 </div>
 <ul class="gnb-usermenu">
-    @if( !UserApp.currentUser().isAnonymous()) {
+    @if( !currentUser.isAnonymous()) {
        <li class="gnb-usermenu-item hide-in-mobile">
           <a href="@routes.Application.notifications()" class="user-item-btn loggged-in">@Messages("notification")</a>
        </li>
@@ -74,7 +79,7 @@
            <a href="@routes.IssueApp.userIssues()" class="user-item-btn loggged-in">@Messages("issue.myIssue")@myOpenIssueCount</a>
         </li>
         <li class="divider"></li>
-        @if(UserApp.currentUser.isSiteManager) {
+        @if(currentUser.isSiteManager) {
         <li class="gnb-usermenu-item">
             <a href="@routes.SiteApp.userList()" data-toggle="tooltip" title="@Messages("menu.siteAdmin")" data-placement="bottom" class="usermenu-icon-button show-progress-bar">
                 <i class="yobicon-wrench"></i>
@@ -85,9 +90,9 @@
         <li class="gnb-usermenu-dropdown" id="sidebar-open-btn">
             <a href="javascript:void(0);" class="gnb-dropdown-toggle">
                 <span class="avatar-wrap smaller">
-                    <img src="@UserApp.currentUser().avatarUrl(32)" />
+                    <img src="@currentUser.avatarUrl(32)" />
                 </span>
-                <span class="caret-text">@UserApp.currentUser().name</span>
+                <span class="caret-text">@currentUser.name</span>
                 <span class="caret"></span>
             </a>
         </li>
@@ -102,11 +107,13 @@
                   @Messages("button.newProject")
                   </a>
               </li>
+              @if(!currentUser.isGuest){
               <li>
                   <a href="@routes.OrganizationApp.newForm()">
                   @Messages("title.newOrganization")
                   </a>
               </li>
+              }
           </ul>
       </li>
       } else {
app/views/organization/header.scala.html
--- app/views/organization/header.scala.html
+++ app/views/organization/header.scala.html
@@ -45,7 +45,7 @@
             <div class="project-util-wrap">
                 <ul class="project-util">
                     <button class="ybtn ybtn-small @if(User.enrolled(org)) { ybtn-info } dropdown-toggle" type="button" data-toggle="dropdown">
-                        <i class="yobicon-addfriend"> @Messages("organization.member.enrollment.title") </i>
+                        <i class="yobicon-addfriend"></i> @Messages("organization.member.enrollment.title")
                     </button>
                     <div class="dropdown-menu flat right title">
                         <div class="pop-title">@getPopupTitle</div>
app/views/project/header.scala.html
--- app/views/project/header.scala.html
+++ app/views/project/header.scala.html
@@ -93,7 +93,7 @@
                         </div>
                         } else {
                         <button class="ybtn ybtn-small dropdown-toggle" type="button" data-toggle="dropdown">
-                            <i class="yobicon-addfriend"> @Messages("organization.member.enrollment.title")</i>
+                            <i class="yobicon-addfriend"></i>@Messages("organization.member.enrollment.title")
                         </button>
                         <div class="dropdown-menu flat right title">
                             <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
+++ app/views/user/view.scala.html
@@ -89,6 +89,7 @@
             </div>
 
             <div class="user-stream-box">
+                @if(!UserApp.currentUser().isGuest){
                 <div class="pull-right">
                     @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")
                 </div>
@@ -191,6 +192,7 @@
                     }
                </div>
             </div>
+            }
         </section>
     </div>
 </div>
conf/application.conf.default
--- conf/application.conf.default
+++ conf/application.conf.default
@@ -21,6 +21,21 @@
 # want to allow that, set signup.require.confirm to true.
 application.allowsAnonymousAccess=true
 
+# Guest User Id Rule
+# ~~~~~~~~~~~~~
+# If login id is created with following prefixes,
+# Yona treat that user is Guest User.
+# Guest user is extremely restricted in use of Yona.
+# They can not see any project listing of instance and
+# only create own account's projects.
+# In other words, they cannot create organization.
+# If multiple prefixes are needed, user , (comma)
+#
+# eg.
+# "PT_, GUEST_"
+
+application.guest.user.login.id.prefix = ""
+
 #
 # Signup options
 # ~~~~~~~~~~~~~~
conf/messages
--- conf/messages
+++ conf/messages
@@ -207,6 +207,7 @@
 error.forbidden = You are not authorized
 error.forbidden.or.notfound = Project not exists or unauthorized user
 error.forbidden.or.not.allowed = Forbidden or not allowed request
+error.forbidden.to.guest.user = This is a request that is not allowed for guest user
 error.timeout = Your request could not be processed because of server timeout
 error.internalServerError = Server error has occurred; service is not available
 error.notfound = Page not found
conf/messages.ko-KR
--- conf/messages.ko-KR
+++ conf/messages.ko-KR
@@ -211,6 +211,7 @@
 error.forbidden = 권한이 없습니다
 error.forbidden.or.notfound = 권한이 없거나 존재하지 않는 프로젝트입니다.
 error.forbidden.or.not.allowed = 권한이 없거나 허용하지 않는 요청입니다.
+error.forbidden.to.guest.user = 게스트 유저에게는 허용하지 않는 요청입니다
 error.timeout = 요청 처리가 너무 오래 걸려 중단되었습니다
 error.internalServerError = 서버 오류가 발생하여 서비스를 이용할 수 없습니다
 error.notfound = 페이지를 찾을 수 없습니다
Add a comment
List