/* * Copyright (c) 2010-2016 Evolveum * * 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. */ package com.evolveum.midpoint.web.security; import java.io.File; import java.io.FilenameFilter; import java.io.InputStreamReader; import java.io.Reader; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; import com.evolveum.midpoint.model.common.SystemObjectCache; import com.evolveum.midpoint.model.common.expression.ExpressionFactory; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.match.MatchingRuleRegistry; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.security.api.SecurityEnforcer; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.web.page.error.*; import com.evolveum.midpoint.wf.api.WorkflowManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType; import org.apache.commons.configuration.Configuration; import org.apache.commons.io.IOUtils; import org.apache.wicket.RuntimeConfigurationType; import org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSession; import org.apache.wicket.authroles.authentication.AuthenticatedWebApplication; import org.apache.wicket.core.request.mapper.MountedMapper; import org.apache.wicket.markup.head.PriorityFirstComparator; import org.apache.wicket.markup.html.SecurePackageResourceGuard; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.request.mapper.parameter.PageParametersEncoder; import org.apache.wicket.request.resource.PackageResourceReference; import org.apache.wicket.request.resource.SharedResourceReference; import org.apache.wicket.resource.loader.IStringResourceLoader; import org.apache.wicket.settings.ApplicationSettings; import org.apache.wicket.settings.ResourceSettings; import org.apache.wicket.spring.injection.annot.SpringComponentInjector; import org.apache.wicket.util.lang.Bytes; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.stereotype.Component; import com.evolveum.midpoint.common.configuration.api.MidpointConfiguration; import com.evolveum.midpoint.gui.api.page.PageBase; import com.evolveum.midpoint.gui.api.util.WebComponentUtil; import com.evolveum.midpoint.model.api.ModelAuditService; import com.evolveum.midpoint.model.api.ModelInteractionService; import com.evolveum.midpoint.model.api.ModelService; import com.evolveum.midpoint.model.api.TaskService; import com.evolveum.midpoint.model.api.WorkflowService; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.security.api.AuthorizationConstants; import com.evolveum.midpoint.task.api.TaskManager; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.web.application.DescriptorLoader; import com.evolveum.midpoint.web.component.GuiComponents; import com.evolveum.midpoint.web.page.admin.home.PageDashboard; import com.evolveum.midpoint.web.page.login.PageLogin; import com.evolveum.midpoint.web.page.self.PageSelfDashboard; import com.evolveum.midpoint.web.resource.img.ImgResources; import com.evolveum.midpoint.web.util.Utf8BundleStringResourceLoader; /** * @author lazyman */ @Component("midpointApplication") public class MidPointApplication extends AuthenticatedWebApplication { /** * Max. photo size for user/jpegPhoto */ public static final Bytes FOCUS_PHOTO_MAX_FILE_SIZE = Bytes.kilobytes(192); public static final String WEB_APP_CONFIGURATION = "midpoint.webApplication"; public static final List<LocaleDescriptor> AVAILABLE_LOCALES; private static final String LOCALIZATION_DESCRIPTOR = "/localization/locale.properties"; private static final String PROP_NAME = ".name"; private static final String PROP_FLAG = ".flag"; private static final String PROP_DEFAULT = ".default"; private static final Trace LOGGER = TraceManager.getTrace(MidPointApplication.class); static { List<LocaleDescriptor> locales = new ArrayList<>(); try { ClassLoader classLoader = MidPointApplication.class.getClassLoader(); Enumeration<URL> urls = classLoader.getResources(LOCALIZATION_DESCRIPTOR); while (urls.hasMoreElements()) { final URL url = urls.nextElement(); LOGGER.debug("Found localization descriptor {}.", new Object[]{url.toString()}); Properties properties = new Properties(); Reader reader = null; try { reader = new InputStreamReader(url.openStream(), "utf-8"); properties.load(reader); Map<String, Map<String, String>> localeMap = new HashMap<>(); Set<String> keys = (Set) properties.keySet(); for (String key : keys) { String[] array = key.split("\\."); if (array.length != 2) { continue; } String locale = array[0]; Map<String, String> map = localeMap.get(locale); if (map == null) { map = new HashMap<>(); localeMap.put(locale, map); } map.put(key, properties.getProperty(key)); } for (String key : localeMap.keySet()) { Map<String, String> localeDefinition = localeMap.get(key); if (!localeDefinition.containsKey(key + PROP_NAME) || !localeDefinition.containsKey(key + PROP_FLAG)) { continue; } LocaleDescriptor descriptor = new LocaleDescriptor( localeDefinition.get(key + PROP_NAME), localeDefinition.get(key + PROP_FLAG), localeDefinition.get(key + PROP_DEFAULT), WebComponentUtil.getLocaleFromString(key) ); locales.add(descriptor); } } catch (Exception ex) { LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load localization", ex); } finally { IOUtils.closeQuietly(reader); } } Collections.sort(locales); } catch (Exception ex) { LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load locales", ex); } AVAILABLE_LOCALES = Collections.unmodifiableList(locales); } @Autowired transient ModelService model; @Autowired transient ModelInteractionService modelInteractionService; @Autowired transient TaskService taskService; @Autowired transient PrismContext prismContext; @Autowired transient ExpressionFactory expressionFactory; @Autowired transient TaskManager taskManager; @Autowired transient ModelAuditService auditService; @Autowired transient private RepositoryService repositoryService; // temporary @Autowired transient private WorkflowService workflowService; @Autowired transient private WorkflowManager workflowManager; @Autowired transient MidpointConfiguration configuration; @Autowired transient Protector protector; @Autowired transient MatchingRuleRegistry matchingRuleRegistry; @Autowired transient SecurityEnforcer securityEnforcer; @Autowired transient SystemObjectCache systemObjectCache; private WebApplicationConfiguration webApplicationConfiguration; @Override protected void onDestroy() { GuiComponents.destroy(); super.onDestroy(); } @Override public Class<? extends PageBase> getHomePage() { if (WebComponentUtil.isAuthorized(AuthorizationConstants.AUTZ_UI_DASHBOARD_URL, AuthorizationConstants.AUTZ_UI_HOME_ALL_URL)) { return PageDashboard.class; } else { return PageSelfDashboard.class; } } @Override public void init() { super.init(); getJavaScriptLibrarySettings().setJQueryReference( new PackageResourceReference(MidPointApplication.class, "../../../../../webjars/adminlte/2.3.11/plugins/jQuery/jquery-2.2.3.min.js")); GuiComponents.init(); getComponentInstantiationListeners().add(new SpringComponentInjector(this)); ResourceSettings resourceSettings = getResourceSettings(); resourceSettings.setParentFolderPlaceholder("$-$"); resourceSettings.setHeaderItemComparator(new PriorityFirstComparator(true)); SecurePackageResourceGuard guard = (SecurePackageResourceGuard) resourceSettings.getPackageResourceGuard(); guard.addPattern("+*.woff2"); List<IStringResourceLoader> resourceLoaders = resourceSettings.getStringResourceLoaders(); resourceLoaders.add(0, new Utf8BundleStringResourceLoader("localization/Midpoint")); resourceLoaders.add(1, new Utf8BundleStringResourceLoader(SchemaConstants.SCHEMA_LOCALIZATION_PROPERTIES_RESOURCE_BASE_PATH)); resourceSettings.setThrowExceptionOnMissingResource(false); getMarkupSettings().setStripWicketTags(true); // getMarkupSettings().setDefaultBeforeDisabledLink(""); // getMarkupSettings().setDefaultAfterDisabledLink(""); if (RuntimeConfigurationType.DEVELOPMENT.equals(getConfigurationType())) { getDebugSettings().setAjaxDebugModeEnabled(true); getDebugSettings().setDevelopmentUtilitiesEnabled(true); } //pretty url for resources (e.g. images) mountFiles(ImgResources.BASE_PATH, ImgResources.class); //exception handling an error pages ApplicationSettings appSettings = getApplicationSettings(); appSettings.setAccessDeniedPage(PageError401.class); appSettings.setInternalErrorPage(PageError.class); appSettings.setPageExpiredErrorPage(PageError.class); mount(new MountedMapper("/error", PageError.class, new PageParametersEncoder())); mount(new MountedMapper("/error/401", PageError401.class, new PageParametersEncoder())); mount(new MountedMapper("/error/403", PageError403.class, new PageParametersEncoder())); mount(new MountedMapper("/error/404", PageError404.class, new PageParametersEncoder())); mount(new MountedMapper("/error/410", PageError410.class, new PageParametersEncoder())); getRequestCycleListeners().add(new LoggingRequestCycleListener(this)); //descriptor loader, used for customization new DescriptorLoader().loadData(this); } private void mountFiles(String path, Class<?> clazz) { try { List<Resource> list = new ArrayList<>(); String packagePath = clazz.getPackage().getName().replace('.', '/'); PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource[] res = resolver.getResources("classpath:" + packagePath + "/*.png"); if (res != null) { list.addAll(Arrays.asList(res)); } res = resolver.getResources("classpath:" + packagePath + "/*.gif"); if (res != null) { list.addAll(Arrays.asList(res)); } for (Resource resource : list) { URI uri = resource.getURI(); File file = new File(uri.toString()); mountResource(path + "/" + file.getName(), new SharedResourceReference(clazz, file.getName())); } } catch (Exception ex) { LoggingUtils.logUnexpectedException(LOGGER, "Couldn't mount files", ex); } } public WebApplicationConfiguration getWebApplicationConfiguration() { if (webApplicationConfiguration == null) { Configuration config = configuration.getConfiguration(WEB_APP_CONFIGURATION); webApplicationConfiguration = new WebApplicationConfiguration(config); } return webApplicationConfiguration; } public SecurityEnforcer getSecurityEnforcer() { return securityEnforcer; } public ModelService getModel() { return model; } public TaskManager getTaskManager() { return taskManager; } public ModelAuditService getAuditService() { return auditService; } public RepositoryService getRepositoryService() { return repositoryService; } public TaskService getTaskService() { return taskService; } public PrismContext getPrismContext() { return prismContext; } public ExpressionFactory getExpressionFactory() { return expressionFactory; } public Protector getProtector() { return protector; } @Override protected Class<? extends WebPage> getSignInPageClass() { return PageLogin.class; } @Override protected Class<? extends AbstractAuthenticatedWebSession> getWebSessionClass() { return MidPointAuthWebSession.class; } public WorkflowService getWorkflowService() { return workflowService; } public WorkflowManager getWorkflowManager() { return workflowManager; } public ModelInteractionService getModelInteractionService() { return modelInteractionService; } public static boolean containsLocale(Locale locale) { if (locale == null) { return false; } for (LocaleDescriptor descriptor : AVAILABLE_LOCALES) { if (locale.equals(descriptor.getLocale())) { return true; } } return false; } public static Locale getDefaultLocale() { for (LocaleDescriptor descriptor : AVAILABLE_LOCALES) { if (descriptor.isDefault()) { return descriptor.getLocale(); } } return new Locale("en", "US"); } public MatchingRuleRegistry getMatchingRuleRegistry() { return matchingRuleRegistry; } public SystemConfigurationType getSystemConfiguration() throws SchemaException { PrismObject<SystemConfigurationType> config = systemObjectCache.getSystemConfiguration(new OperationResult("dummy")); return config != null ? config.asObjectable() : null; } public SystemConfigurationType getSystemConfigurationIfAvailable() { try { PrismObject<SystemConfigurationType> config = systemObjectCache.getSystemConfiguration(new OperationResult("dummy")); return config != null ? config.asObjectable() : null; } catch (SchemaException e) { LoggingUtils.logUnexpectedException(LOGGER, "Couldn't retrieve system configuration", e); return null; } } private static class ResourceFileFilter implements FilenameFilter { @Override public boolean accept(File parent, String name) { if (name.endsWith("png") || name.endsWith("gif")) { return true; } return false; } } public static MidPointApplication get() { return (MidPointApplication) WebApplication.get(); } }