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.captcha.CaptchaHelper; 034import org.ametys.core.right.RightManager; 035import org.ametys.core.right.RightManager.RightResult; 036import org.ametys.core.user.CurrentUserProvider; 037import org.ametys.core.user.UserIdentity; 038import org.ametys.core.user.population.UserPopulationDAO; 039import org.ametys.runtime.exception.ServiceUnavailableException; 040import org.ametys.runtime.servlet.RuntimeServlet; 041import org.ametys.runtime.servlet.RuntimeServlet.MaintenanceStatus; 042import org.ametys.runtime.workspace.WorkspaceMatcher; 043 044/** 045 * This action will stop user during the maintenance mode (except administrators) 046 */ 047public class MaintenanceAction extends ServiceableAction 048{ 049 /** url requires for authentication */ 050 protected static final Collection<Pattern> ACCEPTED_URL_PATTERNS = Arrays.asList(new Pattern[] { 051 Pattern.compile("^plugins/core-ui/current-user/image_[0-9]+$"), 052 Pattern.compile("^plugins/core-ui/user/[^/]+/[^/]+/image_[0-9]+$") 053 }); 054 055 private CurrentUserProvider _currentUserProvider; 056 private RightManager _rightManager; 057 058 @Override 059 public void service(ServiceManager smanager) throws ServiceException 060 { 061 super.service(smanager); 062 _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE); 063 _rightManager = (RightManager) smanager.lookup(RightManager.ROLE); 064 } 065 066 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 067 { 068 if (RuntimeServlet.getMaintenanceStatus() != MaintenanceStatus.NONE && !acceptedUrl(ObjectModelHelper.getRequest(objectModel))) 069 { 070 UserIdentity user = _currentUserProvider.getUser(); 071 072 if ((user == null 073 || user.getPopulationId() != UserPopulationDAO.ADMIN_POPULATION_ID 074 && _rightManager.hasRight(user, "Runtime_Rights_Admin_Maintenance_Access", "/admin") != RightResult.RIGHT_ALLOW) 075 && !_isACaptchaUrl((String) ObjectModelHelper.getRequest(objectModel).getAttribute(WorkspaceMatcher.IN_WORKSPACE_URL))) 076 { 077 throw new ServiceUnavailableException("User " + (user != null ? UserIdentity.userIdentityToString(user) : "[anonymous]") + " cannot access during maintenance"); 078 } 079 } 080 081 return EMPTY_MAP; 082 } 083 084 private boolean _isACaptchaUrl(String url) 085 { 086 for (Pattern pattern : CaptchaHelper.getUsedUrlPatterns()) 087 { 088 if (pattern.matcher(url).matches()) 089 { 090 return true; 091 } 092 } 093 094 return false; 095 } 096 097 /** 098 * Determine if the request is one of the maintenance process 099 * @param request The request 100 * @return true to allow even in maintenance 101 */ 102 public static boolean acceptedUrl(Request request) 103 { 104 // URL without server context and leading slash. 105 String url = (String) request.getAttribute(WorkspaceMatcher.IN_WORKSPACE_URL); 106 for (Pattern pattern : ACCEPTED_URL_PATTERNS) 107 { 108 if (pattern.matcher(url).matches()) 109 { 110 return true; 111 } 112 } 113 114 return false; 115 } 116}