merged plugins pom

This commit is contained in:
Thorsten Ludewig
2011-01-09 12:55:57 +01:00
29 changed files with 1163 additions and 89 deletions

View File

@@ -78,6 +78,27 @@ public class HgConfigResource
this.handler = handler;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param uriInfo
*
* @return
*/
@POST
@Path("auto-configuration")
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public HgConfig autoConfiguration(@Context UriInfo uriInfo)
{
handler.setConfig(null);
handler.doAutoConfiguration();
return handler.getConfig();
}
//~--- get methods ----------------------------------------------------------
/**

View File

@@ -101,11 +101,8 @@ public class HgRepositoryHandler
* Method description
*
*/
@Override
public void loadConfig()
public void doAutoConfiguration()
{
super.loadConfig();
HgInstaller installer = null;
if (SystemUtil.isWindows())
@@ -163,6 +160,17 @@ public class HgRepositoryHandler
storeConfig();
}
/**
* Method description
*
*/
@Override
public void loadConfig()
{
super.loadConfig();
doAutoConfiguration();
}
//~--- get methods ----------------------------------------------------------
/**

View File

@@ -30,6 +30,7 @@
*/
registerConfigPanel({
id: 'hgConfigForm',
xtype : 'configForm',
title : 'Mercurial Settings',
items : [{
@@ -56,6 +57,14 @@ registerConfigPanel({
name: 'useOptimizedBytecode',
fieldLabel: 'Optimized Bytecode (.pyo)',
inputValue: 'true'
},{
xtype: 'button',
text: 'Load Auto-Configuration',
fieldLabel: 'Auto-Configuration',
handler: function(){
var self = Ext.getCmp('hgConfigForm');
self.loadConfig( self.el, 'config/repositories/hg/auto-configuration.json', 'POST' );
}
}],
onSubmit: function(values){
@@ -76,10 +85,14 @@ registerConfigPanel({
},
onLoad: function(el){
this.loadConfig(el, 'config/repositories/hg.json', 'GET');
},
loadConfig: function(el, url, method){
var tid = setTimeout( function(){ el.mask('Loading ...'); }, 100);
Ext.Ajax.request({
url: restUrl + 'config/repositories/hg.json',
method: 'GET',
url: restUrl + url,
method: method,
scope: this,
disableCaching: true,
success: function(response){

View File

@@ -138,7 +138,7 @@ public class PAMAuthenticationHandler implements AuthenticationHandler
User user = new User(username);
user.setAdmin(isAdmin(unixUser));
result = new AuthenticationResult(user);
result = new AuthenticationResult(user, unixUser.getGroups());
}
}
catch (PAMException ex)

View File

@@ -377,6 +377,19 @@ public class Group
return type;
}
/**
* Method description
*
*
* @param member
*
* @return
*/
public boolean isMember(String member)
{
return (members != null) && members.contains(member);
}
/**
* Method description
*

View File

@@ -38,10 +38,25 @@ package sonia.scm.group;
import sonia.scm.ListenerSupport;
import sonia.scm.Manager;
//~--- JDK imports ------------------------------------------------------------
import java.util.Collection;
/**
*
* @author Sebastian Sdorra
*/
public interface GroupManager
extends Manager<Group, GroupException>,
ListenerSupport<GroupListener> {}
extends Manager<Group, GroupException>, ListenerSupport<GroupListener>
{
/**
* Method description
*
*
* @param member
*
* @return
*/
public Collection<Group> getGroupsForMember(String member);
}

View File

@@ -69,6 +69,7 @@ public class Permission implements Serializable
*/
public Permission(String name)
{
this();
this.name = name;
}
@@ -81,10 +82,24 @@ public class Permission implements Serializable
*/
public Permission(String name, PermissionType type)
{
this.name = name;
this(name);
this.type = type;
}
/**
* Constructs ...
*
*
* @param name
* @param type
* @param groupPermission
*/
public Permission(String name, PermissionType type, boolean groupPermission)
{
this(name, type);
this.groupPermission = groupPermission;
}
//~--- get methods ----------------------------------------------------------
/**
@@ -109,8 +124,30 @@ public class Permission implements Serializable
return type;
}
/**
* Method description
*
*
* @return
*/
public boolean isGroupPermission()
{
return groupPermission;
}
//~--- set methods ----------------------------------------------------------
/**
* Method description
*
*
* @param groupPermission
*/
public void setGroupPermission(boolean groupPermission)
{
this.groupPermission = groupPermission;
}
/**
* Method description
*
@@ -135,6 +172,9 @@ public class Permission implements Serializable
//~--- fields ---------------------------------------------------------------
/** Field description */
private boolean groupPermission = false;
/** Field description */
private String name;

View File

@@ -43,6 +43,7 @@ import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
import java.util.Collection;
import java.util.List;
/**
@@ -134,7 +135,8 @@ public class PermissionUtil
if (permissions != null)
{
result = hasPermission(permissions, username, pt);
result = hasPermission(permissions, username,
securityContext.getGroups(), pt);
}
}
@@ -147,12 +149,13 @@ public class PermissionUtil
*
* @param permissions
* @param username
* @param groups
* @param pt
*
* @return
*/
private static boolean hasPermission(List<Permission> permissions,
String username, PermissionType pt)
String username, Collection<String> groups, PermissionType pt)
{
boolean result = false;
@@ -160,12 +163,15 @@ public class PermissionUtil
{
String name = p.getName();
if ((name != null) && name.equalsIgnoreCase(username)
&& (p.getType().getValue() >= pt.getValue()))
if ((name != null) && (p.getType().getValue() >= pt.getValue()))
{
result = true;
if (name.equals(username)
|| (p.isGroupPermission() && groups.contains(p.getName())))
{
result = true;
break;
break;
}
}
}

View File

@@ -0,0 +1,115 @@
/**
* 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.web.security;
//~--- non-JDK imports --------------------------------------------------------
import sonia.scm.user.User;
//~--- JDK imports ------------------------------------------------------------
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
public abstract class AbstractAuthenticationManager
implements AuthenticationManager
{
/**
* Method description
*
*
* @param listener
*/
@Override
public void addListener(AuthenticationListener listener)
{
listeners.add(listener);
}
/**
* Method description
*
*
* @param listeners
*/
@Override
public void addListeners(Collection<AuthenticationListener> listeners)
{
listeners.addAll(listeners);
}
/**
* Method description
*
*
* @param listener
*/
@Override
public void removeListener(AuthenticationListener listener)
{
listeners.remove(listener);
}
/**
* Method description
*
*
* @param request
* @param response
* @param user
*/
protected void fireAuthenticationEvent(HttpServletRequest request,
HttpServletResponse response, User user)
{
for (AuthenticationListener listener : listeners)
{
listener.onAuthentication(request, response, user);
}
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private Set<AuthenticationListener> listeners =
new HashSet<AuthenticationListener>();
}

View File

@@ -0,0 +1,64 @@
/**
* 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.web.security;
//~--- non-JDK imports --------------------------------------------------------
import sonia.scm.plugin.ExtensionPoint;
import sonia.scm.user.User;
//~--- JDK imports ------------------------------------------------------------
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
@ExtensionPoint
public interface AuthenticationListener
{
/**
* Method description
*
*
* @param request
* @param response
* @param user
*/
public void onAuthentication(HttpServletRequest request,
HttpServletResponse response, User user);
}

View File

@@ -36,7 +36,7 @@ package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import sonia.scm.Initable;
import sonia.scm.user.User;
import sonia.scm.ListenerSupport;
//~--- JDK imports ------------------------------------------------------------
@@ -49,7 +49,8 @@ import javax.servlet.http.HttpServletResponse;
*
* @author Sebastian Sdorra
*/
public interface AuthenticationManager extends Initable, Closeable
public interface AuthenticationManager
extends Initable, Closeable, ListenerSupport<AuthenticationListener>
{
/**
@@ -63,7 +64,6 @@ public interface AuthenticationManager extends Initable, Closeable
*
* @return
*/
public User authenticate(HttpServletRequest request,
HttpServletResponse response, String username,
String password);
public AuthenticationResult authenticate(HttpServletRequest request,
HttpServletResponse response, String username, String password);
}

View File

@@ -37,6 +37,10 @@ package sonia.scm.web.security;
import sonia.scm.user.User;
//~--- JDK imports ------------------------------------------------------------
import java.util.Collection;
/**
*
* @author Sebastian Sdorra
@@ -91,6 +95,37 @@ public class AuthenticationResult
this.state = state;
}
/**
* Constructs ...
*
*
*
* @param user
* @param groups
*/
public AuthenticationResult(User user, Collection<String> groups)
{
this.user = user;
this.groups = groups;
this.state = AuthenticationState.SUCCESS;
}
/**
* Constructs ...
*
*
* @param user
* @param groups
* @param state
*/
public AuthenticationResult(User user, Collection<String> groups,
AuthenticationState state)
{
this.user = user;
this.groups = groups;
this.state = state;
}
//~--- methods --------------------------------------------------------------
/**
@@ -118,6 +153,17 @@ public class AuthenticationResult
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public Collection<String> getGroups()
{
return groups;
}
/**
* Method description
*
@@ -142,6 +188,9 @@ public class AuthenticationResult
//~--- fields ---------------------------------------------------------------
/** Field description */
private Collection<String> groups;
/** Field description */
private AuthenticationState state;

View File

@@ -40,6 +40,8 @@ import sonia.scm.user.User;
//~--- JDK imports ------------------------------------------------------------
import java.util.Collection;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -76,6 +78,14 @@ public interface WebSecurityContext extends SecurityContext
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public Collection<String> getGroups();
/**
* Method description
*

View File

@@ -50,7 +50,10 @@ import static org.mockito.Mockito.*;
//~--- JDK imports ------------------------------------------------------------
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
*
@@ -108,6 +111,53 @@ public class PermissionUtilTest
PermissionUtil.assertPermission(repository, admams, PermissionType.OWNER);
}
/**
* Method description
*
*/
@Test
public void testGroupPermissions()
{
WebSecurityContext dent = mockGroupCtx(new User("dent", "Arthur Dent",
"arthur.dent@hitchhiker.com"),
"devel", "qa");
WebSecurityContext ford = mockGroupCtx(new User("ford", "Ford Prefect",
"ford.prefect@hitchhiker.com"), "devel");
WebSecurityContext zaphod = mockGroupCtx(new User("zaphod",
"Zaphod Beeblebrox",
"zaphod.beeblebrox@hitchhiker.com"), "qa");
WebSecurityContext trillian = mockGroupCtx(new User("trillian",
"Trillian Astra",
"trillian.astra@hitchhiker.com"));
Repository r = new Repository();
r.setPermissions(
new ArrayList<Permission>(
Arrays.asList(
new Permission("dent"),
new Permission("devel", PermissionType.WRITE, true),
new Permission("qa", PermissionType.READ, true))));
// member of both devel and qa
assertTrue(PermissionUtil.hasPermission(r, dent, PermissionType.READ));
assertTrue(PermissionUtil.hasPermission(r, dent, PermissionType.WRITE));
assertFalse(PermissionUtil.hasPermission(r, dent, PermissionType.OWNER));
// now, additionally the owner
r.getPermissions().add(new Permission("dent", PermissionType.OWNER));
assertTrue(PermissionUtil.hasPermission(r, dent, PermissionType.OWNER));
// member of just devel
assertTrue(PermissionUtil.hasPermission(r, ford, PermissionType.READ));
assertTrue(PermissionUtil.hasPermission(r, ford, PermissionType.WRITE));
assertFalse(PermissionUtil.hasPermission(r, ford, PermissionType.OWNER));
// member of just qa
assertTrue(PermissionUtil.hasPermission(r, zaphod, PermissionType.READ));
assertFalse(PermissionUtil.hasPermission(r, zaphod, PermissionType.WRITE));
assertFalse(PermissionUtil.hasPermission(r, zaphod, PermissionType.OWNER));
// member of no groups
assertFalse(PermissionUtil.hasPermission(r, trillian, PermissionType.READ));
assertFalse(PermissionUtil.hasPermission(r, trillian, PermissionType.WRITE));
assertFalse(PermissionUtil.hasPermission(r, trillian, PermissionType.OWNER));
}
//~--- get methods ----------------------------------------------------------
/**
@@ -162,6 +212,24 @@ public class PermissionUtilTest
return context;
}
/**
* Method description
*
*
* @param user
*
* @return
*/
private WebSecurityContext mockGroupCtx(User user, String... groups)
{
WebSecurityContext context = mockCtx(user);
Set<String> groupSet = new HashSet<String>(Arrays.asList(groups));
when(context.getGroups()).thenReturn(groupSet);
return context;
}
//~--- fields ---------------------------------------------------------------
/** Field description */

View File

@@ -39,6 +39,9 @@ import sonia.scm.user.User;
//~--- JDK imports ------------------------------------------------------------
import java.util.HashSet;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -97,6 +100,18 @@ public class DummyWebSecurityContext implements WebSecurityContext
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
@Override
public Set<String> getGroups()
{
return groups;
}
/**
* Method description
*
@@ -123,6 +138,9 @@ public class DummyWebSecurityContext implements WebSecurityContext
//~--- fields ---------------------------------------------------------------
/** Field description */
private Set<String> groups = new HashSet<String>();
/** Field description */
private User user;
}

View File

@@ -50,6 +50,7 @@ import sonia.scm.repository.RepositoryListener;
import sonia.scm.security.EncryptionHandler;
import sonia.scm.user.UserListener;
import sonia.scm.web.security.AuthenticationHandler;
import sonia.scm.web.security.AuthenticationListener;
import sonia.scm.web.security.XmlAuthenticationHandler;
//~--- JDK imports ------------------------------------------------------------
@@ -162,6 +163,19 @@ public class BindingExtensionProcessor implements ExtensionProcessor
repositoryListeners.add(listener);
}
else if (AuthenticationListener.class.isAssignableFrom(extensionClass))
{
if (logger.isInfoEnabled())
{
logger.info("bind AuthenticaitonListener {}",
extensionClass.getName());
}
AuthenticationListener listener =
(AuthenticationListener) extensionClass.newInstance();
authenticationListeners.add(listener);
}
else
{
if (logger.isInfoEnabled())
@@ -206,6 +220,17 @@ public class BindingExtensionProcessor implements ExtensionProcessor
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public Set<AuthenticationListener> getAuthenticationListeners()
{
return authenticationListeners;
}
/**
* Method description
*
@@ -307,6 +332,10 @@ public class BindingExtensionProcessor implements ExtensionProcessor
private Set<RepositoryListener> repositoryListeners =
new HashSet<RepositoryListener>();
/** Field description */
private Set<AuthenticationListener> authenticationListeners =
new HashSet<AuthenticationListener>();
/** Field description */
private Set<UserListener> userListeners = new HashSet<UserListener>();

View File

@@ -144,7 +144,12 @@ public class ScmContextListener extends GuiceServletContextListener
groupManager.init(context);
// init Authenticator
injector.getInstance(AuthenticationManager.class).init(context);
AuthenticationManager authenticationManager =
injector.getInstance(AuthenticationManager.class);
authenticationManager.init(context);
authenticationManager.addListeners(
bindExtProcessor.getAuthenticationListeners());
return injector;
}

View File

@@ -36,9 +36,12 @@ package sonia.scm;
//~--- non-JDK imports --------------------------------------------------------
import sonia.scm.user.User;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
import java.util.Collection;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
@@ -63,12 +66,15 @@ public class ScmState
* Constructs ...
*
*
* @param user
*
* @param securityContext
* @param repositoryTypes
*/
public ScmState(User user, Type[] repositoryTypes)
public ScmState(WebSecurityContext securityContext,
Collection<Type> repositoryTypes)
{
this.user = user;
this.user = securityContext.getUser();
this.groups = securityContext.getGroups();
this.repositoryTypes = repositoryTypes;
}
@@ -80,7 +86,18 @@ public class ScmState
*
* @return
*/
public Type[] getRepositoryTypes()
public Collection<String> getGroups()
{
return groups;
}
/**
* Method description
*
*
* @return
*/
public Collection<Type> getRepositoryTypes()
{
return repositoryTypes;
}
@@ -109,13 +126,24 @@ public class ScmState
//~--- set methods ----------------------------------------------------------
/**
* Method description
*
*
* @param groups
*/
public void setGroups(Collection<String> groups)
{
this.groups = groups;
}
/**
* Method description
*
*
* @param repositoryTypes
*/
public void setRepositoryTypes(Type[] repositoryTypes)
public void setRepositoryTypes(Collection<Type> repositoryTypes)
{
this.repositoryTypes = repositoryTypes;
}
@@ -144,9 +172,12 @@ public class ScmState
//~--- fields ---------------------------------------------------------------
/** Field description */
private Collection<String> groups;
/** Field description */
@XmlElement(name = "repositoryTypes")
private Type[] repositoryTypes;
private Collection<Type> repositoryTypes;
/** Field description */
private boolean success = true;

View File

@@ -0,0 +1,95 @@
/**
* 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.api.rest;
//~--- JDK imports ------------------------------------------------------------
import javax.xml.bind.annotation.XmlRootElement;
/**
*
* @author Sebastian Sdorra
*/
@XmlRootElement(name = "result")
public class RestActionResult
{
/**
* Constructs ...
*
*/
public RestActionResult() {}
/**
* Constructs ...
*
*
* @param success
*/
public RestActionResult(boolean success)
{
this.success = success;
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public boolean isSuccess()
{
return success;
}
//~--- set methods ----------------------------------------------------------
/**
* Method description
*
*
* @param success
*/
public void setSuccess(boolean success)
{
this.success = success;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private boolean success = false;
}

View File

@@ -99,7 +99,8 @@ public class AuthenticationResource
if (user != null)
{
resp = Response.ok(getState(user)).build();
resp = Response.ok(new ScmState(securityContext,
repositoryManger.getTypes())).build();
}
else
{
@@ -135,7 +136,7 @@ public class AuthenticationResource
if (user != null)
{
state = getState(user);
state = new ScmState(securityContext, repositoryManger.getTypes());
}
else
{
@@ -167,7 +168,7 @@ public class AuthenticationResource
logger.debug("return state for user {}", user.getName());
}
state = getState(user);
state = new ScmState(securityContext, repositoryManger.getTypes());
response = Response.ok(state).build();
}
else
@@ -178,25 +179,6 @@ public class AuthenticationResource
return response;
}
/**
* Method description
*
*
*
* @param user
*
* @return
*/
private ScmState getState(User user)
{
ScmState state = new ScmState();
state.setUser(user);
state.setRepositoryTypes(repositoryManger.getTypes().toArray(new Type[0]));
return state;
}
//~--- fields ---------------------------------------------------------------
/** Field description */

View File

@@ -0,0 +1,170 @@
/**
* 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.api.rest.resources;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.api.rest.RestActionResult;
import sonia.scm.security.EncryptionHandler;
import sonia.scm.user.User;
import sonia.scm.user.UserException;
import sonia.scm.user.UserManager;
import sonia.scm.user.xml.XmlUserManager;
import sonia.scm.util.AssertUtil;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
*
* @author Sebastian Sdorra
*/
@Path("action/change-password")
public class ChangePasswordResource
{
/** the logger for ChangePasswordResource */
private static final Logger logger =
LoggerFactory.getLogger(ChangePasswordResource.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param userManager
* @param encryptionHandler
* @param securityContextProvider
*/
@Inject
public ChangePasswordResource(
UserManager userManager, EncryptionHandler encryptionHandler,
Provider<WebSecurityContext> securityContextProvider)
{
this.userManager = userManager;
this.encryptionHandler = encryptionHandler;
this.securityContextProvider = securityContextProvider;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param oldPassword
* @param newPassword
*
* @return
*
* @throws IOException
* @throws UserException
*/
@POST
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Response changePassword(@FormParam("old-password") String oldPassword,
@FormParam("new-password") String newPassword)
throws UserException, IOException
{
AssertUtil.assertIsNotEmpty(oldPassword);
AssertUtil.assertIsNotEmpty(newPassword);
int length = newPassword.length();
if ((length < 6) || (length > 32))
{
throw new WebApplicationException(Response.Status.BAD_REQUEST);
}
Response response = null;
WebSecurityContext securityContext = securityContextProvider.get();
User currentUser = securityContext.getUser();
if (logger.isInfoEnabled())
{
logger.info("password change for user {}", currentUser.getName());
}
if (currentUser.getType().equals(XmlUserManager.TYPE))
{
User dbUser = userManager.get(currentUser.getName());
if (encryptionHandler.encrypt(oldPassword).equals(dbUser.getPassword()))
{
dbUser.setPassword(encryptionHandler.encrypt(newPassword));
userManager.modify(dbUser);
response = Response.ok(new RestActionResult(true)).build();
}
else
{
response = Response.status(Response.Status.BAD_REQUEST).build();
}
}
else
{
logger.error("only xml user can change their passwor");
response = Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
return response;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private EncryptionHandler encryptionHandler;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
/** Field description */
private UserManager userManager;
}

View File

@@ -60,6 +60,7 @@ import java.io.IOException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Set;
/**
*
@@ -323,6 +324,30 @@ public class XmlGroupManager extends AbstractGroupManager
return groups;
}
/**
* Method description
*
*
* @param member
*
* @return
*/
@Override
public Collection<Group> getGroupsForMember(String member)
{
LinkedList<Group> groups = new LinkedList<Group>();
for (Group group : groupDB.values())
{
if (group.isMember(member))
{
groups.add(group.clone());
}
}
return groups;
}
//~--- methods --------------------------------------------------------------
/**

View File

@@ -42,11 +42,18 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
//~--- JDK imports ------------------------------------------------------------
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -74,15 +81,18 @@ public class BasicSecurityContext implements WebSecurityContext
*
* @param configuration
* @param authenticator
* @param groupManager
* @param userManager
*/
@Inject
public BasicSecurityContext(ScmConfiguration configuration,
AuthenticationManager authenticator,
GroupManager groupManager,
UserManager userManager)
{
this.configuration = configuration;
this.authenticator = authenticator;
this.groupManager = groupManager;
this.userManager = userManager;
}
@@ -104,10 +114,13 @@ public class BasicSecurityContext implements WebSecurityContext
HttpServletResponse response, String username,
String password)
{
user = authenticator.authenticate(request, response, username, password);
AuthenticationResult ar = authenticator.authenticate(request, response,
username, password);
if (user != null)
if (ar != null)
{
user = ar.getUser();
try
{
user.setLastLogin(System.currentTimeMillis());
@@ -128,6 +141,20 @@ public class BasicSecurityContext implements WebSecurityContext
{
userManager.create(user);
}
Collection<String> groupCollection = ar.getGroups();
if (groupCollection != null)
{
groups.addAll(groupCollection);
}
loadGroups();
if (logger.isDebugEnabled())
{
logGroups();
}
}
catch (Exception ex)
{
@@ -150,10 +177,28 @@ public class BasicSecurityContext implements WebSecurityContext
public void logout(HttpServletRequest request, HttpServletResponse response)
{
user = null;
groups = new HashSet<String>();
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
@Override
public Collection<String> getGroups()
{
if (groups == null)
{
groups = new HashSet<String>();
}
return groups;
}
/**
* Method description
*
@@ -183,6 +228,51 @@ public class BasicSecurityContext implements WebSecurityContext
return getUser() != null;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*/
private void loadGroups()
{
Collection<Group> groupCollection =
groupManager.getGroupsForMember(user.getName());
if (groupCollection != null)
{
for (Group group : groupCollection)
{
groups.add(group.getName());
}
}
}
/**
* Method description
*
*/
private void logGroups()
{
StringBuilder msg = new StringBuilder("user ");
msg.append(user.getName()).append(" is member of ");
Iterator<String> groupIt = groups.iterator();
while (groupIt.hasNext())
{
msg.append(groupIt.next());
if (groupIt.hasNext())
{
msg.append(", ");
}
}
logger.debug(msg.toString());
}
//~--- fields ---------------------------------------------------------------
/** Field description */
@@ -191,6 +281,12 @@ public class BasicSecurityContext implements WebSecurityContext
/** Field description */
private ScmConfiguration configuration;
/** Field description */
private GroupManager groupManager;
/** Field description */
private Set<String> groups = new HashSet<String>();
/** Field description */
private User user;

View File

@@ -60,7 +60,7 @@ import javax.servlet.http.HttpServletResponse;
* @author Sebastian Sdorra
*/
@Singleton
public class ChainAuthenticatonManager implements AuthenticationManager
public class ChainAuthenticatonManager extends AbstractAuthenticationManager
{
/** the logger for ChainAuthenticatonManager */
@@ -97,11 +97,10 @@ public class ChainAuthenticatonManager implements AuthenticationManager
* @return
*/
@Override
public User authenticate(HttpServletRequest request,
HttpServletResponse response, String username,
String password)
public AuthenticationResult authenticate(HttpServletRequest request,
HttpServletResponse response, String username, String password)
{
User user = null;
AuthenticationResult ar = null;
for (AuthenticationHandler authenticator : authenticationHandlerSet)
{
@@ -122,8 +121,13 @@ public class ChainAuthenticatonManager implements AuthenticationManager
{
if (result.getState().isSuccessfully() && (result.getUser() != null))
{
user = result.getUser();
User user = result.getUser();
user.setType(authenticator.getType());
ar = result;
// notify authentication listeners
fireAuthenticationEvent(request, response, user);
}
break;
@@ -135,7 +139,7 @@ public class ChainAuthenticatonManager implements AuthenticationManager
}
}
return user;
return ar;
}
/**

View File

@@ -58,6 +58,7 @@
<script type="text/javascript" src="resources/js/sonia.config.js"></script>
<script type="text/javascript" src="resources/js/sonia.user.js"></script>
<script type="text/javascript" src="resources/js/sonia.group.js"></script>
<script type="text/javascript" src="resources/js/sonia.action.js"></script>
<script type="text/javascript" src="resources/js/sonia.plugin.js"></script>
<script type="text/javascript" src="resources/js/sonia.scm.js"></script>
<script type="text/javascript" src="plugins/sonia.plugin.js"></script>

View File

@@ -0,0 +1,125 @@
/* *
* 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
*
*/
Ext.ns('Sonia.action');
Sonia.action.ChangePasswordWindow = Ext.extend(Ext.Window,{
initComponent: function(){
var config = {
layout:'fit',
width:300,
height:170,
closable: false,
resizable: false,
plain: true,
border: false,
modal: true,
items: [{
id: 'changePasswordForm',
url: restUrl + 'action/change-password.json',
title: 'Change Password',
frame: true,
xtype: 'form',
monitorValid: true,
defaultType: 'textfield',
items: [{
name: 'old-password',
fieldLabel: 'Old Password',
inputType: 'password',
allowBlank: false,
minLength: 6,
maxLength: 32
},{
id: 'new-password',
name: 'new-password',
fieldLabel: 'New Password',
inputType: 'password',
allowBlank: false,
minLength: 6,
maxLength: 32
},{
name: 'confirm-password',
fieldLabel: 'Confirm Password',
inputType: 'password',
allowBlank: false,
minLength: 6,
maxLength: 32,
vtype: 'password',
initialPassField: 'new-password'
}],
buttons: [{
text: 'Ok',
formBind: true,
scope: this,
handler: this.changePassword
},{
text: 'Cancel',
scope: this,
handler: this.cancel
}]
}]
}
Ext.apply(this, Ext.apply(this.initialConfig, config));
Sonia.action.ChangePasswordWindow.superclass.initComponent.apply(this, arguments);
},
changePassword: function(){
var win = this;
var form = Ext.getCmp('changePasswordForm').getForm();
form.submit({
method:'POST',
waitTitle:'Connecting',
waitMsg:'Sending data...',
success: function(form, action){
if ( debug ){
console.debug( 'change password success' );
}
win.close();
},
failure: function(form, action){
if ( debug ){
console.debug( 'change password failed' );
}
Ext.Msg.alert('change password failed!');
}
});
},
cancel: function(){
this.close();
}
});

View File

@@ -46,6 +46,35 @@ Ext.ns('Sonia.repository');
// functions
Sonia.repository.getPermissionValue = function(type){
var value = 0;
switch (type){
case "READ":
value = 0;
break;
case "WRITE":
value = 10;
break;
case "OWNER":
value = 100;
break;
}
return value;
}
Sonia.repository.isMember = function(group){
var result = false;
if ( Ext.isDefined(state.groups) ){
for ( var i=0; i<state.groups.length; i++ ){
if ( state.groups[i] == group ){
result = true;
break;
}
}
}
return result;
}
Sonia.repository.hasPermission = function(repository, type){
var result = false;
if ( admin ){
@@ -53,10 +82,20 @@ Sonia.repository.hasPermission = function(repository, type){
} else {
var permissions = repository.permissions;
if ( Ext.isDefined(permissions) ){
var value = Sonia.repository.getPermissionValue( type );
for (var i=0;i<permissions.length; i++ ){
if ( permissions[i].name == state.user.name ){
result = permissions[i].type == type;
break;
var p = permissions[i];
var pv = Sonia.repository.getPermissionValue( p.type );
if ( pv >= value ){
if ( p.groupPermission ){
if ( Sonia.repository.isMember( p.name ) ){
result = true;
break;
}
} else if ( p.name == state.user.name ) {
result = true;
break;
}
}
}
}
@@ -187,7 +226,7 @@ Sonia.repository.FormPanel = Ext.extend(Sonia.rest.FormPanel,{
this.permissionStore = new Ext.data.JsonStore({
root: 'permissions',
fields: [ 'name', 'type' ],
fields: [ 'name', 'type', 'groupPermission' ],
sortInfo: {
field: 'name'
}
@@ -208,6 +247,7 @@ Sonia.repository.FormPanel = Ext.extend(Sonia.rest.FormPanel,{
id: 'type',
header: 'Type',
dataIndex: 'type',
width: 80,
editor: new Ext.form.ComboBox({
valueField: 'type',
displayField: 'type',
@@ -224,8 +264,13 @@ Sonia.repository.FormPanel = Ext.extend(Sonia.rest.FormPanel,{
]
})
})
}
]
},{
id: 'groupPermission',
header: 'Group',
dataIndex: 'groupPermission',
width: 60,
editor: new Ext.form.Checkbox()
}]
});
if ( update ){

View File

@@ -120,7 +120,22 @@ Ext.onReady(function(){
}]
});
var securitySection = null;
if ( state.user.type == 'xml' && state.user.name != 'anonymous' ){
securitySection = {
title: 'Security',
items: [{
label: 'Change Password',
fn: function(){
new Sonia.action.ChangePasswordWindow().show();
}
}]
}
}
if ( admin ){
panel.addSections([{
id: 'navConfig',
title: 'Config',
@@ -140,20 +155,31 @@ Ext.onReady(function(){
addTabPanel('plugins', 'pluginGrid', 'Plugins');
}
}]
},{
title: 'Security',
items: [{
label: 'Users',
fn: function(){
addTabPanel('users', 'userPanel', 'Users');
}
},{
label: 'Groups',
fn: function(){
addTabPanel('groups', 'groupPanel', 'Groups');
}
}]
}]);
if ( securitySection == null ){
securitySection = {
title: 'Security',
items: []
}
}
securitySection.items.push({
label: 'Users',
fn: function(){
addTabPanel('users', 'userPanel', 'Users');
}
});
securitySection.items.push({
label: 'Groups',
fn: function(){
addTabPanel('groups', 'groupPanel', 'Groups');
}
});
}
if ( securitySection != null ){
panel.addSection( securitySection );
}
if ( state.user.name == 'anonymous' ){

View File

@@ -69,10 +69,10 @@ public class ChainAuthenticationManagerTest extends AbstractTestBase
@Test
public void testAuthenticateFailed()
{
User user = manager.authenticate(request, response, trillian.getName(),
AuthenticationResult result = manager.authenticate(request, response, trillian.getName(),
"trillian");
assertNull(user);
assertNull(result);
}
/**
@@ -82,9 +82,9 @@ public class ChainAuthenticationManagerTest extends AbstractTestBase
@Test
public void testAuthenticateNotFound()
{
User user = manager.authenticate(request, response, "dent", "trillian");
AuthenticationResult result = manager.authenticate(request, response, "dent", "trillian");
assertNull(user);
assertNull(result);
}
/**
@@ -94,17 +94,17 @@ public class ChainAuthenticationManagerTest extends AbstractTestBase
@Test
public void testAuthenticateSuccess()
{
User user = manager.authenticate(request, response, trillian.getName(),
AuthenticationResult result = manager.authenticate(request, response, trillian.getName(),
"trillian123");
assertNotNull(user);
assertUserEquals(trillian, user);
assertEquals("trilliansType", user.getType());
user = manager.authenticate(request, response, perfect.getName(),
assertNotNull(result);
assertUserEquals(trillian, result.getUser());
assertEquals("trilliansType", result.getUser().getType());
result = manager.authenticate(request, response, perfect.getName(),
"perfect123");
assertNotNull(perfect);
assertUserEquals(perfect, user);
assertEquals("perfectsType", user.getType());
assertUserEquals(perfect, result.getUser());
assertEquals("perfectsType", result.getUser().getType());
}
/**