move sources from scm-web-api and scm-cli to scm-core

This commit is contained in:
Sebastian Sdorra
2010-12-05 14:00:01 +01:00
parent bc3a6305c8
commit f29cff9a13
50 changed files with 53 additions and 85 deletions

View File

@@ -0,0 +1,55 @@
/**
* 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.cli;
//~--- JDK imports ------------------------------------------------------------
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* @author Sebastian Sdorra
*/
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Argument
{
String value();
String longName() default "";
String description() default "";
boolean required() default false;
}

View File

@@ -0,0 +1,80 @@
/**
* 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.cli;
/**
*
* @author Sebastian Sdorra
*/
public class CliException extends Exception
{
/**
* Constructs ...
*
*/
public CliException() {}
/**
* Constructs ...
*
*
* @param string
*/
public CliException(String string)
{
super(string);
}
/**
* Constructs ...
*
*
* @param thrwbl
*/
public CliException(Throwable thrwbl)
{
super(thrwbl);
}
/**
* Constructs ...
*
*
* @param string
* @param thrwbl
*/
public CliException(String string, Throwable thrwbl)
{
super(string, thrwbl);
}
}

View File

@@ -0,0 +1,54 @@
/**
* 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.cli;
//~--- JDK imports ------------------------------------------------------------
import java.util.List;
/**
*
* @author Sebastian Sdorra
*/
public interface CliHelpBuilder
{
/**
* Method description
*
*
* @param arguments
*
* @return
*/
public String createHelp(List<Argument> arguments);
}

View File

@@ -0,0 +1,194 @@
/**
* 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.cli;
//~--- JDK imports ------------------------------------------------------------
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Sebastian Sdorra
*/
public class CliParser
{
/**
* Method description
*
*
*
* @param helpBuilder
* @param clazz
*
* @return
*/
public String createHelp(CliHelpBuilder helpBuilder, Class clazz)
{
Field[] fields = clazz.getDeclaredFields();
List<Argument> arguments = new ArrayList<Argument>();
for (Field field : fields)
{
Argument argument = field.getAnnotation(Argument.class);
if (argument != null)
{
arguments.add(argument);
}
}
return helpBuilder.createHelp(arguments);
}
/**
* Method description
*
*
* @param helpBuilder
* @param object
*
* @return
*/
public String createHelp(CliHelpBuilder helpBuilder, Object object)
{
return createHelp(helpBuilder, object.getClass());
}
/**
* Method description
*
*
* @param object
* @param arguments
*
* @throws CliException
*/
public void parse(Object object, String[] arguments) throws CliException
{
Field[] fields = object.getClass().getDeclaredFields();
int length = arguments.length;
for (Field field : fields)
{
Argument argument = field.getAnnotation(Argument.class);
if (argument != null)
{
String name = "-" + argument.value();
String longName = "--" + argument.longName();
boolean found = false;
for (int i = 0; i < length; i++)
{
if (arguments[i].equals(name)
|| (!longName.equals("--") && arguments[i].startsWith(longName)))
{
found = true;
if (field.getType().isAssignableFrom(Boolean.class))
{
setArgument(object, field, Boolean.TRUE);
}
else if (arguments[i].equals(name) && (i + 1 < length))
{
setArgument(object, field,
ConvertUtil.convertString(field.getType(),
arguments[i + 1]));
}
else if (arguments[i].startsWith(longName + "="))
{
String value = arguments[i].substring(longName.length() + 1);
if ((value != null) && (value.length() > 0))
{
setArgument(object, field,
ConvertUtil.convertString(field.getType(), value));
}
}
else
{
throw new CliException("missing parameter for " + name);
}
}
}
if (!found && argument.required())
{
throw new CliRequiredException(name + " is required");
}
}
}
}
//~--- set methods ----------------------------------------------------------
/**
* Method description
*
*
*
* @param object
* @param field
* @param value
*
* @throws CliException
*/
private void setArgument(Object object, Field field, Object value)
throws CliException
{
try
{
boolean modifyAccess = false;
if (!field.isAccessible())
{
field.setAccessible(true);
modifyAccess = true;
}
field.set(object, value);
if (modifyAccess)
{
field.setAccessible(false);
}
}
catch (Exception ex)
{
throw new CliException(ex);
}
}
}

View File

@@ -0,0 +1,51 @@
/**
* 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.cli;
/**
*
* @author Sebastian Sdorra
*/
public class CliRequiredException extends CliException
{
/**
* Constructs ...
*
*
* @param string
*/
public CliRequiredException(String string)
{
super(string);
}
}

View File

@@ -0,0 +1,111 @@
/**
* 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.cli;
//~--- non-JDK imports --------------------------------------------------------
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//~--- JDK imports ------------------------------------------------------------
import java.math.BigInteger;
/**
*
* @author Sebastian Sdorra
*/
public class ConvertUtil
{
/** Field description */
private static final Logger logger =
LoggerFactory.getLogger(ConvertUtil.class);
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param type
* @param value
*
* @return
*/
public static Object convertString(Class<?> type, String value)
{
Object result = null;
try
{
if (type.isAssignableFrom(String.class))
{
result = value;
}
else if (type.isAssignableFrom(Short.class))
{
result = Short.parseShort(value);
}
else if (type.isAssignableFrom(Integer.class))
{
result = Integer.parseInt(value);
}
else if (type.isAssignableFrom(Long.class))
{
result = Long.parseLong(value);
}
else if (type.isAssignableFrom(BigInteger.class))
{
result = new BigInteger(value);
}
else if (type.isAssignableFrom(Float.class))
{
result = Float.parseFloat(value);
}
else if (type.isAssignableFrom(Double.class))
{
result = Double.parseDouble(value);
}
else if (type.isAssignableFrom(Boolean.class))
{
result = Boolean.parseBoolean(value);
}
}
catch (NumberFormatException ex)
{
logger.debug(ex.getMessage(), ex);
}
return result;
}
}

View File

