| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.syncope.client.console; |
| |
| import com.giffing.wicket.spring.boot.starter.app.WicketBootSecuredWebApplication; |
| import de.agilecoders.wicket.core.Bootstrap; |
| import de.agilecoders.wicket.core.settings.BootstrapSettings; |
| import de.agilecoders.wicket.core.settings.IBootstrapSettings; |
| import de.agilecoders.wicket.core.settings.SingleThemeProvider; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| import org.apache.syncope.client.console.commons.AccessPolicyConfProvider; |
| import org.apache.syncope.client.console.commons.AnyDirectoryPanelAdditionalActionLinksProvider; |
| import org.apache.syncope.client.console.commons.AnyDirectoryPanelAdditionalActionsProvider; |
| import org.apache.syncope.client.console.commons.AnyWizardBuilderAdditionalSteps; |
| import org.apache.syncope.client.console.commons.ExternalResourceProvider; |
| import org.apache.syncope.client.console.commons.ImplementationInfoProvider; |
| import org.apache.syncope.client.console.commons.PolicyTabProvider; |
| import org.apache.syncope.client.console.commons.RealmsUtils; |
| import org.apache.syncope.client.console.commons.StatusProvider; |
| import org.apache.syncope.client.console.commons.VirSchemaDetailsPanelProvider; |
| import org.apache.syncope.client.console.init.ClassPathScanImplementationLookup; |
| import org.apache.syncope.client.console.pages.BasePage; |
| import org.apache.syncope.client.console.pages.Dashboard; |
| import org.apache.syncope.client.console.pages.Login; |
| import org.apache.syncope.client.console.pages.MustChangePassword; |
| import org.apache.syncope.client.console.rest.RealmRestClient; |
| import org.apache.syncope.client.console.wizards.any.UserFormFinalizer; |
| import org.apache.syncope.client.lib.SyncopeAnonymousClient; |
| import org.apache.syncope.client.lib.SyncopeClientFactoryBean; |
| import org.apache.syncope.client.ui.commons.Constants; |
| import org.apache.syncope.client.ui.commons.SyncopeUIRequestCycleListener; |
| import org.apache.syncope.client.ui.commons.annotations.Resource; |
| import org.apache.syncope.client.ui.commons.themes.AdminLTE; |
| import org.apache.syncope.client.ui.commons.wizards.AjaxWizard; |
| import org.apache.syncope.common.keymaster.client.api.ServiceOps; |
| import org.apache.syncope.common.keymaster.client.api.model.NetworkService; |
| import org.apache.syncope.common.rest.api.beans.RealmQuery; |
| import org.apache.wicket.Page; |
| import org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSession; |
| import org.apache.wicket.authroles.authentication.AuthenticatedWebSession; |
| import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy; |
| import org.apache.wicket.markup.html.WebPage; |
| import org.apache.wicket.protocol.http.WebApplication; |
| import org.apache.wicket.protocol.http.servlet.XForwardedRequestWrapperFactory; |
| import org.apache.wicket.protocol.ws.WebSocketAwareResourceIsolationRequestCycleListener; |
| import org.apache.wicket.protocol.ws.api.WebSocketResponse; |
| import org.apache.wicket.request.component.IRequestablePage; |
| import org.apache.wicket.request.cycle.IRequestCycleListener; |
| import org.apache.wicket.request.cycle.RequestCycle; |
| import org.apache.wicket.request.http.WebResponse; |
| import org.apache.wicket.request.mapper.parameter.PageParameters; |
| import org.apache.wicket.request.resource.AbstractResource; |
| import org.apache.wicket.request.resource.IResource; |
| import org.apache.wicket.request.resource.ResourceReference; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
| import org.springframework.util.ClassUtils; |
| |
| public class SyncopeWebApplication extends WicketBootSecuredWebApplication { |
| |
| protected static final Logger LOG = LoggerFactory.getLogger(SyncopeWebApplication.class); |
| |
| public static SyncopeWebApplication get() { |
| return (SyncopeWebApplication) WebApplication.get(); |
| } |
| |
| protected final ConsoleProperties props; |
| |
| protected final ClassPathScanImplementationLookup lookup; |
| |
| protected final ServiceOps serviceOps; |
| |
| protected final ExternalResourceProvider resourceProvider; |
| |
| protected final AnyDirectoryPanelAdditionalActionsProvider anyDirectoryPanelAdditionalActionsProvider; |
| |
| protected final AnyDirectoryPanelAdditionalActionLinksProvider anyDirectoryPanelAdditionalActionLinksProvider; |
| |
| protected final AnyWizardBuilderAdditionalSteps anyWizardBuilderAdditionalSteps; |
| |
| protected final StatusProvider statusProvider; |
| |
| protected final VirSchemaDetailsPanelProvider virSchemaDetailsPanelProvider; |
| |
| protected final ImplementationInfoProvider implementationInfoProvider; |
| |
| protected final AccessPolicyConfProvider accessPolicyConfProvider; |
| |
| protected final List<PolicyTabProvider> policyTabProviders; |
| |
| public SyncopeWebApplication( |
| final ConsoleProperties props, |
| final ClassPathScanImplementationLookup lookup, |
| final ServiceOps serviceOps, |
| final ExternalResourceProvider resourceProvider, |
| final AnyDirectoryPanelAdditionalActionsProvider anyDirectoryPanelAdditionalActionsProvider, |
| final AnyDirectoryPanelAdditionalActionLinksProvider anyDirectoryPanelAdditionalActionLinksProvider, |
| final AnyWizardBuilderAdditionalSteps anyWizardBuilderAdditionalSteps, |
| final StatusProvider statusProvider, |
| final VirSchemaDetailsPanelProvider virSchemaDetailsPanelProvider, |
| final ImplementationInfoProvider implementationInfoProvider, |
| final AccessPolicyConfProvider accessPolicyConfProvider, |
| final List<PolicyTabProvider> policyTabProviders) { |
| |
| this.props = props; |
| this.lookup = lookup; |
| this.serviceOps = serviceOps; |
| this.resourceProvider = resourceProvider; |
| this.anyDirectoryPanelAdditionalActionsProvider = anyDirectoryPanelAdditionalActionsProvider; |
| this.anyDirectoryPanelAdditionalActionLinksProvider = anyDirectoryPanelAdditionalActionLinksProvider; |
| this.anyWizardBuilderAdditionalSteps = anyWizardBuilderAdditionalSteps; |
| this.statusProvider = statusProvider; |
| this.virSchemaDetailsPanelProvider = virSchemaDetailsPanelProvider; |
| this.implementationInfoProvider = implementationInfoProvider; |
| this.accessPolicyConfProvider = accessPolicyConfProvider; |
| this.policyTabProviders = policyTabProviders; |
| } |
| |
| protected SyncopeUIRequestCycleListener buildSyncopeUIRequestCycleListener() { |
| return new SyncopeUIRequestCycleListener() { |
| |
| @Override |
| protected boolean isSignedIn() { |
| return SyncopeConsoleSession.get().isSignedIn(); |
| } |
| |
| @Override |
| protected void invalidateSession() { |
| SyncopeConsoleSession.get().invalidate(); |
| } |
| |
| @Override |
| protected IRequestablePage getErrorPage(final PageParameters errorParameters) { |
| return new Login(errorParameters); |
| } |
| }; |
| } |
| |
| protected void initSecurity() { |
| if (props.isxForward()) { |
| XForwardedRequestWrapperFactory.Config config = new XForwardedRequestWrapperFactory.Config(); |
| config.setProtocolHeader(props.getxForwardProtocolHeader()); |
| config.setHttpServerPort(props.getxForwardHttpPort()); |
| config.setHttpsServerPort(props.getxForwardHttpsPort()); |
| |
| XForwardedRequestWrapperFactory factory = new XForwardedRequestWrapperFactory(); |
| factory.setConfig(config); |
| getFilterFactoryManager().add(factory); |
| } |
| |
| if (props.isCsrf()) { |
| getRequestCycleListeners().add(new WebSocketAwareResourceIsolationRequestCycleListener()); |
| } |
| |
| getCspSettings().blocking().unsafeInline(); |
| |
| getRequestCycleListeners().add(new IRequestCycleListener() { |
| |
| @Override |
| public void onEndRequest(final RequestCycle cycle) { |
| if (cycle.getResponse() instanceof WebResponse && !(cycle.getResponse() instanceof WebSocketResponse)) { |
| props.getSecurityHeaders(). |
| forEach((name, value) -> ((WebResponse) cycle.getResponse()).setHeader(name, value)); |
| } |
| } |
| }); |
| } |
| |
| @Override |
| protected void init() { |
| super.init(); |
| |
| // Application settings |
| IBootstrapSettings settings = new BootstrapSettings(); |
| |
| // set theme provider |
| settings.setThemeProvider(new SingleThemeProvider(new AdminLTE())); |
| |
| // install application settings |
| Bootstrap.install(this, settings); |
| |
| getResourceSettings().setUseMinifiedResources(true); |
| getResourceSettings().setUseDefaultOnMissingResource(true); |
| getResourceSettings().setThrowExceptionOnMissingResource(false); |
| |
| getSecuritySettings().setAuthorizationStrategy(new MetaDataRoleAuthorizationStrategy(this)); |
| |
| lookup.getIdRepoPageClasses(). |
| forEach(cls -> MetaDataRoleAuthorizationStrategy.authorize(cls, Constants.ROLE_AUTHENTICATED)); |
| |
| getMarkupSettings().setStripWicketTags(true); |
| getMarkupSettings().setCompressWhitespace(true); |
| |
| getRequestCycleListeners().add(buildSyncopeUIRequestCycleListener()); |
| |
| initSecurity(); |
| |
| mountPage("/login", getSignInPageClass()); |
| |
| for (Class<? extends AbstractResource> resource : lookup.getClasses(AbstractResource.class)) { |
| Resource annotation = resource.getAnnotation(Resource.class); |
| try { |
| AbstractResource instance = resource.getDeclaredConstructor().newInstance(); |
| |
| LOG.debug("Mounting {} under {}", resource.getName(), annotation.path()); |
| mountResource(annotation.path(), new ResourceReference(annotation.key()) { |
| |
| protected static final long serialVersionUID = -128426276529456602L; |
| |
| @Override |
| public IResource getResource() { |
| return instance; |
| } |
| }); |
| } catch (Exception e) { |
| LOG.error("Could not instantiate {}", resource.getName(), e); |
| } |
| } |
| |
| // enable component path |
| if (getDebugSettings().isAjaxDebugModeEnabled()) { |
| getDebugSettings().setComponentPathAttributeName("syncope-path"); |
| } |
| } |
| |
| @Override |
| protected Class<? extends AbstractAuthenticatedWebSession> getWebSessionClass() { |
| return SyncopeConsoleSession.class; |
| } |
| |
| @Override |
| protected Class<? extends WebPage> getSignInPageClass() { |
| return Login.class; |
| } |
| |
| @Override |
| public Class<? extends Page> getHomePage() { |
| return AuthenticatedWebSession.get().isSignedIn() |
| && SyncopeConsoleSession.get().getSelfTO().isMustChangePassword() |
| ? MustChangePassword.class |
| : Dashboard.class; |
| } |
| |
| public ClassPathScanImplementationLookup getLookup() { |
| return lookup; |
| } |
| |
| public Class<? extends BasePage> getPageClass(final String name) { |
| return props.getPage().get(name); |
| } |
| |
| public ThreadPoolTaskExecutor newThreadPoolTaskExecutor() { |
| ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); |
| executor.setWaitForTasksToCompleteOnShutdown(false); |
| executor.setCorePoolSize(props.getTopology().getCorePoolSize()); |
| executor.setMaxPoolSize(props.getTopology().getMaxPoolSize()); |
| executor.setQueueCapacity(props.getTopology().getQueueCapacity()); |
| executor.initialize(); |
| return executor; |
| } |
| |
| public SyncopeAnonymousClient newAnonymousClient() { |
| return newClientFactory().createAnonymous(props.getAnonymousUser(), props.getAnonymousKey()); |
| } |
| |
| public SyncopeClientFactoryBean newClientFactory() { |
| return new SyncopeClientFactoryBean(). |
| setAddress(serviceOps.get(NetworkService.Type.CORE).getAddress()). |
| setUseCompression(props.isUseGZIPCompression()); |
| } |
| |
| public String getDefaultAnyPanelClass() { |
| return props.getDefaultAnyPanelClass(); |
| } |
| |
| public String getAdminUser() { |
| return props.getAdminUser(); |
| } |
| |
| public String getAnonymousUser() { |
| return props.getAnonymousUser(); |
| } |
| |
| public String getAnonymousKey() { |
| return props.getAnonymousKey(); |
| } |
| |
| public long getMaxWaitTimeInSeconds() { |
| return props.getMaxWaitTimeOnApplyChanges(); |
| } |
| |
| public int getMaxUploadFileSizeMB() { |
| return props.getMaxUploadFileSizeMB(); |
| } |
| |
| public boolean fullRealmsTree() { |
| if (props.getRealmsFullTreeThreshold() <= 0) { |
| return false; |
| } |
| |
| RealmQuery query = RealmsUtils.buildRootQuery(); |
| query.setPage(1); |
| query.setSize(0); |
| return RealmRestClient.search(query).getTotalCount() < props.getRealmsFullTreeThreshold(); |
| } |
| |
| public ExternalResourceProvider getResourceProvider() { |
| return resourceProvider; |
| } |
| |
| public AnyDirectoryPanelAdditionalActionsProvider getAnyDirectoryPanelAdditionalActionsProvider() { |
| return anyDirectoryPanelAdditionalActionsProvider; |
| } |
| |
| public AnyDirectoryPanelAdditionalActionLinksProvider getAnyDirectoryPanelAdditionalActionLinksProvider() { |
| return anyDirectoryPanelAdditionalActionLinksProvider; |
| } |
| |
| public AnyWizardBuilderAdditionalSteps getAnyWizardBuilderAdditionalSteps() { |
| return anyWizardBuilderAdditionalSteps; |
| } |
| |
| public StatusProvider getStatusProvider() { |
| return statusProvider; |
| } |
| |
| public VirSchemaDetailsPanelProvider getVirSchemaDetailsPanelProvider() { |
| return virSchemaDetailsPanelProvider; |
| } |
| |
| public ImplementationInfoProvider getImplementationInfoProvider() { |
| return implementationInfoProvider; |
| } |
| |
| public Collection<PolicyTabProvider> getPolicyTabProviders() { |
| return policyTabProviders; |
| } |
| |
| public List<UserFormFinalizer> getFormFinalizers(final AjaxWizard.Mode mode) { |
| List<UserFormFinalizer> finalizers = new ArrayList<>(); |
| |
| lookup.getUserFormFinalizerClasses(mode).forEach(finalizer -> { |
| if (finalizer != null) { |
| try { |
| finalizers.add(ClassUtils.getConstructorIfAvailable(finalizer).newInstance()); |
| } catch (Exception e) { |
| LOG.error("Could not instantiate {}", finalizer, e); |
| } |
| } |
| }); |
| |
| return finalizers; |
| } |
| |
| public AccessPolicyConfProvider getAccessPolicyConfProvider() { |
| return accessPolicyConfProvider; |
| } |
| } |