001/* 002 * Copyright 2023 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.ametys.runtime.maintenance; 018 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.Map; 022import java.util.regex.Pattern; 023 024import org.apache.avalon.framework.parameters.Parameters; 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.cocoon.acting.ServiceableAction; 028import org.apache.cocoon.environment.ObjectModelHelper; 029import org.apache.cocoon.environment.Redirector; 030import org.apache.cocoon.environment.Request; 031import org.apache.cocoon.environment.SourceResolver; 032 033import org.ametys.core.right.RightManager; 034import org.ametys.core.right.RightManager.RightResult; 035import org.ametys.core.user.CurrentUserProvider; 036import org.ametys.core.user.UserIdentity; 037import org.ametys.core.user.population.UserPopulationDAO; 038import org.ametys.runtime.exception.ServiceUnavailableException; 039import org.ametys.runtime.servlet.RuntimeServlet; 040import org.ametys.runtime.servlet.RuntimeServlet.MaintenanceStatus; 041import org.ametys.runtime.workspace.WorkspaceMatcher; 042 043/** 044 * This action will stop user during the maintenance mode (except administrators) 045 */ 046public class MaintenanceAction extends ServiceableAction 047{ 048 /** url requires for authentication */ 049 protected static final Collection<Pattern> ACCEPTED_URL_PATTERNS = Arrays.asList(new Pattern[] { 050 Pattern.compile("^plugins/core-ui/current-user/image_[0-9]+$"), 051 Pattern.compile("^plugins/core-ui/user/[^/]+/[^/]+/image_[0-9]+$") 052 }); 053 054 private CurrentUserProvider _currentUserProvider; 055 private RightManager _rightManager; 056 057 @Override 058 public void service(ServiceManager smanager) throws ServiceException 059 { 060 super.service(smanager); 061 _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE); 062 _rightManager = (RightManager) smanager.lookup(RightManager.ROLE); 063 } 064 065 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 066 { 067 if (RuntimeServlet.getMaintenanceStatus() != MaintenanceStatus.NONE && !acceptedUrl(ObjectModelHelper.getRequest(objectModel))) 068 { 069 UserIdentity user = _currentUserProvider.getUser(); 070 071 if (user == null 072 || user.getPopulationId() != UserPopulationDAO.ADMIN_POPULATION_ID 073 && _rightManager.hasRight(user, "Runtime_Rights_Admin_Maintenance_Access", "/admin") != RightResult.RIGHT_ALLOW) 074 { 075 throw new ServiceUnavailableException("User " + (user != null ? UserIdentity.userIdentityToString(user) : "[anonymous]") + " cannot access during maintenance"); 076 } 077 } 078 079 return EMPTY_MAP; 080 } 081 082 /** 083 * Determine if the request is one of the maintenance process 084 * @param request The request 085 * @return true to allow even in maintenance 086 */ 087 public static boolean acceptedUrl(Request request) 088 { 089 // URL without server context and leading slash. 090 String url = (String) request.getAttribute(WorkspaceMatcher.IN_WORKSPACE_URL); 091 for (Pattern pattern : ACCEPTED_URL_PATTERNS) 092 { 093 if (pattern.matcher(url).matches()) 094 { 095 return true; 096 } 097 } 098 099 return false; 100 } 101}