@@ -0,0 +1,239 @@
/**
* 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.cli;
//~--- JDK imports ------------------------------------------------------------
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
*
* @author Sebastian Sdorra
*/
public class DefaultCliHelpBuilder implements CliHelpBuilder
{
/**
* Constructs ...
*
*/
public DefaultCliHelpBuilder() {}
/**
* Constructs ...
*
*
* @param prefix
* @param suffix
*/
public DefaultCliHelpBuilder(String prefix, String suffix)
{
this.prefix = prefix;
this.suffix = suffix;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param arguments
*
* @return
*/
@Override
public String createHelp(List<Argument> arguments)
{
String s = System.getProperty("line.separator");
int paramLength = 20;
List<HelpLine> list = new ArrayList<HelpLine>();
if (arguments != null)
{
for (Argument argument : arguments)
{
HelpLine line = new HelpLine();
String name = argument.value();
String longName = argument.longName();
String description = argument.description();
StringBuilder result = new StringBuilder("-");
result.append(name);
if (longName.length() > 0)
{
result.append(",--").append(longName);
}
line.parameter = result.toString();
if (line.parameter.length() > paramLength)
{
paramLength = line.parameter.length();
}
if (description.length() > 0)
{
line.description = description;
}
list.add(line);
}
}
paramLength += 2;
Collections.sort(list);
StringBuilder result = new StringBuilder();
if ((prefix != null) && (prefix.trim().length() > 0))
{
result.append(prefix).append(s);
}
for (HelpLine line : list)
{
int length = line.parameter.length();
result.append(line.parameter);
for (int i = length; i < paramLength; i++)
{
result.append(" ");
}
result.append(line.description).append(s);
}
if ((suffix != null) && (suffix.trim().length() > 0))
{
result.append(suffix).append(s);
}
return result.toString();
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public String getPrefix()
{
return prefix;
}
/**
* Method description
*
*
* @return
*/
public String getSuffix()
{
return suffix;
}
//~--- set methods ----------------------------------------------------------
/**
* Method description
*
*
* @param prefix
*/
public void setPrefix(String prefix)
{
this.prefix = prefix;
}
/**
* Method description
*
*
* @param suffix
*/
public void setSuffix(String suffix)
{
this.suffix = suffix;
}
//~--- inner classes --------------------------------------------------------
/**
* Class description
*
*
* @version Enter version here..., 10/03/06
* @author Enter your name here...
*/
private static class HelpLine implements Comparable<HelpLine>
{
/**
* Method description
*
*
* @param o
*
* @return
*/
@Override
public int compareTo(HelpLine o)
{
return parameter.compareTo(o.parameter);
}
//~--- fields -------------------------------------------------------------
/** Field description */
private String description;
/** Field description */
private String parameter;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private String prefix;
/** Field description */
private String suffix;
}

View File

@@ -1,267 +0,0 @@
/**
* 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.repository.xml;
//~--- non-JDK imports --------------------------------------------------------
import sonia.scm.repository.Repository;
import sonia.scm.xml.XmlTimestampDateAdapter;
//~--- JDK imports ------------------------------------------------------------
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
/**
*
* @author Sebastian Sdorra
*/
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class XmlRepositoryDatabase
{
/**
* Method description
*
*
* @param repository
*/
public void add(Repository repository)
{
repositoryMap.put(createKey(repository), repository);
}
/**
* Method description
*
*
*
* @param type
* @param name
*
* @return
*/
public boolean contains(String type, String name)
{
return repositoryMap.containsKey(createKey(type, name));
}
/**
* Method description
*
*
* @param id
*
* @return
*/
public boolean contains(String id)
{
return get(id) != null;
}
/**
* Method description
*
*
* @param repository
*
* @return
*/
public boolean contains(Repository repository)
{
return repositoryMap.containsKey(createKey(repository));
}
/**
* Method description
*
*
* @param repository
*/
public void remove(Repository repository)
{
repositoryMap.remove(createKey(repository));
}
/**
* Method description
*
*
* @return
*/
public Collection<Repository> values()
{
return repositoryMap.values();
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @param type
* @param name
*
* @return
*/
public Repository get(String type, String name)
{
return repositoryMap.get(createKey(type, name));
}
/**
* Method description
*
*
* @param id
*
* @return
*/
public Repository get(String id)
{
Repository repository = null;
for (Repository r : values())
{
if (r.getId().equals(id))
{
repository = r;
break;
}
}
return repository;
}
/**
* Method description
*
*
* @return
*/
public long getCreationTime()
{
return creationTime;
}
/**
* Method description
*
*
* @return
*/
public long getLastModified()
{
return lastModified;
}
//~--- set methods ----------------------------------------------------------
/**
* Method description
*
*
* @param creationTime
*/
public void setCreationTime(long creationTime)
{
this.creationTime = creationTime;
}
/**
* Method description
*
*
* @param lastModified
*/
public void setLastModified(long lastModified)
{
this.lastModified = lastModified;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param type
* @param name
*
* @return
*/
static String createKey(String type, String name)
{
return type.concat(":").concat(name);
}
/**
* Method description
*
*
* @param repository
*
* @return
*/
static String createKey(Repository repository)
{
return createKey(repository.getType(), repository.getName());
}
//~--- fields ---------------------------------------------------------------
/** Field description */
@XmlJavaTypeAdapter(XmlTimestampDateAdapter.class)
private Long creationTime;
/** Field description */
@XmlJavaTypeAdapter(XmlTimestampDateAdapter.class)
private Long lastModified;
/** Field description */
@XmlJavaTypeAdapter(XmlRepositoryMapAdapter.class)
@XmlElement(name = "repositories")
private Map<String, Repository> repositoryMap = new LinkedHashMap<String,
Repository>();
}

View File

@@ -1,595 +0,0 @@
/**
* 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.repository.xml;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.ConfigurationException;
import sonia.scm.HandlerEvent;
import sonia.scm.SCMContext;
import sonia.scm.SCMContextProvider;
import sonia.scm.Type;
import sonia.scm.repository.AbstractRepositoryManager;
import sonia.scm.repository.PermissionType;
import sonia.scm.repository.PermissionUtil;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryAllreadyExistExeption;
import sonia.scm.repository.RepositoryException;
import sonia.scm.repository.RepositoryHandler;
import sonia.scm.repository.RepositoryHandlerNotFoundException;
import sonia.scm.security.SecurityContext;
import sonia.scm.user.User;
import sonia.scm.util.AssertUtil;
import sonia.scm.util.IOUtil;
//~--- JDK imports ------------------------------------------------------------
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.xml.bind.JAXB;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class XmlRepositoryManager extends AbstractRepositoryManager
{
/** Field description */
public static final String DATABASEFILE =
"config".concat(File.separator).concat("repositories.xml");
/** Field description */
private static final Logger logger =
LoggerFactory.getLogger(XmlRepositoryManager.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
*
* @param securityContextProvider
* @param handlerSet
*/
@Inject
public XmlRepositoryManager(
Provider<SecurityContext> securityContextProvider,
Set<RepositoryHandler> handlerSet)
{
this.securityContextProvider = securityContextProvider;
handlerMap = new HashMap<String, RepositoryHandler>();
types = new HashSet<Type>();
for (RepositoryHandler handler : handlerSet)
{
addHandler(handler);
}
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @throws IOException
*/
@Override
public void close() throws IOException
{
for (RepositoryHandler handler : handlerMap.values())
{
IOUtil.close(handler);
}
}
/**
* Method description
*
*
* @param repository
*
* @throws IOException
* @throws RepositoryException
*/
@Override
public void create(Repository repository)
throws RepositoryException, IOException
{
if (logger.isInfoEnabled())
{
logger.info("create repository {} of type {}", repository.getName(),
repository.getType());
}
assertIsAdmin();
AssertUtil.assertIsValid(repository);
if (repositoryDB.contains(repository))
{
throw new RepositoryAllreadyExistExeption();
}
repository.setId(UUID.randomUUID().toString());
repository.setCreationDate(System.currentTimeMillis());
getHandler(repository).create(repository);
synchronized (XmlRepositoryDatabase.class)
{
repositoryDB.add(repository.clone());
storeDB();
}
fireEvent(repository, HandlerEvent.CREATE);
}
/**
* Method description
*
*
* @param repository
*
* @throws IOException
* @throws RepositoryException
*/
@Override
public void delete(Repository repository)
throws RepositoryException, IOException
{
if (logger.isInfoEnabled())
{
logger.info("delete repository {} of type {}", repository.getName(),
repository.getType());
}
assertIsOwner(repository);
if (repositoryDB.contains(repository))
{
getHandler(repository).delete(repository);
synchronized (XmlRepositoryDatabase.class)
{
repositoryDB.remove(repository);
storeDB();
}
}
else
{
throw new RepositoryException(
"repository ".concat(repository.getName()).concat(" not found"));
}
fireEvent(repository, HandlerEvent.DELETE);
}
/**
* Method description
*
*
* @param context
*/
@Override
public void init(SCMContextProvider context)
{
File directory = context.getBaseDirectory();
repositoryDBFile = new File(directory, DATABASEFILE);
if (repositoryDBFile.exists())
{
loadDB();
}
else
{
IOUtil.mkdirs(repositoryDBFile.getParentFile());
repositoryDB = new XmlRepositoryDatabase();
repositoryDB.setCreationTime(System.currentTimeMillis());
}
}
/**
* Method description
*
*
* @param repository
*
* @throws IOException
* @throws RepositoryException
*/
@Override
public void modify(Repository repository)
throws RepositoryException, IOException
{
if (logger.isInfoEnabled())
{
logger.info("modify repository {} of type {}", repository.getName(),
repository.getType());
}
AssertUtil.assertIsValid(repository);
Repository notModifiedRepository = repositoryDB.get(repository.getType(),
repository.getName());
if (notModifiedRepository != null)
{
assertIsOwner(notModifiedRepository);
getHandler(repository).modify(repository);
repository.setLastModified(System.currentTimeMillis());
synchronized (XmlRepositoryDatabase.class)
{
repositoryDB.remove(repository);
repositoryDB.add(repository.clone());
storeDB();
}
}
else
{
throw new RepositoryException(
"repository ".concat(repository.getName()).concat(" not found"));
}
fireEvent(repository, HandlerEvent.MODIFY);
}
/**
* Method description
*
*
* @param repository
*
* @throws IOException
* @throws RepositoryException
*/
@Override
public void refresh(Repository repository)
throws RepositoryException, IOException
{
AssertUtil.assertIsNotNull(repository);
assertIsReader(repository);
Repository fresh = repositoryDB.get(repository.getType(),
repository.getName());
if (fresh != null)
{
fresh.copyProperties(repository);
}
else
{
throw new RepositoryException(
"repository ".concat(repository.getName()).concat(" not found"));
}
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @param id
*
* @return
*/
@Override
public Repository get(String id)
{
AssertUtil.assertIsNotEmpty(id);
Repository repository = repositoryDB.get(id);
if (repository != null)
{
assertIsReader(repository);
repository = repository.clone();
}
return repository;
}
/**
* Method description
*
*
* @param type
* @param name
*
* @return
*/
@Override
public Repository get(String type, String name)
{
AssertUtil.assertIsNotEmpty(type);
AssertUtil.assertIsNotEmpty(name);
Repository repository = repositoryDB.get(type, name);
if (repository != null)
{
if (isReader(repository))
{
repository = repository.clone();
}
else
{
repository = null;
}
}
return repository;
}
/**
* Method description
*
*
* @return
*/
@Override
public Collection<Repository> getAll()
{
LinkedList<Repository> repositories = new LinkedList<Repository>();
for (Repository repository : repositoryDB.values())
{
if (isReader(repository))
{
repositories.add(repository.clone());
}
}
return repositories;
}
/**
* Method description
*
*
* @param type
*
* @return
*/
@Override
public RepositoryHandler getHandler(String type)
{
return handlerMap.get(type);
}
/**
* Method description
*
*
* @return
*/
@Override
public Collection<Type> getTypes()
{
return types;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param handler
*/
private void addHandler(RepositoryHandler handler)
{
AssertUtil.assertIsNotNull(handler);
Type type = handler.getType();
AssertUtil.assertIsNotNull(type);
if (handlerMap.containsKey(type.getName()))
{
throw new ConfigurationException(
type.getName().concat("allready registered"));
}
if (logger.isInfoEnabled())
{
logger.info("added RepositoryHandler {} for type {}", handler.getClass(),
type);
}
handlerMap.put(type.getName(), handler);
handler.init(SCMContext.getContext());
types.add(type);
}
/**
* Method description
*
*
* @throws RepositoryException
*/
private void assertIsAdmin() throws RepositoryException
{
if (!getCurrentUser().isAdmin())
{
throw new RepositoryException("admin permsission required");
}
}
/**
* Method description
*
*
* @param repository
*/
private void assertIsOwner(Repository repository)
{
PermissionUtil.assertPermission(repository, getCurrentUser(),
PermissionType.OWNER);
}
/**
* Method description
*
*
* @param repository
*/
private void assertIsReader(Repository repository)
{
PermissionUtil.assertPermission(repository, getCurrentUser(),
PermissionType.READ);
}
/**
* Method description
*
*/
private void loadDB()
{
repositoryDB = JAXB.unmarshal(repositoryDBFile,
XmlRepositoryDatabase.class);
}
/**
* Method description
*
*/
private void storeDB()
{
repositoryDB.setLastModified(System.currentTimeMillis());
JAXB.marshal(repositoryDB, repositoryDBFile);
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
private User getCurrentUser()
{
SecurityContext context = securityContextProvider.get();
AssertUtil.assertIsNotNull(context);
User user = context.getUser();
AssertUtil.assertIsNotNull(user);
return user;
}
/**
* Method description
*
*
* @param repository
*
* @return
*
*
* @throws RepositoryException
*/
private RepositoryHandler getHandler(Repository repository)
throws RepositoryException
{
String type = repository.getType();
RepositoryHandler handler = handlerMap.get(type);
if (handler == null)
{
throw new RepositoryHandlerNotFoundException(
"could not find handler for ".concat(type));
}
else if (!handler.isConfigured())
{
throw new RepositoryException("handler is not configured");
}
return handler;
}
/**
* Method description
*
*
* @param repository
*
* @return
*/
private boolean isReader(Repository repository)
{
return PermissionUtil.hasPermission(repository, getCurrentUser(),
PermissionType.READ);
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private Map<String, RepositoryHandler> handlerMap;
/** Field description */
private XmlRepositoryDatabase repositoryDB;
/** Field description */
private File repositoryDBFile;
/** Field description */
private Provider<SecurityContext> securityContextProvider;
/** Field description */
private Set<Type> types;
}

View File

@@ -1,388 +0,0 @@
/**
* 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.user.xml;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.SCMContextProvider;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.security.SecurityContext;
import sonia.scm.user.AbstractUserManager;
import sonia.scm.user.User;
import sonia.scm.user.UserAllreadyExistException;
import sonia.scm.user.UserException;
import sonia.scm.util.IOUtil;
import sonia.scm.util.SecurityUtil;
import sonia.scm.util.Util;
//~--- JDK imports ------------------------------------------------------------
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.LinkedList;
import javax.xml.bind.JAXB;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class XmlUserManager extends AbstractUserManager
{
/** Field description */
public static final String ADMIN_PATH = "/sonia/scm/config/admin-account.xml";
/** Field description */
public static final String DATABASEFILE =
"config".concat(File.separator).concat("users.xml");
/** Field description */
public static final String TYPE = "xml";
/** the logger for XmlUserManager */
private static final Logger logger =
LoggerFactory.getLogger(XmlUserManager.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param scurityContextProvider
*/
@Inject
public XmlUserManager(Provider<SecurityContext> scurityContextProvider)
{
this.scurityContextProvider = scurityContextProvider;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @throws IOException
*/
@Override
public void close() throws IOException
{
// do nothing
}
/**
* Method description
*
*
* @param username
*
* @return
*/
@Override
public boolean contains(String username)
{
return userDB.contains(username);
}
/**
* Method description
*
*
* @param user
*
* @throws IOException
* @throws UserException
*/
@Override
public void create(User user) throws UserException, IOException
{
User currentUser = SecurityUtil.getCurrentUser(scurityContextProvider);
if (!user.equals(currentUser) &&!currentUser.isAdmin())
{
throw new ScmSecurityException("admin account is required");
}
if (userDB.contains(user.getName()))
{
throw new UserAllreadyExistException();
}
String type = user.getType();
if (Util.isEmpty(type))
{
user.setType(TYPE);
}
synchronized (XmlUserManager.class)
{
userDB.add(user.clone());
storeDB();
}
}
/**
* Method description
*
*
* @param user
*
* @throws IOException
* @throws UserException
*/
@Override
public void delete(User user) throws UserException, IOException
{
SecurityUtil.assertIsAdmin(scurityContextProvider);
String name = user.getName();
if (userDB.contains(name))
{
synchronized (XmlUserManager.class)
{
userDB.remove(name);
storeDB();
}
}
else
{
throw new UserException("user does not exists");
}
}
/**
* Method description
*
*
* @param context
*/
@Override
public void init(SCMContextProvider context)
{
File directory = context.getBaseDirectory();
userDBFile = new File(directory, DATABASEFILE);
if (userDBFile.exists())
{
loadDB();
}
else
{
IOUtil.mkdirs(userDBFile.getParentFile());
userDB = new XmlUserDatabase();
userDB.setCreationTime(System.currentTimeMillis());
createAdminAccount();
}
}
/**
* Method description
*
*
* @param user
*
* @throws IOException
* @throws UserException
*/
@Override
public void modify(User user) throws UserException, IOException
{
User currentUser = SecurityUtil.getCurrentUser(scurityContextProvider);
if (!user.equals(currentUser) &&!currentUser.isAdmin())
{
throw new ScmSecurityException("admin account is required");
}
String name = user.getName();
if (userDB.contains(name))
{
synchronized (XmlUserManager.class)
{
userDB.remove(name);
userDB.add(user.clone());
storeDB();
}
}
else
{
throw new UserException("user does not exists");
}
}
/**
* Method description
*
*
* @param user
*
* @throws IOException
* @throws UserException
*/
@Override
public void refresh(User user) throws UserException, IOException
{
SecurityUtil.assertIsAdmin(scurityContextProvider);
User fresh = userDB.get(user.getName());
if (fresh == null)
{
throw new UserException("user does not exists");
}
fresh.copyProperties(user);
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @param id
*
* @return
*/
@Override
public User get(String id)
{
// SecurityUtil.assertIsAdmin(scurityContextProvider);
User user = userDB.get(id);
if (user != null)
{
user = user.clone();
}
return user;
}
/**
* Method description
*
*
* @return
*/
@Override
public Collection<User> getAll()
{
SecurityUtil.assertIsAdmin(scurityContextProvider);
LinkedList<User> users = new LinkedList<User>();
for (User user : userDB.values())
{
users.add(user.clone());
}
return users;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*/
private void createAdminAccount()
{
InputStream input = XmlUserManager.class.getResourceAsStream(ADMIN_PATH);
try
{
User user = JAXB.unmarshal(input, User.class);
userDB.add(user);
storeDB();
}
catch (Exception ex)
{
logger.error("could not create AdminAccount", ex);
}
finally
{
IOUtil.close(input);
}
}
/**
* Method description
*
*/
private void loadDB()
{
userDB = JAXB.unmarshal(userDBFile, XmlUserDatabase.class);
}
/**
* Method description
*
*/
private void storeDB()
{
userDB.setLastModified(System.currentTimeMillis());
JAXB.marshal(userDB, userDBFile);
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private Provider<SecurityContext> scurityContextProvider;
/** Field description */
private XmlUserDatabase userDB;
/** Field description */
private File userDBFile;
}

View File

@@ -0,0 +1,194 @@
/**
* 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.cgi;
//~--- JDK imports ------------------------------------------------------------
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
public abstract class AbstractCGIServlet extends HttpServlet
{
/** Field description */
private static final long serialVersionUID = -8638099037069714140L;
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @param req
*
* @return
*
* @throws IOException
* @throws ServletException
*/
protected abstract File getCommand(HttpServletRequest req)
throws ServletException, IOException;
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @throws ServletException
*/
@Override
public void init() throws ServletException
{
cgiRunner = new CGIRunner(getServletContext(), getCmdPrefix(),
isExitStateIgnored());
baseEnvironment = createBaseEnvironment();
}
/**
* Method description
*
*
* @return
*
* @throws ServletException
*/
protected EnvList createBaseEnvironment() throws ServletException
{
EnvList env = new EnvList();
Enumeration e = getInitParameterNames();
while (e.hasMoreElements())
{
String n = (String) e.nextElement();
if ((n != null) && n.startsWith("ENV_"))
{
env.set(n.substring(4), getInitParameter(n));
}
}
if (!env.containsKey("SystemRoot"))
{
String os = System.getProperty("os.name");
if ((os != null) && (os.toLowerCase().indexOf("windows") != -1))
{
env.set("SystemRoot", "C:\\WINDOWS");
}
}
return env;
}
/**
* Method description
*
*
*
* @param request
* @param baseEnvironment
*
* @return
*
* @throws ServletException
*/
protected EnvList createRequestEnvironment(HttpServletRequest request,
EnvList baseEnvironment)
throws ServletException
{
return new EnvList(baseEnvironment);
}
/**
* Method description
*
*
* @param req
* @param resp
*
* @throws IOException
* @throws ServletException
*/
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
cgiRunner.exec(createRequestEnvironment(req, baseEnvironment),
getCommand(req), req.getPathInfo(), req, resp);
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
protected String getCmdPrefix()
{
return null;
}
/**
* Method description
*
*
* @return
*/
protected boolean isExitStateIgnored()
{
return false;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private EnvList baseEnvironment;
/** Field description */
private CGIRunner cgiRunner;
}

View File

@@ -0,0 +1,485 @@
/**
* 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.cgi;
//~--- non-JDK imports --------------------------------------------------------
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.util.IOUtil;
import sonia.scm.util.Util;
//~--- JDK imports ------------------------------------------------------------
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Enumeration;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Based on org.eclipse.jetty.servlets.CGI
*
* @author Sebastian Sdorra
*
*/
public class CGIRunner
{
/** Field description */
public static final int BUFFERSIZE = 2 * 8192;
/** Field description */
private static final Logger logger = LoggerFactory.getLogger(CGIRunner.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param context
* @param cmdPrefix
* @param ignoreExitState
*/
public CGIRunner(ServletContext context, String cmdPrefix,
boolean ignoreExitState)
{
this.context = context;
this.cmdPrefix = cmdPrefix;
this.ignoreExitState = ignoreExitState;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
*
* @param environment
* @param command
* @param pathInfo
* @param req
* @param res
*
* @throws IOException
*/
public void exec(EnvList environment, File command, String pathInfo,
HttpServletRequest req, HttpServletResponse res)
throws IOException
{
String path = command.getAbsolutePath();
File dir = command.getParentFile();
String scriptName = req.getRequestURI().substring(0,
req.getRequestURI().length() - pathInfo.length());
String scriptPath = context.getRealPath(scriptName);
String pathTranslated = req.getPathTranslated();
int len = req.getContentLength();
if (len < 0)
{
len = 0;
}
if (Util.isEmpty(pathTranslated))
{
pathTranslated = path;
}
// these ones are from "The WWW Common Gateway Interface Version 1.1"
// look at :
// http://Web.Golux.Com/coar/cgi/draft-coar-cgi-v11-03-clean.html#6.1.1
environment.set("AUTH_TYPE", req.getAuthType());
environment.set("CONTENT_LENGTH", Integer.toString(len));
environment.set("CONTENT_TYPE", req.getContentType());
environment.set("GATEWAY_INTERFACE", "CGI/1.1");
environment.set("PATH_INFO", pathInfo);
environment.set("PATH_TRANSLATED", pathTranslated);
environment.set("QUERY_STRING", req.getQueryString());
environment.set("REMOTE_ADDR", req.getRemoteAddr());
environment.set("REMOTE_HOST", req.getRemoteHost());
// The identity information reported about the connection by a
// RFC 1413 [11] request to the remote agent, if
// available. Servers MAY choose not to support this feature, or
// not to request the data for efficiency reasons.
// "REMOTE_IDENT" => "NYI"
environment.set("REMOTE_USER", req.getRemoteUser());
environment.set("REQUEST_METHOD", req.getMethod());
environment.set("SCRIPT_NAME", scriptName);
environment.set("SCRIPT_FILENAME", scriptPath);
environment.set("SERVER_NAME", req.getServerName());
environment.set("SERVER_PORT", Integer.toString(req.getServerPort()));
environment.set("SERVER_PROTOCOL", req.getProtocol());
environment.set("SERVER_SOFTWARE", context.getServerInfo());
Enumeration enm = req.getHeaderNames();
while (enm.hasMoreElements())
{
String name = (String) enm.nextElement();
String value = req.getHeader(name);
environment.set("HTTP_" + name.toUpperCase().replace('-', '_'), value);
}
// these extra ones were from printenv on www.dev.nomura.co.uk
environment.set("HTTPS", (req.isSecure()
? "ON"
: "OFF"));
// "DOCUMENT_ROOT" => root + "/docs",
// "SERVER_URL" => "NYI - http://us0245",
// "TZ" => System.getProperty("user.timezone"),
// are we meant to decode args here ? or does the script get them
// via PATH_INFO ? if we are, they should be decoded and passed
// into exec here...
String execCmd = path;
if ((execCmd.charAt(0) != '"') && (execCmd.indexOf(" ") >= 0))
{
execCmd = "\"" + execCmd + "\"";
}
if (cmdPrefix != null)
{
execCmd = cmdPrefix + " " + execCmd;
}
if (logger.isDebugEnabled())
{
logger.debug("execute cgi: ".concat(execCmd));
}
Process p = (dir == null)
? Runtime.getRuntime().exec(execCmd, environment.getEnvArray())
: Runtime.getRuntime().exec(execCmd, environment.getEnvArray(),
dir);
// hook processes input to browser's output (async)
final InputStream inFromReq = req.getInputStream();
final OutputStream outToCgi = p.getOutputStream();
final int inLength = len;
// TODO: print or log error stream
IOUtil.copyThread(new InputStreamReader(p.getErrorStream()),
new OutputStreamWriter(System.err));
new Thread(new Runnable()
{
@Override
public void run()
{
try
{
if (inLength > 0)
{
copy(inFromReq, outToCgi, inLength);
}
outToCgi.close();
}
catch (IOException ex)
{
logger.debug(ex.getMessage(), ex);
}
finally
{
IOUtil.close(inFromReq);
IOUtil.close(outToCgi);
}
}
}).start();
// hook processes output to browser's input (sync)
// if browser closes stream, we should detect it and kill process...
OutputStream os = null;
try
{
// read any headers off the top of our input stream
// NOTE: Multiline header items not supported!
String line = null;
InputStream inFromCgi = p.getInputStream();
// br=new BufferedReader(new InputStreamReader(inFromCgi));
// while ((line=br.readLine())!=null)
while ((line = getTextLineFromStream(inFromCgi)).length() > 0)
{
if (!line.startsWith("HTTP"))
{
int k = line.indexOf(':');
if (k > 0)
{
String key = line.substring(0, k).trim();
String value = line.substring(k + 1).trim();
if ("Location".equals(key))
{
res.sendRedirect(res.encodeRedirectURL(value));
}
else if ("Status".equals(key))
{
String[] token = value.split(" ");
int status = Integer.parseInt(token[0]);
res.setStatus(status);
}
else
{
// add remaining header items to our response header
res.addHeader(key, value);
}
}
}
}
// copy cgi content to response stream...
os = res.getOutputStream();
IOUtil.copy(inFromCgi, os);
p.waitFor();
if (!ignoreExitState)
{
int exitValue = p.exitValue();
if (0 != exitValue)
{
StringBuilder msg = new StringBuilder("Non-zero exit status (");
msg.append(exitValue).append(") from CGI program: ").append(path);
logger.warn(msg.toString());
if (!res.isCommitted())
{
res.sendError(500, "Failed to exec CGI");
}
}
}
}
catch (IOException e)
{
// browser has probably closed its input stream - we
// terminate and clean up...
logger.debug("CGI: Client closed connection!");
}
catch (InterruptedException ie)
{
logger.debug("CGI: interrupted!");
}
finally
{
if (os != null)
{
IOUtil.close(os);
}
os = null;
p.destroy();
// Log.debug("CGI: terminated!");
}
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public String getCmdPrefix()
{
return cmdPrefix;
}
/**
* Method description
*
*
* @return
*/
public ServletContext getContext()
{
return context;
}
/**
* Method description
*
*
* @return
*/
public boolean isIgnoreExitState()
{
return ignoreExitState;
}
//~--- set methods ----------------------------------------------------------
/**
* Method description
*
*
* @param cmdPrefix
*/
public void setCmdPrefix(String cmdPrefix)
{
this.cmdPrefix = cmdPrefix;
}
/**
* Method description
*
*
* @param context
*/
public void setContext(ServletContext context)
{
this.context = context;
}
/**
* Method description
*
*
* @param ignoreExitState
*/
public void setIgnoreExitState(boolean ignoreExitState)
{
this.ignoreExitState = ignoreExitState;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param in
* @param out
* @param byteCount
*
* @throws IOException
*/
private void copy(InputStream in, OutputStream out, long byteCount)
throws IOException
{
byte buffer[] = new byte[BUFFERSIZE];
int len = BUFFERSIZE;
if (byteCount >= 0)
{
while (byteCount > 0)
{
int max = (byteCount < BUFFERSIZE)
? (int) byteCount
: BUFFERSIZE;
len = in.read(buffer, 0, max);
if (len == -1)
{
break;
}
byteCount -= len;
out.write(buffer, 0, len);
}
}
else
{
while (true)
{
len = in.read(buffer, 0, BUFFERSIZE);
if (len < 0)
{
break;
}
out.write(buffer, 0, len);
}
}
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @param is
*
* @return
*
* @throws IOException
*/
private String getTextLineFromStream(InputStream is) throws IOException
{
StringBuilder buffer = new StringBuilder();
int b;
while ((b = is.read()) != -1 && (b != (int) '\n'))
{
buffer.append((char) b);
}
return buffer.toString().trim();
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private String cmdPrefix;
/** Field description */
private ServletContext context;
/** Field description */
private boolean ignoreExitState;
}

View File

@@ -29,54 +29,59 @@
*
*/
package sonia.scm.user.xml;
package sonia.scm.web.cgi;
//~--- non-JDK imports --------------------------------------------------------
import sonia.scm.user.User;
import sonia.scm.util.Util;
//~--- JDK imports ------------------------------------------------------------
import java.util.Iterator;
import java.util.LinkedList;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
*
* @author Sebastian Sdorra
*/
@XmlRootElement(name = "users")
@XmlAccessorType(XmlAccessType.FIELD)
public class XmlUserList implements Iterable<User>
public class EnvList
{
/**
* Constructs ...
* Constructs ...
*
*/
public XmlUserList() {}
public EnvList()
{
envMap = new HashMap<String, String>();
}
/**
* Constructs ...
*
*
*
* @param userMap
* @param l
*/
public XmlUserList(Map<String, User> userMap)
public EnvList(EnvList l)
{
this.users = new LinkedList<User>(userMap.values());
envMap = new HashMap<String, String>(l.envMap);
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param key
*
* @return
*/
public boolean containsKey(String key)
{
return envMap.containsKey(key);
}
/**
* Method description
*
@@ -84,40 +89,38 @@ public class XmlUserList implements Iterable<User>
* @return
*/
@Override
public Iterator<User> iterator()
public String toString()
{
return users.iterator();
return envMap.toString();
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
* Get representation suitable for passing to exec.
*
* @return
*/
public LinkedList<User> getUsers()
public String[] getEnvArray()
{
return users;
return envMap.values().toArray(new String[envMap.size()]);
}
//~--- set methods ----------------------------------------------------------
/**
* Method description
* Set a name/value pair, null values will be treated as an empty String
*
*
* @param users
* @param name
* @param value
*/
public void setUsers(LinkedList<User> users)
public void set(String name, String value)
{
this.users = users;
envMap.put(name, name.concat("=").concat(Util.nonNull(value)));
}
//~--- fields ---------------------------------------------------------------
/** Field description */
@XmlElement(name = "user")
private LinkedList<User> users;
private Map<String, String> envMap;
}

View File

@@ -0,0 +1,183 @@
/**
* 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.filter;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import sonia.scm.user.User;
import sonia.scm.util.Util;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
import com.sun.jersey.core.util.Base64;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class BasicAuthenticationFilter extends HttpFilter
{
/** Field description */
public static final String AUTHENTICATION_REALM = "SONIA :: SCM Manager";
/** Field description */
public static final String AUTHORIZATION_BASIC_PREFIX = "BASIC";
/** Field description */
public static final String CREDENTIAL_SEPARATOR = ":";
/** Field description */
public static final String HEADERVALUE_CONNECTION_CLOSE = "close";
/** Field description */
public static final String HEADER_AUTHORIZATION = "Authorization";
/** Field description */
public static final String HEADER_CONNECTION = "connection";
/** Field description */
public static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate";
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param securityContextProvider
*/
@Inject
public BasicAuthenticationFilter(
Provider<WebSecurityContext> securityContextProvider)
{
this.securityContextProvider = securityContextProvider;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param response
* @param chain
*
* @throws IOException
* @throws ServletException
*/
@Override
protected void doFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
{
WebSecurityContext securityContext = securityContextProvider.get();
User user = null;
if (securityContext != null)
{
if (!securityContext.isAuthenticated())
{
String authentication = request.getHeader(HEADER_AUTHORIZATION);
if (Util.isEmpty(authentication))
{
sendUnauthorized(response);
}
else
{
if (!authentication.toUpperCase().startsWith(
AUTHORIZATION_BASIC_PREFIX))
{
throw new ServletException("wrong basic header");
}
String token = authentication.substring(6);
token = new String(Base64.decode(token.getBytes()));
String[] credentials = token.split(CREDENTIAL_SEPARATOR);
user = securityContext.authenticate(request, response,
credentials[0], credentials[1]);
}
}
else
{
user = securityContext.getUser();
}
}
if (user != null)
{
chain.doFilter(new SecurityHttpServletRequestWrapper(request, user),
response);
}
else
{
sendUnauthorized(response);
}
}
/**
* Method description
*
*
* @param response
*/
private void sendUnauthorized(HttpServletResponse response)
{
response.setHeader(HEADER_WWW_AUTHENTICATE,
"Basic realm=\"" + AUTHENTICATION_REALM + "\"");
response.setHeader(HEADER_CONNECTION, HEADERVALUE_CONNECTION_CLOSE);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
}

View File

@@ -0,0 +1,123 @@
/**
* 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.filter;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
public abstract class HttpFilter implements Filter
{
/**
* Method description
*
*
* @param request
* @param response
* @param chain
*
* @throws IOException
* @throws ServletException
*/
protected abstract void doFilter(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain)
throws IOException, ServletException;
/**
* Method description
*
*/
@Override
public void destroy()
{
// do nothing
}
/**
* Method description
*
*
* @param request
* @param response
* @param chain
*
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException
{
if ((request instanceof HttpServletRequest)
&& (response instanceof HttpServletResponse))
{
doFilter((HttpServletRequest) request, (HttpServletResponse) response,
chain);
}
else
{
throw new IllegalArgumentException("request is not an http request");
}
}
/**
* Method description
*
*
* @param filterConfig
*
* @throws ServletException
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
// do nothing
}
}

View File

@@ -0,0 +1,521 @@
/**
* 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.filter;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.util.Util;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class LoggingFilter extends HttpFilter
{
/** Field description */
private static final Logger logger =
LoggerFactory.getLogger(LoggingFilter.class);
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param response
* @param chain
*
* @throws IOException
* @throws ServletException
*/
@Override
protected void doFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
{
if (logger.isDebugEnabled())
{
LoggingHttpServletRequest loggingRequest =
new LoggingHttpServletRequest(request);
LoggingHttpServletResponse loggingResponse =
new LoggingHttpServletResponse(response);
logRequest(loggingRequest);
chain.doFilter(request, response);
logResponse(loggingResponse);
}
else
{
chain.doFilter(request, response);
}
}
/**
* Method description
*
*
* @param request
*/
private void logRequest(LoggingHttpServletRequest request)
{
logger.debug("**************** request ****************");
logger.debug("Info: Request-Uri = {}", request.getRequestURI());
logger.debug("Info: Remote-Addr = {}", request.getRemoteAddr());
logger.debug("Info: Remote-User = {}",
Util.nonNull(request.getRemoteUser()));
logger.debug("Info: Content-Size = {}",
Integer.toString(request.getContentLength()));
logger.debug("Info: Content-Type = {}",
Util.nonNull(request.getContentType()));
logger.debug("Info: Method = {}", request.getMethod());
logger.debug("Info: AuthType = {}", Util.nonNull(request.getAuthType()));
Enumeration headers = request.getHeaderNames();
while (headers.hasMoreElements())
{
String header = (String) headers.nextElement();
logger.debug("Header: {} = {}", header, request.getHeader(header));
}
Cookie[] cookies = request.getCookies();
if (cookies != null)
{
for (Cookie cookie : cookies)
{
logger.debug("Cookie: {} = {}", cookie.getName(), cookie.getValue());
}
}
Enumeration parameters = request.getParameterNames();
if (parameters != null)
{
while (parameters.hasMoreElements())
{
String parameter = (String) parameters.nextElement();
logger.debug("Parameter: {} = {}", parameter,
request.getParameter(parameter));
}
}
Enumeration attributes = request.getAttributeNames();
if (attributes != null)
{
while (attributes.hasMoreElements())
{
String attribute = (String) attributes.nextElement();
logger.debug("Attribute: {} = {}", attribute,
request.getAttribute(attribute).toString());
}
}
HttpSession session = request.getSession(true);
logger.debug("Session-New: {}", Boolean.toString(session.isNew()));
Enumeration sAttributes = session.getAttributeNames();
if (sAttributes != null)
{
while (sAttributes.hasMoreElements())
{
String sAttribute = (String) sAttributes.nextElement();
logger.debug("Session-Attribute: {} = {}", sAttribute,
request.getSession().getAttribute(sAttribute).toString());
}
}
}
/**
* Method description
*
*
* @param response
*/
private void logResponse(LoggingHttpServletResponse response)
{
logger.debug("**************** response ****************");
logger.debug("status code = {}",
Integer.toString(response.getStatusCode()));
logger.debug("status message = {}",
Util.nonNull(response.getStatusMessage()));
logger.debug("charset = {}", Util.nonNull(response.getCharacterEncoding()));
logger.debug("content-type = {}", Util.nonNull(response.getContentType()));
logger.debug("content-length = {}",
Integer.toString(response.getContentLength()));
for (Cookie cookie : response.getCookies())
{
logger.debug("Cookie: {} = {}", cookie.getName(), cookie.getValue());
}
for (Entry<String, String> header : response.getHeaders().entrySet())
{
logger.debug("Header: {} = {}", header.getKey(), header.getValue());
}
}
//~--- inner classes --------------------------------------------------------
/**
* Class description
*
*
* @version Enter version here..., 10/09/29
* @author Enter your name here...
*/
private static class LoggingHttpServletRequest
extends HttpServletRequestWrapper
{
/**
* Constructs ...
*
*
* @param request
*/
public LoggingHttpServletRequest(HttpServletRequest request)
{
super(request);
}
}
/**
* Class description
*
*
* @version Enter version here..., 10/09/29
* @author Enter your name here...
*/
private static class LoggingHttpServletResponse
extends HttpServletResponseWrapper
{
/**
* Constructs ...
*
*
* @param response
*/
public LoggingHttpServletResponse(HttpServletResponse response)
{
super(response);
}
//~--- methods ------------------------------------------------------------
/**
* Method description
*
*
* @param cookie
*/
@Override
public void addCookie(Cookie cookie)
{
cookies.add(cookie);
super.addCookie(cookie);
}
/**
* Method description
*
*
* @param name
* @param date
*/
@Override
public void addDateHeader(String name, long date)
{
headers.put(name, new Date(date).toString());
super.addDateHeader(name, date);
}
/**
* Method description
*
*
* @param name
* @param value
*/
@Override
public void addHeader(String name, String value)
{
headers.put(name, value);
super.addHeader(name, value);
}
/**
* Method description
*
*
* @param name
* @param value
*/
@Override
public void addIntHeader(String name, int value)
{
headers.put(name, Integer.toString(value));
super.addIntHeader(name, value);
}
/**
* Method description
*
*
* @param sc
*
* @throws IOException
*/
@Override
public void sendError(int sc) throws IOException
{
this.statusCode = sc;
super.sendError(sc);
}
/**
* Method description
*
*
* @param sc
* @param msg
*
* @throws IOException
*/
@Override
public void sendError(int sc, String msg) throws IOException
{
this.statusCode = sc;
this.statusMessage = msg;
super.sendError(sc, msg);
}
//~--- get methods --------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public int getContentLength()
{
return contentLength;
}
/**
* Method description
*
*
* @return
*/
public Set<Cookie> getCookies()
{
return cookies;
}
/**
* Method description
*
*
* @return
*/
public Map<String, String> getHeaders()
{
return headers;
}
/**
* Method description
*
*
* @return
*/
public int getStatusCode()
{
return statusCode;
}
/**
* Method description
*
*
* @return
*/
public String getStatusMessage()
{
return statusMessage;
}
//~--- set methods --------------------------------------------------------
/**
* Method description
*
*
* @param len
*/
@Override
public void setContentLength(int len)
{
this.contentLength = len;
super.setContentLength(len);
}
/**
* Method description
*
*
* @param name
* @param date
*/
@Override
public void setDateHeader(String name, long date)
{
headers.put(name, new Date(date).toString());
super.setDateHeader(name, date);
}
/**
* Method description
*
*
* @param name
* @param value
*/
@Override
public void setHeader(String name, String value)
{
headers.put(name, value);
super.setHeader(name, value);
}
/**
* Method description
*
*
* @param name
* @param value
*/
@Override
public void setIntHeader(String name, int value)
{
headers.put(name, Integer.toString(value));
super.setIntHeader(name, value);
}
/**
* Method description
*
*
* @param sc
*/
@Override
public void setStatus(int sc)
{
this.statusCode = sc;
super.setStatus(sc);
}
/**
* Method description
*
*
* @param sc
* @param sm
*/
@Override
public void setStatus(int sc, String sm)
{
this.statusCode = sc;
this.statusMessage = sm;
super.setStatus(sc, sm);
}
//~--- fields -------------------------------------------------------------
/** Field description */
private int contentLength = -1;
/** Field description */
private Set<Cookie> cookies = new HashSet<Cookie>();
/** Field description */
private int statusCode = HttpServletResponse.SC_OK;
/** Field description */
private Map<String, String> headers = new LinkedHashMap<String, String>();
/** Field description */
private String statusMessage;
}
}

View File

@@ -0,0 +1,183 @@
/**
* 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.filter;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.repository.PermissionType;
import sonia.scm.repository.PermissionUtil;
import sonia.scm.repository.Repository;
import sonia.scm.user.User;
import sonia.scm.util.AssertUtil;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
public abstract class PermissionFilter extends HttpFilter
{
/** the logger for PermissionFilter */
private static final Logger logger =
LoggerFactory.getLogger(PermissionFilter.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param securityContextProvider
*/
public PermissionFilter(Provider<WebSecurityContext> securityContextProvider)
{
this.securityContextProvider = securityContextProvider;
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @param request
*
* @return
*/
protected abstract Repository getRepository(HttpServletRequest request);
/**
* Method description
*
*
* @param request
*
* @return
*/
protected abstract boolean isWriteRequest(HttpServletRequest request);
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param response
* @param chain
*
* @throws IOException
* @throws ServletException
*/
@Override
protected void doFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
{
WebSecurityContext securityContext = securityContextProvider.get();
AssertUtil.assertIsNotNull(securityContext);
User user = securityContext.getUser();
if (user != null)
{
Repository repository = getRepository(request);
if (repository != null)
{
boolean writeRequest = isWriteRequest(request);
if (PermissionUtil.hasPermission(repository, securityContext.getUser(),
writeRequest
? PermissionType.WRITE
: PermissionType.READ))
{
chain.doFilter(request, response);
}
else
{
if (logger.isInfoEnabled())
{
logger.info("{} access to repostitory {} for user {} denied",
new Object[] { writeRequest
? "write"
: "read", repository.getName(),
user.getName() });
}
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
}
else
{
if (logger.isDebugEnabled())
{
logger.debug("repository not found");
}
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
else
{
if (logger.isDebugEnabled())
{
logger.debug("user in not authenticated");
}
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
}
//~--- fields ---------------------------------------------------------------
/** Field description */
protected Provider<WebSecurityContext> securityContextProvider;
}

View File

@@ -0,0 +1,134 @@
/**
* 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.filter;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Provider;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
/**
*
* @author Sebastian Sdorra
*/
public abstract class RegexPermissionFilter extends PermissionFilter
{
/** Field description */
public static final Pattern PATTERN_REPOSITORYNAME =
Pattern.compile("/[^/]+/([^/]+)(?:/.*)?");
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param securityContextProvider
* @param repositoryManager
*/
public RegexPermissionFilter(
Provider<WebSecurityContext> securityContextProvider,
RepositoryManager repositoryManager)
{
super(securityContextProvider);
this.repositoryManager = repositoryManager;
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
protected abstract String getType();
/**
* Method description
*
*
* @param request
*
* @return
*/
@Override
protected Repository getRepository(HttpServletRequest request)
{
Repository repository = null;
String uri = request.getRequestURI();
uri = uri.substring(request.getContextPath().length());
Matcher m = PATTERN_REPOSITORYNAME.matcher(uri);
if (m.matches())
{
String repositoryname = m.group(1);
repository = getRepository(repositoryname);
}
return repository;
}
/**
* Method description
*
*
* @param name
*
* @return
*/
protected Repository getRepository(String name)
{
return repositoryManager.get(getType(), name);
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private RepositoryManager repositoryManager;
}

View File

@@ -29,69 +29,79 @@
*
*/
package sonia.scm.repository.xml;
package sonia.scm.web.filter;
//~--- non-JDK imports --------------------------------------------------------
import sonia.scm.repository.Repository;
import sonia.scm.user.User;
//~--- JDK imports ------------------------------------------------------------
import java.util.LinkedHashMap;
import java.util.Map;
import java.security.Principal;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
*
* @author Sebastian Sdorra
*/
public class XmlRepositoryMapAdapter
extends XmlAdapter<XmlRepositoryList, Map<String, Repository>>
public class SecurityHttpServletRequestWrapper extends HttpServletRequestWrapper
{
/**
* Constructs ...
*
*
* @param request
* @param user
*/
public SecurityHttpServletRequestWrapper(HttpServletRequest request,
User user)
{
super(request);
this.user = user;
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @param repositoryMap
*
* @return
*
* @throws Exception
*/
@Override
public XmlRepositoryList marshal(Map<String, Repository> repositoryMap)
throws Exception
public String getRemoteUser()
{
return new XmlRepositoryList(repositoryMap);
return user.getName();
}
/**
* Method description
*
*
* @param repositories
* @return
*/
public User getUser()
{
return user;
}
/**
* Method description
*
*
* @return
*
* @throws Exception
*/
@Override
public Map<String, Repository> unmarshal(XmlRepositoryList repositories)
throws Exception
public Principal getUserPrincipal()
{
Map<String, Repository> repositoryMap = new LinkedHashMap<String,
Repository>();
for (Repository repository : repositories)
{
repositoryMap.put(XmlRepositoryDatabase.createKey(repository),
repository);
}
return repositoryMap;
return user;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private User user;
}

View File

@@ -0,0 +1,68 @@
/**
* 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.Initable;
import sonia.scm.TypedObject;
//~--- JDK imports ------------------------------------------------------------
import java.io.Closeable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
public interface AuthenticationHandler extends Initable, Closeable, TypedObject
{
/**
* Method description
*
*
* @param request
* @param response
* @param username
* @param password
*
* @return
*/
public AuthenticationResult authenticate(HttpServletRequest request,
HttpServletResponse response, String username, String password);
}

View File

@@ -0,0 +1,69 @@
/**
* 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.Initable;
import sonia.scm.user.User;
//~--- JDK imports ------------------------------------------------------------
import java.io.Closeable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
public interface AuthenticationManager extends Initable, Closeable
{
/**
* Method description
*
*
* @param request
* @param response
* @param username
* @param password
*
* @return
*/
public User authenticate(HttpServletRequest request,
HttpServletResponse response, String username,
String password);
}

View File

@@ -31,48 +31,64 @@
package sonia.scm.repository.xml;
package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import sonia.scm.repository.Repository;
//~--- JDK imports ------------------------------------------------------------
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import sonia.scm.user.User;
/**
*
* @author Sebastian Sdorra
*/
@XmlRootElement(name = "repositories")
@XmlAccessorType(XmlAccessType.FIELD)
public class XmlRepositoryList implements Iterable<Repository>
public class AuthenticationResult
{
/** Field description */
public static final AuthenticationResult NOT_FOUND =
new AuthenticationResult(AuthenticationState.NOT_FOUND);
/** Field description */
public static final AuthenticationResult FAILED =
new AuthenticationResult(AuthenticationState.FAILED);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param state
*/
public XmlRepositoryList() {}
public AuthenticationResult(AuthenticationState state)
{
this.state = state;
}
/**
* Constructs ...
*
*
*
* @param repositoryMap
* @param user
*/
public XmlRepositoryList(Map<String, Repository> repositoryMap)
public AuthenticationResult(User user)
{
this.repositories = new LinkedList<Repository>(repositoryMap.values());
this.user = user;
this.state = AuthenticationState.SUCCESS;
}
/**
* Constructs ...
*
*
* @param user
* @param state
*/
public AuthenticationResult(User user, AuthenticationState state)
{
this.user = user;
this.state = state;
}
//~--- methods --------------------------------------------------------------
@@ -84,9 +100,20 @@ public class XmlRepositoryList implements Iterable<Repository>
* @return
*/
@Override
public Iterator<Repository> iterator()
public String toString()
{
return repositories.iterator();
StringBuilder out = new StringBuilder("user: ");
if (user != null)
{
out.append(user.getName());
}
else
{
out.append("null");
}
return out.append(", state: ").append(state.toString()).toString();
}
//~--- get methods ----------------------------------------------------------
@@ -97,27 +124,27 @@ public class XmlRepositoryList implements Iterable<Repository>
*
* @return
*/
public LinkedList<Repository> getRepositories()
public AuthenticationState getState()
{
return repositories;
return state;
}
//~--- set methods ----------------------------------------------------------
/**
* Method description
*
*
* @param repositories
* @return
*/
public void setRepositories(LinkedList<Repository> repositories)
public User getUser()
{
this.repositories = repositories;
return user;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
@XmlElement(name = "repository")
private LinkedList<Repository> repositories;
private AuthenticationState state;
/** Field description */
private User user;
}

View File

@@ -0,0 +1,72 @@
/**
* 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;
/**
*
* @author Sebastian Sdorra
*/
public enum AuthenticationState
{
SUCCESS(true), NOT_FOUND(false), FAILED(false);
/**
* Constructs ...
*
*
* @param successfully
*/
private AuthenticationState(boolean successfully)
{
this.successfully = successfully;
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public boolean isSuccessfully()
{
return successfully;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private boolean successfully = false;
}

View File

@@ -31,80 +31,117 @@
package sonia.scm.user.xml;
package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.servlet.SessionScoped;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.user.User;
import sonia.scm.xml.XmlTimestampDateAdapter;
import sonia.scm.user.UserManager;
//~--- JDK imports ------------------------------------------------------------
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
@XmlRootElement(name = "user-db")
@XmlAccessorType(XmlAccessType.FIELD)
public class XmlUserDatabase
@SessionScoped
public class BasicSecurityContext implements WebSecurityContext
{
/** the logger for BasicSecurityContext */
private static final Logger logger =
LoggerFactory.getLogger(BasicSecurityContext.class);
//~--- constructors ---------------------------------------------------------
/**
* Method description
* Constructs ...
*
*
* @param user
* @param authenticator
* @param userManager
*/
public void add(User user)
@Inject
public BasicSecurityContext(AuthenticationManager authenticator,
UserManager userManager)
{
userMap.put(user.getName(), user);
this.authenticator = authenticator;
this.userManager = userManager;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param response
* @param username
* @param password
*
* @return
*/
public boolean contains(String username)
@Override
public User authenticate(HttpServletRequest request,
HttpServletResponse response, String username,
String password)
{
return userMap.containsKey(username);
user = authenticator.authenticate(request, response, username, password);
if (user != null)
{
try
{
user.setLastLogin(System.currentTimeMillis());
User dbUser = userManager.get(username);
if (dbUser != null)
{
// update properties
dbUser.setDisplayName(user.getDisplayName());
dbUser.setLastLogin(user.getLastLogin());
dbUser.setMail(user.getMail());
dbUser.setType(user.getType());
userManager.modify(dbUser);
}
else
{
userManager.create(user);
}
}
catch (Exception ex)
{
user = null;
logger.error(ex.getMessage(), ex);
}
}
return user;
}
/**
* Method description
*
*
* @param username
*
* @return
* @param request
* @param response
*/
public User remove(String username)
@Override
public void logout(HttpServletRequest request, HttpServletResponse response)
{
return userMap.remove(username);
}
/**
* Method description
*
*
* @return
*/
public Collection<User> values()
{
return userMap.values();
user = null;
}
//~--- get methods ----------------------------------------------------------
@@ -113,13 +150,12 @@ public class XmlUserDatabase
* Method description
*
*
* @param username
*
* @return
*/
public User get(String username)
@Override
public User getUser()
{
return userMap.get(username);
return user;
}
/**
@@ -128,58 +164,20 @@ public class XmlUserDatabase
*
* @return
*/
public long getCreationTime()
@Override
public boolean isAuthenticated()
{
return creationTime;
}
/**
* Method description
*
*
* @return
*/
public long getLastModified()
{
return lastModified;
}
//~--- set methods ----------------------------------------------------------
/**
* Method description
*
*
* @param creationTime
*/
public void setCreationTime(long creationTime)
{
this.creationTime = creationTime;
}
/**
* Method description
*
*
* @param lastModified
*/
public void setLastModified(long lastModified)
{
this.lastModified = lastModified;
return user != null;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
@XmlJavaTypeAdapter(XmlTimestampDateAdapter.class)
private Long creationTime;
private AuthenticationManager authenticator;
/** Field description */
@XmlJavaTypeAdapter(XmlTimestampDateAdapter.class)
private Long lastModified;
private User user;
/** Field description */
@XmlJavaTypeAdapter(XmlUserMapAdapter.class)
@XmlElement(name = "users")
private Map<String, User> userMap = new LinkedHashMap<String, User>();
private UserManager userManager;
}

View File

@@ -0,0 +1,175 @@
/**
* 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 com.google.inject.Inject;
import com.google.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.SCMContextProvider;
import sonia.scm.user.User;
import sonia.scm.util.AssertUtil;
import sonia.scm.util.IOUtil;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class ChainAuthenticatonManager implements AuthenticationManager
{
/** the logger for ChainAuthenticatonManager */
private static final Logger logger =
LoggerFactory.getLogger(ChainAuthenticatonManager.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param authenticationHandlerSet
*/
@Inject
public ChainAuthenticatonManager(
Set<AuthenticationHandler> authenticationHandlerSet)
{
AssertUtil.assertIsNotEmpty(authenticationHandlerSet);
this.authenticationHandlerSet = authenticationHandlerSet;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param response
* @param username
* @param password
*
* @return
*/
@Override
public User authenticate(HttpServletRequest request,
HttpServletResponse response, String username,
String password)
{
User user = null;
for (AuthenticationHandler authenticator : authenticationHandlerSet)
{
try
{
AuthenticationResult result = authenticator.authenticate(request,
response, username, password);
if (logger.isDebugEnabled())
{
logger.debug("authenticator {} ends with result, {}",
authenticator.getClass().getName(), result);
}
if ((result != null) && (result.getState() != null)
&& (result.getState().isSuccessfully()
|| (result.getState() == AuthenticationState.FAILED)))
{
if (result.getState().isSuccessfully() && (result.getUser() != null))
{
user = result.getUser();
user.setType(authenticator.getType());
}
break;
}
}
catch (Exception ex)
{
logger.error(ex.getMessage(), ex);
}
}
return user;
}
/**
* Method description
*
*
* @throws IOException
*/
@Override
public void close() throws IOException
{
for (AuthenticationHandler authenticator : authenticationHandlerSet)
{
IOUtil.close(authenticator);
}
}
/**
* Method description
*
*
* @param context
*/
@Override
public void init(SCMContextProvider context)
{
for (AuthenticationHandler authenticator : authenticationHandlerSet)
{
authenticator.init(context);
}
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private Set<AuthenticationHandler> authenticationHandlerSet;
}

View File

@@ -31,63 +31,56 @@
package sonia.scm.user.xml;
package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import sonia.scm.security.SecurityContext;
import sonia.scm.user.User;
//~--- JDK imports ------------------------------------------------------------
import java.util.LinkedHashMap;
import java.util.Map;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
public class XmlUserMapAdapter
extends XmlAdapter<XmlUserList, Map<String, User>>
public interface WebSecurityContext extends SecurityContext
{
/**
* Method description
*
*
* @param userMap
* @param request
* @param response
* @param username
* @param password
*
* @return
*
* @throws Exception
*/
@Override
public XmlUserList marshal(Map<String, User> userMap) throws Exception
{
return new XmlUserList(userMap);
}
public User authenticate(HttpServletRequest request,
HttpServletResponse response, String username,
String password);
/**
* Method description
*
*
* @param users
* @param request
* @param response
*/
public void logout(HttpServletRequest request, HttpServletResponse response);
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*
* @throws Exception
*/
@Override
public Map<String, User> unmarshal(XmlUserList users) throws Exception
{
Map<String, User> userMap = new LinkedHashMap<String, User>();
for (User user : users)
{
userMap.put(user.getName(), user);
}
return userMap;
}
public boolean isAuthenticated();
}

View File

@@ -0,0 +1,230 @@
/**
* 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 com.google.inject.Inject;
import com.google.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.SCMContextProvider;
import sonia.scm.security.EncryptionHandler;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class XmlAuthenticationHandler implements AuthenticationHandler
{
/** Field description */
public static final String NAME_DIRECTORY = "users";
/** Field description */
public static final String TYPE = "xml";
/** the logger for XmlAuthenticationHandler */
private static final Logger logger =
LoggerFactory.getLogger(XmlAuthenticationHandler.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param userManager
* @param encryptionHandler
*/
@Inject
public XmlAuthenticationHandler(UserManager userManager,
EncryptionHandler encryptionHandler)
{
this.userManager = userManager;
this.encryptionHandler = encryptionHandler;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param response
* @param username
* @param password
*
* @return
*/
@Override
public AuthenticationResult authenticate(HttpServletRequest request,
HttpServletResponse response, String username, String password)
{
AuthenticationResult result = null;
User user = userManager.get(username);
if (user != null)
{
if (TYPE.equals(user.getType()))
{
result = authenticate(user, username, password);
}
else
{
if (logger.isDebugEnabled())
{
logger.debug("{} is not an xml user", username);
}
result = AuthenticationResult.NOT_FOUND;
}
}
else
{
if (logger.isDebugEnabled())
{
logger.debug("could not find user {}", username);
}
result = AuthenticationResult.NOT_FOUND;
}
return result;
}
/**
* Method description
*
*
* @throws IOException
*/
@Override
public void close() throws IOException
{
// do nothing
}
/**
* Method description
*
*
* @param provider
*/
@Override
public void init(SCMContextProvider provider)
{
// do nothing
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
@Override
public String getType()
{
return TYPE;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param user
* @param username
* @param password
*
* @return
*/
private AuthenticationResult authenticate(User user, String username,
String password)
{
AuthenticationResult result = null;
String encryptedPassword = encryptionHandler.encrypt(password);
if (!encryptedPassword.equalsIgnoreCase(user.getPassword()))
{
user = null;
if (logger.isDebugEnabled())
{
logger.debug("password for user {} is wrong", username);
}
result = AuthenticationResult.FAILED;
}
else
{
if (logger.isDebugEnabled())
{
logger.debug("user {} logged in successfully", username);
}
user.setPassword(null);
result = new AuthenticationResult(user, AuthenticationState.SUCCESS);
}
return result;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private EncryptionHandler encryptionHandler;
/** Field description */
private UserManager userManager;
}