diff --git a/scm-plugin-backend/pom.xml b/scm-plugin-backend/pom.xml index dd04bd385c..cc0ab9e5a2 100644 --- a/scm-plugin-backend/pom.xml +++ b/scm-plugin-backend/pom.xml @@ -32,6 +32,26 @@ 1.1 provided + + + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + + + + org.slf4j + log4j-over-slf4j + ${slf4j.version} + + + + ch.qos.logback + logback-classic + ${logback.version} + org.freemarker @@ -51,12 +71,6 @@ ${jersey.version} - - ch.qos.logback - logback-classic - ${logback.version} - - rome rome diff --git a/scm-plugin-backend/src/main/java/sonia/scm/plugin/AdminAccountConfiguration.java b/scm-plugin-backend/src/main/java/sonia/scm/plugin/AdminAccountConfiguration.java new file mode 100644 index 0000000000..8ad43824a0 --- /dev/null +++ b/scm-plugin-backend/src/main/java/sonia/scm/plugin/AdminAccountConfiguration.java @@ -0,0 +1,229 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.plugin; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Objects; + +import org.apache.shiro.authc.SaltedAuthenticationInfo; +import org.apache.shiro.codec.Base64; +import org.apache.shiro.subject.PrincipalCollection; +import org.apache.shiro.subject.SimplePrincipalCollection; +import org.apache.shiro.util.ByteSource; + +//~--- JDK imports ------------------------------------------------------------ + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * + * @author Sebastian Sdorra + */ +@XmlRootElement(name = "admin-account") +@XmlAccessorType(XmlAccessType.FIELD) +public class AdminAccountConfiguration implements SaltedAuthenticationInfo +{ + + /** Field description */ + private static final long serialVersionUID = -8678832281151044462L; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + */ + public AdminAccountConfiguration() {} + + /** + * Constructs ... + * + * + * @param username + * @param salt + * @param password + */ + public AdminAccountConfiguration(String username, String salt, + String password) + { + this.username = username; + this.salt = salt; + this.password = password; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param obj + * + * @return + */ + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final AdminAccountConfiguration other = (AdminAccountConfiguration) obj; + + return Objects.equal(username, other.username) + && Objects.equal(salt, other.salt) + && Objects.equal(password, other.password); + } + + /** + * Method description + * + * + * @return + */ + @Override + public int hashCode() + { + return Objects.hashCode(username, salt, password); + } + + /** + * Method description + * + * + * @return + */ + @Override + public String toString() + { + //J- + return Objects.toStringHelper(this) + .add("username", username) + .add("salt", "xxx") + .add("password", "xxx") + .toString(); + //J+ + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + public Object getCredentials() + { + return password; + } + + /** + * Method description + * + * + * @return + */ + @Override + public ByteSource getCredentialsSalt() + { + return ByteSource.Util.bytes(Base64.decode(salt)); + } + + /** + * Method description + * + * + * @return + */ + public String getPassword() + { + return password; + } + + /** + * Method description + * + * + * @return + */ + @Override + public PrincipalCollection getPrincipals() + { + + // TODO + return new SimplePrincipalCollection(username, "scm-backend"); + } + + /** + * Method description + * + * + * @return + */ + public String getSalt() + { + return salt; + } + + /** + * Method description + * + * + * @return + */ + public String getUsername() + { + return username; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private String password; + + /** Field description */ + private String salt; + + /** Field description */ + private String username; +} diff --git a/scm-plugin-backend/src/main/java/sonia/scm/plugin/BackendConfiguration.java b/scm-plugin-backend/src/main/java/sonia/scm/plugin/BackendConfiguration.java index cc2538033f..d30b781433 100644 --- a/scm-plugin-backend/src/main/java/sonia/scm/plugin/BackendConfiguration.java +++ b/scm-plugin-backend/src/main/java/sonia/scm/plugin/BackendConfiguration.java @@ -42,7 +42,6 @@ import sonia.scm.xml.XmlIntervalAdapter; import java.io.File; - import java.util.Set; import javax.xml.bind.annotation.XmlAccessType; @@ -61,6 +60,17 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; public class BackendConfiguration { + /** + * Method description + * + * + * @return + */ + public AdminAccountConfiguration getAdminAccount() + { + return adminAccount; + } + /** * Method description * @@ -140,6 +150,10 @@ public class BackendConfiguration //~--- fields --------------------------------------------------------------- + /** Field description */ + @XmlElement(name = "admin-account") + private AdminAccountConfiguration adminAccount; + /** Field description */ @XmlElement(name = "directory") @XmlElementWrapper(name = "directories") diff --git a/scm-plugin-backend/src/main/java/sonia/scm/plugin/BackendContextListener.java b/scm-plugin-backend/src/main/java/sonia/scm/plugin/BackendContextListener.java index 05672e3380..efa88255f7 100644 --- a/scm-plugin-backend/src/main/java/sonia/scm/plugin/BackendContextListener.java +++ b/scm-plugin-backend/src/main/java/sonia/scm/plugin/BackendContextListener.java @@ -39,10 +39,14 @@ import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.servlet.GuiceServletContextListener; +import org.apache.shiro.guice.web.ShiroWebModule; + import sonia.scm.plugin.scanner.PluginScannerScheduler; +import sonia.scm.plugin.security.SecurityModule; //~--- JDK imports ------------------------------------------------------------ +import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; /** @@ -74,6 +78,7 @@ public class BackendContextListener extends GuiceServletContextListener @Override public void contextInitialized(ServletContextEvent servletContextEvent) { + this.servletContext = servletContextEvent.getServletContext(); super.contextInitialized(servletContextEvent); scheduler = injector.getInstance(PluginScannerScheduler.class); scheduler.start(); @@ -90,7 +95,8 @@ public class BackendContextListener extends GuiceServletContextListener @Override protected Injector getInjector() { - injector = Guice.createInjector(new ScmBackendModule()); + injector = Guice.createInjector(ShiroWebModule.guiceFilterModule(), + new SecurityModule(servletContext), new ScmBackendModule()); return injector; } @@ -102,4 +108,7 @@ public class BackendContextListener extends GuiceServletContextListener /** Field description */ private PluginScannerScheduler scheduler; + + /** Field description */ + private ServletContext servletContext; } diff --git a/scm-plugin-backend/src/main/java/sonia/scm/plugin/Roles.java b/scm-plugin-backend/src/main/java/sonia/scm/plugin/Roles.java new file mode 100644 index 0000000000..fe37f8b681 --- /dev/null +++ b/scm-plugin-backend/src/main/java/sonia/scm/plugin/Roles.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ +package sonia.scm.plugin; + +/** + * + * @author Sebastian Sdorra + */ +public class Roles +{ + public static final String ADMIN = "admin"; +} diff --git a/scm-plugin-backend/src/main/java/sonia/scm/plugin/ScmBackendModule.java b/scm-plugin-backend/src/main/java/sonia/scm/plugin/ScmBackendModule.java index 4f3139271b..3e09ffbced 100644 --- a/scm-plugin-backend/src/main/java/sonia/scm/plugin/ScmBackendModule.java +++ b/scm-plugin-backend/src/main/java/sonia/scm/plugin/ScmBackendModule.java @@ -36,7 +36,6 @@ package sonia.scm.plugin; //~--- non-JDK imports -------------------------------------------------------- import com.google.inject.multibindings.Multibinder; -import com.google.inject.name.Names; import com.google.inject.servlet.ServletModule; import net.sf.ehcache.CacheManager; @@ -51,7 +50,6 @@ import sonia.scm.plugin.scanner.DefaultPluginScannerFactory; import sonia.scm.plugin.scanner.PluginScannerFactory; import sonia.scm.plugin.scanner.PluginScannerScheduler; import sonia.scm.plugin.scanner.TimerPluginScannerScheduler; -import sonia.scm.util.Util; import sonia.scm.web.proxy.ProxyConfigurationProvider; import sonia.scm.web.proxy.ProxyServlet; diff --git a/scm-plugin-backend/src/main/java/sonia/scm/plugin/rest/AdminResource.java b/scm-plugin-backend/src/main/java/sonia/scm/plugin/rest/AdminResource.java new file mode 100644 index 0000000000..8be0a12453 --- /dev/null +++ b/scm-plugin-backend/src/main/java/sonia/scm/plugin/rest/AdminResource.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.plugin.rest; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.inject.Inject; + +import sonia.scm.plugin.BackendConfiguration; + +//~--- JDK imports ------------------------------------------------------------ + +import com.sun.jersey.api.view.Viewable; + +import java.util.Map; + +import javax.servlet.ServletContext; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +/** + * + * @author Sebastian Sdorra + */ +@Path("admin") +public class AdminResource extends ViewableResource +{ + + /** Field description */ + private static final String PAGE_OVERVIEW = "/admin/index"; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param context + * @param configuration + */ + @Inject + public AdminResource(ServletContext context, + BackendConfiguration configuration) + { + super(context, configuration); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @GET + @Path("index.html") + @Produces(MediaType.TEXT_HTML) + public Viewable overview() + { + Map env = createVarMap("Administrator"); + + return new Viewable(PAGE_OVERVIEW, env); + } +} diff --git a/scm-plugin-backend/src/main/java/sonia/scm/plugin/rest/ErrorResource.java b/scm-plugin-backend/src/main/java/sonia/scm/plugin/rest/ErrorResource.java new file mode 100644 index 0000000000..0d415e8323 --- /dev/null +++ b/scm-plugin-backend/src/main/java/sonia/scm/plugin/rest/ErrorResource.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.plugin.rest; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.inject.Inject; + +import sonia.scm.plugin.BackendConfiguration; + +//~--- JDK imports ------------------------------------------------------------ + +import com.sun.jersey.api.view.Viewable; + +import java.util.Map; + +import javax.servlet.ServletContext; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +/** + * + * @author Sebastian Sdorra + */ +@Path("error") +public class ErrorResource extends ViewableResource +{ + + /** Field description */ + private static final String PAGE_UNAUTHORIZED = "/error/Unauthorized.html"; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param context + * @param configuration + */ + @Inject + public ErrorResource(ServletContext context, + BackendConfiguration configuration) + { + super(context, configuration); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @GET + @Path("unauthorized.html") + @Produces(MediaType.TEXT_HTML) + public Viewable unauthorized() + { + Map env = createVarMap("Unauthorized"); + + return new Viewable(PAGE_UNAUTHORIZED, env); + } +} diff --git a/scm-plugin-backend/src/main/java/sonia/scm/plugin/rest/LoginResource.java b/scm-plugin-backend/src/main/java/sonia/scm/plugin/rest/LoginResource.java new file mode 100644 index 0000000000..00c4897814 --- /dev/null +++ b/scm-plugin-backend/src/main/java/sonia/scm/plugin/rest/LoginResource.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.plugin.rest; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.inject.Inject; + +import sonia.scm.plugin.BackendConfiguration; + +//~--- JDK imports ------------------------------------------------------------ + +import com.sun.jersey.api.view.Viewable; + +import java.util.Map; + +import javax.servlet.ServletContext; + +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +/** + * + * @author Sebastian Sdorra + */ +@Path("/page/login.html") +public class LoginResource extends ViewableResource +{ + + /** Field description */ + private static final String PAGE_LOGIN = "/login"; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param context + * @param configuration + */ + @Inject + public LoginResource(ServletContext context, + BackendConfiguration configuration) + { + super(context, configuration); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @POST + @Produces(MediaType.TEXT_HTML) + public Viewable loginFailed() + { + Map vars = createVarMap("Login"); + + vars.put("error", "Login failed"); + + return new Viewable(PAGE_LOGIN, vars); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @GET + @Produces(MediaType.TEXT_HTML) + public Viewable getForm() + { + Map vars = createVarMap("Login"); + + return new Viewable(PAGE_LOGIN, vars); + } +} diff --git a/scm-plugin-backend/src/main/java/sonia/scm/plugin/security/DefaultAdminRealm.java b/scm-plugin-backend/src/main/java/sonia/scm/plugin/security/DefaultAdminRealm.java new file mode 100644 index 0000000000..2d2f7e8fb7 --- /dev/null +++ b/scm-plugin-backend/src/main/java/sonia/scm/plugin/security/DefaultAdminRealm.java @@ -0,0 +1,154 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.plugin.security; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Inject; + +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.UnknownAccountException; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.authc.credential.CredentialsMatcher; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.plugin.AdminAccountConfiguration; +import sonia.scm.plugin.BackendConfiguration; +import sonia.scm.plugin.Roles; + +/** + * + * @author Sebastian Sdorra + */ +public class DefaultAdminRealm extends AuthorizingRealm +{ + + /** Field description */ + public static final String NAME = "scm.backend"; + + /** + * the logger for ScmBackendRealm + */ + private static final Logger logger = + LoggerFactory.getLogger(DefaultAdminRealm.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param configuration + * @param credentialsMatcher + */ + @Inject + public DefaultAdminRealm(BackendConfiguration configuration, + CredentialsMatcher credentialsMatcher) + { + super(credentialsMatcher); + this.configuration = configuration; + setAuthenticationTokenClass(UsernamePasswordToken.class); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param token + * + * @return + * + * @throws AuthenticationException + */ + @Override + protected AuthenticationInfo doGetAuthenticationInfo( + AuthenticationToken token) + throws AuthenticationException + { + Preconditions.checkNotNull(token); + + UsernamePasswordToken upToken = (UsernamePasswordToken) token; + + String username = upToken.getUsername(); + + if (logger.isDebugEnabled()) + { + logger.debug("start authentication for user {}", username); + } + + AdminAccountConfiguration adminAccount = configuration.getAdminAccount(); + + if (!adminAccount.getUsername().equals(adminAccount.getUsername())) + { + throw new UnknownAccountException("unknown account ".concat(username)); + } + + return adminAccount; + } + + /** + * Method description + * + * + * @param principals + * + * @return + */ + @Override + protected AuthorizationInfo doGetAuthorizationInfo( + PrincipalCollection principals) + { + if (principals == null) + { + throw new AuthenticationException("principals should not be null"); + } + + return new SimpleAuthorizationInfo(ImmutableSet.of(Roles.ADMIN)); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private BackendConfiguration configuration; +} diff --git a/scm-plugin-backend/src/main/java/sonia/scm/plugin/security/SecurityModule.java b/scm-plugin-backend/src/main/java/sonia/scm/plugin/security/SecurityModule.java new file mode 100644 index 0000000000..de32606898 --- /dev/null +++ b/scm-plugin-backend/src/main/java/sonia/scm/plugin/security/SecurityModule.java @@ -0,0 +1,198 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.plugin.security; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.inject.name.Named; +import com.google.inject.name.Names; + +import org.apache.shiro.authc.credential.CredentialsMatcher; +import org.apache.shiro.authc.credential.HashedCredentialsMatcher; +import org.apache.shiro.crypto.RandomNumberGenerator; +import org.apache.shiro.crypto.SecureRandomNumberGenerator; +import org.apache.shiro.crypto.hash.SimpleHash; +import org.apache.shiro.guice.web.ShiroWebModule; +import org.apache.shiro.util.ByteSource; + +import sonia.scm.plugin.Roles; + +//~--- JDK imports ------------------------------------------------------------ + +import javax.servlet.ServletContext; + +import javax.swing.JOptionPane; + +/** + * + * @author Sebastian Sdorra + */ +public class SecurityModule extends ShiroWebModule +{ + + /** Field description */ + private static final String ATTRIBUTE_FAILURE = "shiroLoginFailure"; + + /** Field description */ + private static final String HASH_ALGORITHM = "SHA-256"; + + /** Field description */ + private static final int HASH_ITERATIONS = 1024; + + /** Field description */ + private static final String PAGE_LOGIN = "/page/login.html"; + + /** Field description */ + private static final String PAGE_SUCCESS = "/admin/index.html"; + + /** Field description */ + private static final String PAGE_UNAUTHORIZED = "/error/unauthorized.html"; + + /** Field description */ + private static final String PARAM_PASSWORD = "password"; + + /** Field description */ + private static final String PARAM_REMEMBERME = "rememberme"; + + /** Field description */ + private static final String PARAM_USERNAME = "username"; + + /** Field description */ + private static final String PATTERN_ADMIN = "/admin/**"; + + /** Field description */ + private static final Named NAMED_USERNAMEPARAM = + Names.named("shiro.usernameParam"); + + /** Field description */ + private static final Named NAMED_UNAUTHORIZEDURL = + Names.named("shiro.unauthorizedUrl"); + + /** Field description */ + private static final Named NAMED_SUCCESSURL = Names.named("shiro.successUrl"); + + /** Field description */ + private static final Named NAMED_REMEMBERMEPARAM = + Names.named("shiro.rememberMeParam"); + + /** Field description */ + private static final Named NAMED_PASSWORDPARAM = + Names.named("shiro.passwordParam"); + + /** Field description */ + private static final Named NAMED_LOGINURL = Names.named("shiro.loginUrl"); + + /** Field description */ + private static final Named NAMED_FAILUREKEYATTRIBUTE = + Names.named("shiro.failureKeyAttribute"); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param servletContext + */ + public SecurityModule(ServletContext servletContext) + { + super(servletContext); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param args + */ + public static void main(String[] args) + { + String value = JOptionPane.showInputDialog("Password"); + RandomNumberGenerator rng = new SecureRandomNumberGenerator(); + ByteSource salt = rng.nextBytes(); + SimpleHash hash = new SimpleHash(HASH_ALGORITHM, value, salt, + HASH_ITERATIONS); + + System.out.append("Salt: ").println(salt.toBase64()); + System.out.append("Hash: ").println(hash.toBase64()); + } + + /** + * Method description + * + */ + @Override + protected void configureShiroWeb() + { + bindConstants(); + bindCredentialsMatcher(); + + // bind realm + bindRealm().to(DefaultAdminRealm.class); + + // add filters + addFilterChain(PAGE_LOGIN, AUTHC); + addFilterChain(PATTERN_ADMIN, AUTHC, config(ROLES, Roles.ADMIN)); + } + + /** + * Method description + * + */ + private void bindConstants() + { + bindConstant().annotatedWith(NAMED_LOGINURL).to(PAGE_LOGIN); + bindConstant().annotatedWith(NAMED_USERNAMEPARAM).to(PARAM_USERNAME); + bindConstant().annotatedWith(NAMED_PASSWORDPARAM).to(PARAM_PASSWORD); + bindConstant().annotatedWith(NAMED_REMEMBERMEPARAM).to(PARAM_REMEMBERME); + bindConstant().annotatedWith(NAMED_SUCCESSURL).to(PAGE_SUCCESS); + bindConstant().annotatedWith(NAMED_UNAUTHORIZEDURL).to(PAGE_UNAUTHORIZED); + bindConstant().annotatedWith(NAMED_FAILUREKEYATTRIBUTE).to( + ATTRIBUTE_FAILURE); + } + + /** + * Method description + * + */ + private void bindCredentialsMatcher() + { + HashedCredentialsMatcher matcher = + new HashedCredentialsMatcher(HASH_ALGORITHM); + + matcher.setHashIterations(HASH_ITERATIONS); + matcher.setStoredCredentialsHexEncoded(false); + bind(CredentialsMatcher.class).toInstance(matcher); + } +} diff --git a/scm-plugin-backend/src/main/resources/logback.xml b/scm-plugin-backend/src/main/resources/logback.xml index 9f6c714a38..aba83a8d17 100644 --- a/scm-plugin-backend/src/main/resources/logback.xml +++ b/scm-plugin-backend/src/main/resources/logback.xml @@ -52,6 +52,9 @@ + + + diff --git a/scm-plugin-backend/src/main/webapp/WEB-INF/ftl/admin/index.html b/scm-plugin-backend/src/main/webapp/WEB-INF/ftl/admin/index.html new file mode 100644 index 0000000000..9ba191266f --- /dev/null +++ b/scm-plugin-backend/src/main/webapp/WEB-INF/ftl/admin/index.html @@ -0,0 +1,5 @@ +<#include "../template/header.html"> + +

