/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.security.web.auth; import java.io.BufferedReader; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.Principal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.logging.Level; import javax.servlet.AsyncContext; import javax.servlet.DispatcherType; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.servlet.http.Part; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.OnChangeAjaxBehavior; import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink; import org.apache.wicket.markup.html.form.CheckBox; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.FormComponent; import org.apache.wicket.markup.html.form.FormComponentPanel; import org.apache.wicket.markup.html.form.SubmitLink; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.form.validation.AbstractFormValidator; import org.apache.wicket.markup.html.link.Link; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.util.convert.IConverter; import org.apache.wicket.validation.IValidatable; import org.apache.wicket.validation.IValidator; import org.apache.wicket.validation.ValidationError; import org.apache.wicket.validation.validator.RangeValidator; import org.geoserver.platform.GeoServerExtensions; import org.geoserver.security.GeoServerSecurityFilterChain; import org.geoserver.security.GeoServerSecurityFilterChainProxy; import org.geoserver.security.HTTPMethod; import org.geoserver.security.RequestFilterChain; import org.geoserver.security.config.BruteForcePreventionConfig; import org.geoserver.security.config.LogoutFilterConfig; import org.geoserver.security.config.SSLFilterConfig; import org.geoserver.security.config.SecurityManagerConfig; import org.geoserver.security.web.AbstractSecurityPage; import org.geoserver.web.wicket.HelpLink; import org.geoserver.web.wicket.ParamResourceModel; import org.h2.bnf.Bnf; import org.springframework.security.web.util.matcher.IpAddressMatcher; /** * Main menu page for authentication. * * @author Justin Deoliveira, OpenGeo */ public class AuthenticationPage extends AbstractSecurityPage { Form<SecurityManagerConfig> form; LogoutFilterConfig logoutFilterConfig; SSLFilterConfig sslFilterConfig; SecurityManagerConfig config; AuthFilterChainPanel authFilterChainPanel; public AuthenticationPage() { initComponents(); } @SuppressWarnings("serial") void initComponents() { // The request filter chain objects have to be cloned config = getSecurityManager().getSecurityConfig(); List<RequestFilterChain> clones = new ArrayList<RequestFilterChain>(); for (RequestFilterChain chain : config.getFilterChain().getRequestChains()) { try { clones.add((RequestFilterChain)chain.clone()); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } } config.setFilterChain(new GeoServerSecurityFilterChain(clones)); form = new Form("form", new CompoundPropertyModel<SecurityManagerConfig>(config)); add(form); try { logoutFilterConfig= (LogoutFilterConfig) getSecurityManager().loadFilterConfig(GeoServerSecurityFilterChain.FORM_LOGOUT_FILTER); } catch (IOException e1) { throw new RuntimeException(e1); } form.add(new TextField<String>("redirectURL",new PropertyModel<String>(this, "logoutFilterConfig.redirectURL"))); try { sslFilterConfig= (SSLFilterConfig) getSecurityManager().loadFilterConfig(GeoServerSecurityFilterChain.SSL_FILTER); } catch (IOException e1) { throw new RuntimeException(e1); } form.add(new TextField<Integer>("sslPort",new PropertyModel<Integer>(this, "sslFilterConfig.sslPort"))); // brute force attack form.add(new CheckBox("bfEnabled", new PropertyModel<Boolean>(this, "config.bruteForcePrevention.enabled"))); final TextField<Integer> bfMinDelay = new TextField<Integer>("bfMinDelaySeconds", new PropertyModel<Integer>(this, "config.bruteForcePrevention.minDelaySeconds")); bfMinDelay.add(RangeValidator.minimum(0)); form.add(bfMinDelay); final TextField<Integer> bfMaxDelay = new TextField<Integer>("bfMaxDelaySeconds", new PropertyModel<Integer>(this, "config.bruteForcePrevention.maxDelaySeconds")); bfMaxDelay.add(RangeValidator.minimum(0)); form.add(bfMaxDelay); final TextField<List<String>> netmasks = new TextField<List<String>>("bfWhitelistedNetmasks", new PropertyModel<List<String>>(this, "config.bruteForcePrevention.whitelistedMasks")) { @Override public <C> IConverter<C> getConverter(Class<C> type) { return (IConverter<C>) new CommaSeparatedListConverter(); } }; netmasks.add(new IValidator<List<String>>() { @Override public void validate(IValidatable<List<String>> validatable) { List<String> masks = validatable.getValue(); for (String mask : masks) { try { new IpAddressMatcher(mask); } catch(Exception e) { form.error(new ParamResourceModel("invalidMask", getPage(), mask).getString()); } } } }); form.add(netmasks); form.add(new AbstractFormValidator() { @Override public void validate(Form<?> form) { Integer min = bfMinDelay.getConvertedInput(); Integer max = bfMaxDelay.getConvertedInput(); if(max < min) { form.error(new ParamResourceModel("bfInvalidMinMax", getPage()).getString()); } } @Override public FormComponent<?>[] getDependentFormComponents() { return new FormComponent[] {bfMinDelay, bfMaxDelay}; } }); final TextField<Integer> bfMaxBlockedThreads = new TextField<Integer>("bfMaxBlockedThreads", new PropertyModel<Integer>(this, "config.bruteForcePrevention.maxBlockedThreads")); bfMaxBlockedThreads.add(RangeValidator.minimum(0)); form.add(bfMaxBlockedThreads); form.add(new AuthenticationFiltersPanel("authFilters")); form.add(new HelpLink("authFiltersHelp").setDialog(dialog)); form.add(new AuthenticationProvidersPanel("authProviders")); form.add(new HelpLink("authProvidersHelp").setDialog(dialog)); form.add(new SecurityFilterChainsPanel("authChains",config)); form.add(new HelpLink("authChainsHelp").setDialog(dialog)); form.add(authFilterChainPanel = new AuthFilterChainPanel("filterChain", new PropertyModel<GeoServerSecurityFilterChain>(form.getModel(), "filterChain"))); form.add(new HelpLink("filterChainHelp").setDialog(dialog)); form.add(new AuthenticationChainPanel("providerChain", form)); form.add(new HelpLink("providerChainHelp").setDialog(dialog)); form.add(new SubmitLink("save", form) { @Override public void onSubmit() { try { getSecurityManager() .saveSecurityConfig((SecurityManagerConfig) getForm().getModelObject()); getSecurityManager().saveFilter(logoutFilterConfig); getSecurityManager().saveFilter(sslFilterConfig); doReturn(); } catch (Exception e) { LOGGER.log(Level.WARNING, "Error saving authentication config", e); error(e); } } }); form.add(new Link("cancel") { @Override public void onClick() { doReturn(); } }); } public void updateChainComponents() { form.replace(new SecurityFilterChainsPanel("authChains", config)); } class AuthenticationChainPanel extends FormComponentPanel { public AuthenticationChainPanel(String id, Form form) { super(id, new Model()); add(new AuthenticationChainPalette("authProviderNames")); } } class AuthFilterChainPanel extends FormComponentPanel { DropDownChoice<HTTPMethod> httpMethodChoice; TextField<String> urlPathField,chainTestResultField; String urlPath,chainTestResult; HTTPMethod httpMethod=HTTPMethod.GET; public AuthFilterChainPanel(String id, IModel<GeoServerSecurityFilterChain> model) { super(id, new Model()); this.setOutputMarkupId(true); add(urlPathField=new TextField<String>("urlPath",new PropertyModel<String>(this,"urlPath"))); urlPathField.setOutputMarkupId(true); urlPathField.add( new OnChangeAjaxBehavior() { @Override protected void onUpdate(AjaxRequestTarget target) { } }); add(chainTestResultField=new TextField<String>("chainTestResult",new PropertyModel<String>(this,"chainTestResult"))); chainTestResultField.setEnabled(false); chainTestResultField.setOutputMarkupId(true); add(httpMethodChoice=new DropDownChoice<HTTPMethod>("httpMethod", new PropertyModel<HTTPMethod>(this,"httpMethod"), Arrays.asList(HTTPMethod.values()))); httpMethodChoice.setOutputMarkupId(true); httpMethodChoice.setNullValid(false); httpMethodChoice.add( new OnChangeAjaxBehavior() { @Override protected void onUpdate(AjaxRequestTarget target) { } }); add(new AjaxSubmitLink("chainTest") { @Override protected void onSubmit(AjaxRequestTarget target, Form<?> form) { try { String result="NONE"; HttpServletRequest request = getHttpRequest(); for (RequestFilterChain chain : config.getFilterChain().getRequestChains()) { if (httpMethod!=null && urlPath!=null) { if (getProxy().matcherForChain(chain).matches(request)) { result=chain.getName(); break; } } } chainTestResultField.getModel().setObject(result); target.add(chainTestResultField); } catch(Exception e) { error(e); LOGGER.log(Level.WARNING, "Connection error", e); target.add(feedbackPanel); } } protected GeoServerSecurityFilterChainProxy getProxy() { return GeoServerExtensions.bean(GeoServerSecurityFilterChainProxy.class); } HttpServletRequest getHttpRequest() { return new HttpServletRequest() { public void setCharacterEncoding(String env) throws UnsupportedEncodingException { } public void setAttribute(String name, Object o) { } public void removeAttribute(String name) { } public boolean isSecure() { return false; } public int getServerPort() { return 0; } public String getServerName() { return null; } public String getScheme() { return null; } public RequestDispatcher getRequestDispatcher(String path) { return null; } public int getRemotePort() { return 0; } public String getRemoteHost() { return null; } public String getRemoteAddr() { return null; } public String getRealPath(String path) { return null; } public BufferedReader getReader() throws IOException { return null; } public String getProtocol() { return null; } public String[] getParameterValues(String name) { return null; } public Enumeration getParameterNames() { return null; } public Map getParameterMap() { return null; } public String getParameter(String name) { return null; } public Enumeration getLocales() { return null; } public Locale getLocale() { return null; } public int getLocalPort() { return 0; } @Override public ServletContext getServletContext() { return null; } @Override public AsyncContext startAsync() throws IllegalStateException { return null; } @Override public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { return null; } @Override public boolean isAsyncStarted() { return false; } @Override public boolean isAsyncSupported() { return false; } @Override public AsyncContext getAsyncContext() { return null; } @Override public DispatcherType getDispatcherType() { return null; } public String getLocalName() { return null; } public String getLocalAddr() { return null; } public ServletInputStream getInputStream() throws IOException { return null; } public String getContentType() { return null; } public int getContentLength() { return 0; } public String getCharacterEncoding() { return null; } public Enumeration getAttributeNames() { return null; } public Object getAttribute(String name) { return null; } public boolean isUserInRole(String role) { return false; } public boolean isRequestedSessionIdValid() { return false; } public boolean isRequestedSessionIdFromUrl() { return false; } @Override public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { return false; } @Override public void login(String username, String password) throws ServletException { } @Override public void logout() throws ServletException { } @Override public Collection<Part> getParts() throws IOException, ServletException { return null; } @Override public Part getPart(String name) throws IOException, ServletException { return null; } public boolean isRequestedSessionIdFromURL() { return false; } public boolean isRequestedSessionIdFromCookie() { return false; } public Principal getUserPrincipal() { return null; } public HttpSession getSession(boolean create) { return null; } public HttpSession getSession() { return null; } public String getServletPath() { return ""; } public String getRequestedSessionId() { return null; } public StringBuffer getRequestURL() { return null; } public String getRequestURI() { return null; } public String getRemoteUser() { return null; } public String getQueryString() { if(urlPath == null || urlPath.indexOf("?") == -1) { return null; } else { return urlPath.substring(urlPath.indexOf("?") + 1); } } public String getPathTranslated() { return null; } public String getPathInfo() { if(urlPath == null || urlPath.indexOf("?") == -1) { return urlPath; } else { return urlPath.substring(0, urlPath.indexOf("?")); } } public String getMethod() { return httpMethod.toString(); } public int getIntHeader(String name) { return 0; } public Enumeration getHeaders(String name) { return null; } public Enumeration getHeaderNames() { return null; } public String getHeader(String name) { return null; } public long getDateHeader(String name) { return 0; } public Cookie[] getCookies() { return null; } public String getContextPath() { return null; } public String getAuthType() { return null; } }; } }.setDefaultFormProcessing(false)); } } }