/*
* 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.application;
import com.evolveum.midpoint.security.api.AuthorizationConstants;
import com.evolveum.midpoint.util.ClassPathUtil;
import com.evolveum.midpoint.util.DebugDumpable;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.DisplayableValue;
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.security.MidPointApplication;
import com.evolveum.midpoint.web.util.ExactMatchMountedMapper;
import com.evolveum.midpoint.xml.ns._public.gui.admin_1.DescriptorType;
import com.evolveum.midpoint.xml.ns._public.gui.admin_1.ObjectFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.wicket.core.request.mapper.MountedMapper;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import java.io.InputStream;
import java.util.*;
/**
* @author lazyman
*/
public final class DescriptorLoader implements DebugDumpable {
private static final Trace LOGGER = TraceManager.getTrace(DescriptorLoader.class);
private static Map<String, DisplayableValue<String>[]> actions = new HashMap<>();
private static Map<String, Class> urlClassMap = new HashMap<>();
private String baseFileName = "/WEB-INF/descriptor.xml";
private String customFileName = "/WEB-INF/classes/descriptor.xml";
public static Map<String, DisplayableValue<String>[]> getActions() {
return actions;
}
public static Map<String, Class> getUrlClassMap() {
return urlClassMap;
}
public String getBaseFileName() {
return baseFileName;
}
public void setBaseFileName(String baseFileName) {
this.baseFileName = baseFileName;
}
public String getCustomFileName() {
return customFileName;
}
public void setCustomFileName(String customFileName) {
this.customFileName = customFileName;
}
public void loadData(MidPointApplication application) {
LOGGER.debug("Loading data from descriptor files.");
try (InputStream baseInput = application.getServletContext().getResourceAsStream(baseFileName);
InputStream customInput = application.getServletContext().getResourceAsStream(customFileName)) {
if (baseInput == null) {
LOGGER.error("Couldn't find " + baseFileName + " file, can't load application menu and other stuff.");
}
JAXBContext context = JAXBContext.newInstance(ObjectFactory.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
JAXBElement<DescriptorType> element = (JAXBElement) unmarshaller.unmarshal(baseInput);
DescriptorType descriptor = element.getValue();
LOGGER.debug("Loading menu bar from " + baseFileName + " .");
DescriptorType customDescriptor = null;
if (customInput != null) {
element = (JAXBElement) unmarshaller.unmarshal(customInput);
customDescriptor = element.getValue();
}
scanPackagesForPages(descriptor.getPackagesToScan(), application);
if (customDescriptor != null) {
scanPackagesForPages(customDescriptor.getPackagesToScan(), application);
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("loaded:\n{}", debugDump(1));
}
} catch (Exception ex) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't process application descriptor", ex);
}
}
private void scanPackagesForPages(List<String> packages, MidPointApplication application)
throws InstantiationException, IllegalAccessException {
for (String pac : packages) {
LOGGER.debug("Scanning package package {} for page annotations", new Object[]{pac});
Set<Class> classes = ClassPathUtil.listClasses(pac);
for (Class clazz : classes) {
if (!WebPage.class.isAssignableFrom(clazz)) {
continue;
}
PageDescriptor descriptor = (PageDescriptor) clazz.getAnnotation(PageDescriptor.class);
if (descriptor == null) {
continue;
}
mountPage(descriptor, clazz, application);
loadActions(descriptor);
}
}
}
private void loadActions(PageDescriptor descriptor) {
List<AuthorizationActionValue> actions = new ArrayList<>();
//avoid of setting guiAll authz for "public" pages (e.g. login page)
if (descriptor.action() == null || descriptor.action().length == 0) {
return;
}
boolean canAccess = true;
for (AuthorizationAction action : descriptor.action()) {
actions.add(new AuthorizationActionValue(action.actionUri(), action.label(), action.description()));
if (AuthorizationConstants.AUTZ_NO_ACCESS_URL.equals(action.actionUri())) {
canAccess = false;
break;
}
}
//add http://.../..#guiAll authorization only for displayable pages, not for pages used for development..
if (canAccess) {
actions.add(new AuthorizationActionValue(AuthorizationConstants.AUTZ_GUI_ALL_DEPRECATED_URL,
AuthorizationConstants.AUTZ_GUI_ALL_LABEL, AuthorizationConstants.AUTZ_GUI_ALL_DESCRIPTION));
actions.add(new AuthorizationActionValue(AuthorizationConstants.AUTZ_GUI_ALL_URL,
AuthorizationConstants.AUTZ_GUI_ALL_LABEL, AuthorizationConstants.AUTZ_GUI_ALL_DESCRIPTION));
}
for (String url : descriptor.url()) {
this.actions.put(buildPrefixUrl(url), actions.toArray(new DisplayableValue[actions.size()]));
}
for (Url url : descriptor.urls()) {
String urlForSecurity = url.matchUrlForSecurity();
if (StringUtils.isEmpty(urlForSecurity)) {
urlForSecurity = buildPrefixUrl(url.mountUrl());
}
this.actions.put(urlForSecurity, actions.toArray(new DisplayableValue[actions.size()]));
}
}
public String buildPrefixUrl(String url) {
StringBuilder sb = new StringBuilder();
sb.append(url);
if (!url.endsWith("/")) {
sb.append("/");
}
sb.append("**");
return sb.toString();
}
private void mountPage(PageDescriptor descriptor, Class clazz, MidPointApplication application)
throws InstantiationException, IllegalAccessException {
//todo remove for cycle later
for (String url : descriptor.url()) {
IPageParametersEncoder encoder = descriptor.encoder().newInstance();
LOGGER.trace("Mounting page '{}' to url '{}' with encoder '{}'.", new Object[]{
clazz.getName(), url, encoder.getClass().getSimpleName()});
application.mount(new ExactMatchMountedMapper(url, clazz, encoder));
urlClassMap.put(url, clazz);
}
for (Url url : descriptor.urls()) {
IPageParametersEncoder encoder = descriptor.encoder().newInstance();
LOGGER.trace("Mounting page '{}' to url '{}' with encoder '{}'.", new Object[]{
clazz.getName(), url, encoder.getClass().getSimpleName()});
application.mount(new ExactMatchMountedMapper(url.mountUrl(), clazz, encoder));
urlClassMap.put(url.mountUrl(), clazz);
}
}
@Override
public String debugDump() {
return debugDump(0);
}
@Override
public String debugDump(int indent) {
StringBuilder sb = new StringBuilder();
DebugUtil.indentDebugDump(sb, indent);
sb.append("DescriptorLoader\n");
DebugUtil.debugDumpWithLabelLn(sb, "actions", actions, indent + 1);
DebugUtil.debugDumpWithLabel(sb, "urlClassMap", urlClassMap, indent + 1);
return sb.toString();
}
}