Admin

+ +<#include "../template/footer.html"> \ No newline at end of file diff --git a/scm-plugin-backend/src/main/webapp/WEB-INF/ftl/error/unauthorized.html b/scm-plugin-backend/src/main/webapp/WEB-INF/ftl/error/unauthorized.html new file mode 100644 index 0000000000..7f989ea5b1 --- /dev/null +++ b/scm-plugin-backend/src/main/webapp/WEB-INF/ftl/error/unauthorized.html @@ -0,0 +1,5 @@ +<#include "../template/header.html"> + +

Unauthorized

+ +<#include "../template/footer.html"> \ No newline at end of file diff --git a/scm-plugin-backend/src/main/webapp/WEB-INF/ftl/login.html b/scm-plugin-backend/src/main/webapp/WEB-INF/ftl/login.html new file mode 100644 index 0000000000..a3524202d0 --- /dev/null +++ b/scm-plugin-backend/src/main/webapp/WEB-INF/ftl/login.html @@ -0,0 +1,28 @@ +<#include "template/header.html"> + +
+ + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ +<#include "template/footer.html"> \ No newline at end of file diff --git a/scm-plugin-backend/src/main/webapp/WEB-INF/ftl/template/header.html b/scm-plugin-backend/src/main/webapp/WEB-INF/ftl/template/header.html index 4ed6e881af..1928caf9ec 100644 --- a/scm-plugin-backend/src/main/webapp/WEB-INF/ftl/template/header.html +++ b/scm-plugin-backend/src/main/webapp/WEB-INF/ftl/template/header.html @@ -113,6 +113,9 @@

${title}

+ <#if error??> +
${error}
+