mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 08:25:44 +01:00
Merge with 2.0.0-m3
This commit is contained in:
@@ -1,207 +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;
|
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import org.apache.commons.lang.StringEscapeUtils;
|
|
||||||
import sonia.scm.util.Util;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Sebastian Sdorra
|
|
||||||
* @since 1.15
|
|
||||||
*/
|
|
||||||
public final class EscapeUtil
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs ...
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private EscapeUtil() {}
|
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param result
|
|
||||||
*/
|
|
||||||
public static void escape(BrowserResult result)
|
|
||||||
{
|
|
||||||
result.setBranch(escape(result.getBranch()));
|
|
||||||
result.setTag(escape(result.getTag()));
|
|
||||||
|
|
||||||
for (FileObject fo : result)
|
|
||||||
{
|
|
||||||
escape(fo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param result
|
|
||||||
* @since 1.17
|
|
||||||
*/
|
|
||||||
public static void escape(BlameResult result)
|
|
||||||
{
|
|
||||||
for (BlameLine line : result.getBlameLines())
|
|
||||||
{
|
|
||||||
escape(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param line
|
|
||||||
* @since 1.17
|
|
||||||
*/
|
|
||||||
public static void escape(BlameLine line)
|
|
||||||
{
|
|
||||||
line.setDescription(escape(line.getDescription()));
|
|
||||||
escape(line.getAuthor());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param fo
|
|
||||||
*/
|
|
||||||
public static void escape(FileObject fo)
|
|
||||||
{
|
|
||||||
fo.setDescription(escape(fo.getDescription()));
|
|
||||||
fo.setName(fo.getName());
|
|
||||||
fo.setPath(fo.getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param changeset
|
|
||||||
*/
|
|
||||||
public static void escape(Changeset changeset)
|
|
||||||
{
|
|
||||||
changeset.setDescription(escape(changeset.getDescription()));
|
|
||||||
escape(changeset.getAuthor());
|
|
||||||
changeset.setBranches(escapeList(changeset.getBranches()));
|
|
||||||
changeset.setTags(escapeList(changeset.getTags()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param person
|
|
||||||
* @since 1.17
|
|
||||||
*/
|
|
||||||
public static void escape(Person person)
|
|
||||||
{
|
|
||||||
if (person != null)
|
|
||||||
{
|
|
||||||
person.setName(escape(person.getName()));
|
|
||||||
person.setMail(escape(person.getMail()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param result
|
|
||||||
*/
|
|
||||||
public static void escape(ChangesetPagingResult result)
|
|
||||||
{
|
|
||||||
for (Changeset c : result)
|
|
||||||
{
|
|
||||||
escape(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param value
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static String escape(String value)
|
|
||||||
{
|
|
||||||
return StringEscapeUtils.escapeHtml(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param values
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static List<String> escapeList(List<String> values)
|
|
||||||
{
|
|
||||||
if (Util.isNotEmpty(values))
|
|
||||||
{
|
|
||||||
List<String> newList = Lists.newArrayList();
|
|
||||||
|
|
||||||
for (String v : values)
|
|
||||||
{
|
|
||||||
newList.add(StringEscapeUtils.escapeHtml(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
values = newList;
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void escape(Modifications modifications) {
|
|
||||||
modifications.setAdded(escapeList(modifications.getAdded()));
|
|
||||||
modifications.setModified(escapeList(modifications.getModified()));
|
|
||||||
modifications.setRemoved(escapeList(modifications.getRemoved()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -111,7 +111,6 @@ public class PreProcessorUtil
|
|||||||
blameLine.getLineNumber(), repository.getName());
|
blameLine.getLineNumber(), repository.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
EscapeUtil.escape(blameLine);
|
|
||||||
handlePreProcess(repository,blameLine,blameLinePreProcessorFactorySet, blameLinePreProcessorSet);
|
handlePreProcess(repository,blameLine,blameLinePreProcessorFactorySet, blameLinePreProcessorSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,22 +122,6 @@ public class PreProcessorUtil
|
|||||||
* @param blameResult
|
* @param blameResult
|
||||||
*/
|
*/
|
||||||
public void prepareForReturn(Repository repository, BlameResult blameResult)
|
public void prepareForReturn(Repository repository, BlameResult blameResult)
|
||||||
{
|
|
||||||
prepareForReturn(repository, blameResult, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param repository
|
|
||||||
* @param blameResult
|
|
||||||
* @param escape
|
|
||||||
*
|
|
||||||
* @since 1.35
|
|
||||||
*/
|
|
||||||
public void prepareForReturn(Repository repository, BlameResult blameResult,
|
|
||||||
boolean escape)
|
|
||||||
{
|
{
|
||||||
if (logger.isTraceEnabled())
|
if (logger.isTraceEnabled())
|
||||||
{
|
{
|
||||||
@@ -146,10 +129,6 @@ public class PreProcessorUtil
|
|||||||
repository.getName());
|
repository.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (escape)
|
|
||||||
{
|
|
||||||
EscapeUtil.escape(blameResult);
|
|
||||||
}
|
|
||||||
handlePreProcessForIterable(repository, blameResult.getBlameLines(),blameLinePreProcessorFactorySet, blameLinePreProcessorSet);
|
handlePreProcessForIterable(repository, blameResult.getBlameLines(),blameLinePreProcessorFactorySet, blameLinePreProcessorSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,32 +141,12 @@ public class PreProcessorUtil
|
|||||||
*/
|
*/
|
||||||
public void prepareForReturn(Repository repository, Changeset changeset)
|
public void prepareForReturn(Repository repository, Changeset changeset)
|
||||||
{
|
{
|
||||||
prepareForReturn(repository, changeset, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param repository
|
|
||||||
* @param changeset
|
|
||||||
* @param escape
|
|
||||||
*
|
|
||||||
* @since 1.35
|
|
||||||
*/
|
|
||||||
public void prepareForReturn(Repository repository, Changeset changeset, boolean escape){
|
|
||||||
logger.trace("prepare changeset {} of repository {} for return", changeset.getId(), repository.getName());
|
logger.trace("prepare changeset {} of repository {} for return", changeset.getId(), repository.getName());
|
||||||
if (escape) {
|
|
||||||
EscapeUtil.escape(changeset);
|
|
||||||
}
|
|
||||||
handlePreProcess(repository, changeset, changesetPreProcessorFactorySet, changesetPreProcessorSet);
|
handlePreProcess(repository, changeset, changesetPreProcessorFactorySet, changesetPreProcessorSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void prepareForReturn(Repository repository, Modifications modifications, boolean escape) {
|
public void prepareForReturn(Repository repository, Modifications modifications) {
|
||||||
logger.trace("prepare modifications {} of repository {} for return", modifications, repository.getName());
|
logger.trace("prepare modifications {} of repository {} for return", modifications, repository.getName());
|
||||||
if (escape) {
|
|
||||||
EscapeUtil.escape(modifications);
|
|
||||||
}
|
|
||||||
handlePreProcess(repository, modifications, modificationsPreProcessorFactorySet, modificationsPreProcessorSet);
|
handlePreProcess(repository, modifications, modificationsPreProcessorFactorySet, modificationsPreProcessorSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,22 +158,6 @@ public class PreProcessorUtil
|
|||||||
* @param result
|
* @param result
|
||||||
*/
|
*/
|
||||||
public void prepareForReturn(Repository repository, BrowserResult result)
|
public void prepareForReturn(Repository repository, BrowserResult result)
|
||||||
{
|
|
||||||
prepareForReturn(repository, result, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param repository
|
|
||||||
* @param result
|
|
||||||
* @param escape
|
|
||||||
*
|
|
||||||
* @since 1.35
|
|
||||||
*/
|
|
||||||
public void prepareForReturn(Repository repository, BrowserResult result,
|
|
||||||
boolean escape)
|
|
||||||
{
|
{
|
||||||
if (logger.isTraceEnabled())
|
if (logger.isTraceEnabled())
|
||||||
{
|
{
|
||||||
@@ -222,10 +165,6 @@ public class PreProcessorUtil
|
|||||||
repository.getName());
|
repository.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (escape)
|
|
||||||
{
|
|
||||||
EscapeUtil.escape(result);
|
|
||||||
}
|
|
||||||
handlePreProcessForIterable(repository, result,fileObjectPreProcessorFactorySet, fileObjectPreProcessorSet);
|
handlePreProcessForIterable(repository, result,fileObjectPreProcessorFactorySet, fileObjectPreProcessorSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,12 +174,8 @@ public class PreProcessorUtil
|
|||||||
*
|
*
|
||||||
* @param repository
|
* @param repository
|
||||||
* @param result
|
* @param result
|
||||||
* @param escape
|
|
||||||
*
|
|
||||||
* @since 1.35
|
|
||||||
*/
|
*/
|
||||||
public void prepareForReturn(Repository repository,
|
public void prepareForReturn(Repository repository, ChangesetPagingResult result)
|
||||||
ChangesetPagingResult result, boolean escape)
|
|
||||||
{
|
{
|
||||||
if (logger.isTraceEnabled())
|
if (logger.isTraceEnabled())
|
||||||
{
|
{
|
||||||
@@ -248,27 +183,9 @@ public class PreProcessorUtil
|
|||||||
repository.getName());
|
repository.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (escape)
|
|
||||||
{
|
|
||||||
EscapeUtil.escape(result);
|
|
||||||
}
|
|
||||||
handlePreProcessForIterable(repository,result,changesetPreProcessorFactorySet, changesetPreProcessorSet);
|
handlePreProcessForIterable(repository,result,changesetPreProcessorFactorySet, changesetPreProcessorSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param repository
|
|
||||||
* @param result
|
|
||||||
*/
|
|
||||||
public void prepareForReturn(Repository repository,
|
|
||||||
ChangesetPagingResult result)
|
|
||||||
{
|
|
||||||
prepareForReturn(repository, result, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private <T, F extends PreProcessorFactory<T>, P extends PreProcessor<T>> void handlePreProcess(Repository repository, T processedObject,
|
private <T, F extends PreProcessorFactory<T>, P extends PreProcessor<T>> void handlePreProcess(Repository repository, T processedObject,
|
||||||
Collection<F> factories,
|
Collection<F> factories,
|
||||||
Collection<P> preProcessors) {
|
Collection<P> preProcessors) {
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ public final class BlameCommandBuilder
|
|||||||
|
|
||||||
if (!disablePreProcessors && (result != null))
|
if (!disablePreProcessors && (result != null))
|
||||||
{
|
{
|
||||||
preProcessorUtil.prepareForReturn(repository, result, !disableEscaping);
|
preProcessorUtil.prepareForReturn(repository, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -210,24 +210,6 @@ public final class BlameCommandBuilder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable html escaping for the returned blame lines. By default all
|
|
||||||
* blame lines are html escaped.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param disableEscaping true to disable the html escaping
|
|
||||||
*
|
|
||||||
* @return {@code this}
|
|
||||||
*
|
|
||||||
* @since 1.35
|
|
||||||
*/
|
|
||||||
public BlameCommandBuilder setDisableEscaping(boolean disableEscaping)
|
|
||||||
{
|
|
||||||
this.disableEscaping = disableEscaping;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable the execution of pre processors.
|
* Disable the execution of pre processors.
|
||||||
*
|
*
|
||||||
@@ -362,9 +344,6 @@ public final class BlameCommandBuilder
|
|||||||
/** the cache */
|
/** the cache */
|
||||||
private final Cache<CacheKey, BlameResult> cache;
|
private final Cache<CacheKey, BlameResult> cache;
|
||||||
|
|
||||||
/** disable escaping */
|
|
||||||
private boolean disableEscaping = false;
|
|
||||||
|
|
||||||
/** disable change */
|
/** disable change */
|
||||||
private boolean disableCache = false;
|
private boolean disableCache = false;
|
||||||
|
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ public final class BrowseCommandBuilder
|
|||||||
|
|
||||||
if (!disablePreProcessors && (result != null))
|
if (!disablePreProcessors && (result != null))
|
||||||
{
|
{
|
||||||
preProcessorUtil.prepareForReturn(repository, result, !disableEscaping);
|
preProcessorUtil.prepareForReturn(repository, result);
|
||||||
|
|
||||||
List<FileObject> fileObjects = result.getFiles();
|
List<FileObject> fileObjects = result.getFiles();
|
||||||
|
|
||||||
@@ -212,24 +212,6 @@ public final class BrowseCommandBuilder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable html escaping for the returned file objects. By default all
|
|
||||||
* file objects are html escaped.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param disableEscaping true to disable the html escaping
|
|
||||||
*
|
|
||||||
* @return {@code this}
|
|
||||||
*
|
|
||||||
* @since 1.35
|
|
||||||
*/
|
|
||||||
public BrowseCommandBuilder setDisableEscaping(boolean disableEscaping)
|
|
||||||
{
|
|
||||||
this.disableEscaping = disableEscaping;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disabling the last commit means that every call to
|
* Disabling the last commit means that every call to
|
||||||
* {@link FileObject#getDescription()} and
|
* {@link FileObject#getDescription()} and
|
||||||
@@ -433,9 +415,6 @@ public final class BrowseCommandBuilder
|
|||||||
/** cache */
|
/** cache */
|
||||||
private final Cache<CacheKey, BrowserResult> cache;
|
private final Cache<CacheKey, BrowserResult> cache;
|
||||||
|
|
||||||
/** disable escaping */
|
|
||||||
private boolean disableEscaping = false;
|
|
||||||
|
|
||||||
/** disables the cache */
|
/** disables the cache */
|
||||||
private boolean disableCache = false;
|
private boolean disableCache = false;
|
||||||
|
|
||||||
|
|||||||
@@ -132,8 +132,7 @@ public final class HookChangesetBuilder
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
copy = DeepCopy.copy(c);
|
copy = DeepCopy.copy(c);
|
||||||
preProcessorUtil.prepareForReturn(repository, copy,
|
preProcessorUtil.prepareForReturn(repository, copy);
|
||||||
!disableEscaping);
|
|
||||||
}
|
}
|
||||||
catch (IOException ex)
|
catch (IOException ex)
|
||||||
{
|
{
|
||||||
@@ -156,24 +155,6 @@ public final class HookChangesetBuilder
|
|||||||
|
|
||||||
//~--- set methods ----------------------------------------------------------
|
//~--- set methods ----------------------------------------------------------
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable html escaping for the returned changesets. By default all
|
|
||||||
* changesets are html escaped.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param disableEscaping true to disable the html escaping
|
|
||||||
*
|
|
||||||
* @return {@code this}
|
|
||||||
*
|
|
||||||
* @since 1.35
|
|
||||||
*/
|
|
||||||
public HookChangesetBuilder setDisableEscaping(boolean disableEscaping)
|
|
||||||
{
|
|
||||||
this.disableEscaping = disableEscaping;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable the execution of pre processors.
|
* Disable the execution of pre processors.
|
||||||
*
|
*
|
||||||
@@ -192,9 +173,6 @@ public final class HookChangesetBuilder
|
|||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|
||||||
/** disable escaping */
|
|
||||||
private boolean disableEscaping = false;
|
|
||||||
|
|
||||||
/** disable pre processors marker */
|
/** disable pre processors marker */
|
||||||
private boolean disablePreProcessors = false;
|
private boolean disablePreProcessors = false;
|
||||||
|
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ public final class LogCommandBuilder
|
|||||||
|
|
||||||
if (!disablePreProcessors && (cpr != null))
|
if (!disablePreProcessors && (cpr != null))
|
||||||
{
|
{
|
||||||
preProcessorUtil.prepareForReturn(repository, cpr, !disableEscaping);
|
preProcessorUtil.prepareForReturn(repository, cpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return cpr;
|
return cpr;
|
||||||
@@ -306,24 +306,6 @@ public final class LogCommandBuilder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable html escaping for the returned changesets. By default all
|
|
||||||
* changesets are html escaped.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param disableEscaping true to disable the html escaping
|
|
||||||
*
|
|
||||||
* @return {@code this}
|
|
||||||
*
|
|
||||||
* @since 1.35
|
|
||||||
*/
|
|
||||||
public LogCommandBuilder setDisableEscaping(boolean disableEscaping)
|
|
||||||
{
|
|
||||||
this.disableEscaping = disableEscaping;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable the execution of pre processors.
|
* Disable the execution of pre processors.
|
||||||
*
|
*
|
||||||
@@ -545,9 +527,6 @@ public final class LogCommandBuilder
|
|||||||
/** repository to query */
|
/** repository to query */
|
||||||
private final Repository repository;
|
private final Repository repository;
|
||||||
|
|
||||||
/** disable escaping */
|
|
||||||
private boolean disableEscaping = false;
|
|
||||||
|
|
||||||
/** disable cache */
|
/** disable cache */
|
||||||
private boolean disableCache = false;
|
private boolean disableCache = false;
|
||||||
|
|
||||||
|
|||||||
@@ -43,9 +43,6 @@ public final class ModificationsCommandBuilder {
|
|||||||
|
|
||||||
private final PreProcessorUtil preProcessorUtil;
|
private final PreProcessorUtil preProcessorUtil;
|
||||||
|
|
||||||
@Setter
|
|
||||||
private boolean disableEscaping = false;
|
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private boolean disableCache = false;
|
private boolean disableCache = false;
|
||||||
|
|
||||||
@@ -90,7 +87,7 @@ public final class ModificationsCommandBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!disablePreProcessors && (modifications != null)) {
|
if (!disablePreProcessors && (modifications != null)) {
|
||||||
preProcessorUtil.prepareForReturn(repository, modifications, !disableEscaping);
|
preProcessorUtil.prepareForReturn(repository, modifications);
|
||||||
}
|
}
|
||||||
return modifications;
|
return modifications;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package sonia.scm.user;
|
||||||
|
|
||||||
|
public class ChangePasswordNotAllowedException extends RuntimeException {
|
||||||
|
|
||||||
|
public static final String WRONG_USER_TYPE = "User of type {0} are not allowed to change password";
|
||||||
|
|
||||||
|
public ChangePasswordNotAllowedException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package sonia.scm.user;
|
||||||
|
|
||||||
|
public class InvalidPasswordException extends RuntimeException {
|
||||||
|
|
||||||
|
public static final String INVALID_MATCHING = "The given Password does not match with the stored one.";
|
||||||
|
|
||||||
|
public InvalidPasswordException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -274,6 +274,10 @@ User extends BasicPropertiesAware implements Principal, ModelObject, PermissionO
|
|||||||
//J+
|
//J+
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public User changePassword(String password){
|
||||||
|
setPassword(password);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
//~--- get methods ----------------------------------------------------------
|
//~--- get methods ----------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ package sonia.scm.user;
|
|||||||
import sonia.scm.Manager;
|
import sonia.scm.Manager;
|
||||||
import sonia.scm.search.Searchable;
|
import sonia.scm.search.Searchable;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import static sonia.scm.user.ChangePasswordNotAllowedException.WRONG_USER_TYPE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The central class for managing {@link User} objects.
|
* The central class for managing {@link User} objects.
|
||||||
* This class is a singleton and is available via injection.
|
* This class is a singleton and is available via injection.
|
||||||
@@ -68,4 +73,22 @@ public interface UserManager
|
|||||||
* @since 1.14
|
* @since 1.14
|
||||||
*/
|
*/
|
||||||
public String getDefaultType();
|
public String getDefaultType();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only account of the default type "xml" can change their password
|
||||||
|
*/
|
||||||
|
default Consumer<User> getUserTypeChecker() {
|
||||||
|
return user -> {
|
||||||
|
if (!isTypeDefault(user)) {
|
||||||
|
throw new ChangePasswordNotAllowedException(MessageFormat.format(WRONG_USER_TYPE, user.getType()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isTypeDefault(User user) {
|
||||||
|
return getDefaultType().equals(user.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class VndMediaType {
|
|||||||
public static final String PERMISSION = PREFIX + "permission" + SUFFIX;
|
public static final String PERMISSION = PREFIX + "permission" + SUFFIX;
|
||||||
public static final String CHANGESET = PREFIX + "changeset" + SUFFIX;
|
public static final String CHANGESET = PREFIX + "changeset" + SUFFIX;
|
||||||
public static final String CHANGESET_COLLECTION = PREFIX + "changesetCollection" + SUFFIX;
|
public static final String CHANGESET_COLLECTION = PREFIX + "changesetCollection" + SUFFIX;
|
||||||
public static final String MODIFICATIONS = PREFIX + "modifications" + SUFFIX;;
|
public static final String MODIFICATIONS = PREFIX + "modifications" + SUFFIX;
|
||||||
public static final String TAG = PREFIX + "tag" + SUFFIX;
|
public static final String TAG = PREFIX + "tag" + SUFFIX;
|
||||||
public static final String TAG_COLLECTION = PREFIX + "tagCollection" + SUFFIX;
|
public static final String TAG_COLLECTION = PREFIX + "tagCollection" + SUFFIX;
|
||||||
public static final String BRANCH = PREFIX + "branch" + SUFFIX;
|
public static final String BRANCH = PREFIX + "branch" + SUFFIX;
|
||||||
@@ -35,6 +35,8 @@ public class VndMediaType {
|
|||||||
public static final String REPOSITORY_TYPE = PREFIX + "repositoryType" + SUFFIX;
|
public static final String REPOSITORY_TYPE = PREFIX + "repositoryType" + SUFFIX;
|
||||||
public static final String UI_PLUGIN = PREFIX + "uiPlugin" + SUFFIX;
|
public static final String UI_PLUGIN = PREFIX + "uiPlugin" + SUFFIX;
|
||||||
public static final String UI_PLUGIN_COLLECTION = PREFIX + "uiPluginCollection" + SUFFIX;
|
public static final String UI_PLUGIN_COLLECTION = PREFIX + "uiPluginCollection" + SUFFIX;
|
||||||
|
@SuppressWarnings("squid:S2068")
|
||||||
|
public static final String PASSWORD_CHANGE = PREFIX + "passwordChange" + SUFFIX;
|
||||||
|
|
||||||
public static final String ME = PREFIX + "me" + SUFFIX;
|
public static final String ME = PREFIX + "me" + SUFFIX;
|
||||||
public static final String SOURCE = PREFIX + "source" + SUFFIX;
|
public static final String SOURCE = PREFIX + "source" + SUFFIX;
|
||||||
|
|||||||
79
scm-it/src/test/java/sonia/scm/it/MeITCase.java
Normal file
79
scm-it/src/test/java/sonia/scm/it/MeITCase.java
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package sonia.scm.it;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import sonia.scm.it.utils.ScmRequests;
|
||||||
|
import sonia.scm.it.utils.TestData;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public class MeITCase {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() {
|
||||||
|
TestData.cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void adminShouldChangeOwnPassword() {
|
||||||
|
String newPassword = TestData.USER_SCM_ADMIN + "1";
|
||||||
|
// admin change the own password
|
||||||
|
ScmRequests.start()
|
||||||
|
.given()
|
||||||
|
.url(TestData.getMeUrl())
|
||||||
|
.usernameAndPassword(TestData.USER_SCM_ADMIN, TestData.USER_SCM_ADMIN)
|
||||||
|
.getMeResource()
|
||||||
|
.assertStatusCode(200)
|
||||||
|
.usingMeResponse()
|
||||||
|
.assertAdmin(aBoolean -> assertThat(aBoolean).isEqualTo(Boolean.TRUE))
|
||||||
|
.assertPassword(Assert::assertNull)
|
||||||
|
.assertType(s -> assertThat(s).isEqualTo("xml"))
|
||||||
|
.requestChangePassword(TestData.USER_SCM_ADMIN, newPassword)
|
||||||
|
.assertStatusCode(204);
|
||||||
|
// assert password is changed -> login with the new Password than undo changes
|
||||||
|
ScmRequests.start()
|
||||||
|
.given()
|
||||||
|
.url(TestData.getUserUrl(TestData.USER_SCM_ADMIN))
|
||||||
|
.usernameAndPassword(TestData.USER_SCM_ADMIN, newPassword)
|
||||||
|
.getMeResource()
|
||||||
|
.assertStatusCode(200)
|
||||||
|
.usingMeResponse()
|
||||||
|
.assertAdmin(aBoolean -> assertThat(aBoolean).isEqualTo(Boolean.TRUE))// still admin
|
||||||
|
.requestChangePassword(newPassword, TestData.USER_SCM_ADMIN)
|
||||||
|
.assertStatusCode(204);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldHidePasswordLinkIfUserTypeIsNotXML() {
|
||||||
|
String newUser = "user";
|
||||||
|
String password = "pass";
|
||||||
|
String type = "not XML Type";
|
||||||
|
TestData.createUser(newUser, password, true, type);
|
||||||
|
ScmRequests.start()
|
||||||
|
.given()
|
||||||
|
.url(TestData.getMeUrl())
|
||||||
|
.usernameAndPassword(newUser, password)
|
||||||
|
.getMeResource()
|
||||||
|
.assertStatusCode(200)
|
||||||
|
.usingMeResponse()
|
||||||
|
.assertAdmin(aBoolean -> assertThat(aBoolean).isEqualTo(Boolean.TRUE))
|
||||||
|
.assertPassword(Assert::assertNull)
|
||||||
|
.assertType(s -> assertThat(s).isEqualTo(type))
|
||||||
|
.assertPasswordLinkDoesNotExists();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldGet403IfUserIsNotAdmin() {
|
||||||
|
String newUser = "user";
|
||||||
|
String password = "pass";
|
||||||
|
String type = "xml";
|
||||||
|
TestData.createUser(newUser, password, false, type);
|
||||||
|
ScmRequests.start()
|
||||||
|
.given()
|
||||||
|
.url(TestData.getMeUrl())
|
||||||
|
.usernameAndPassword(newUser, password)
|
||||||
|
.getMeResource()
|
||||||
|
.assertStatusCode(403);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,8 @@ import org.junit.rules.TemporaryFolder;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.junit.runners.Parameterized;
|
||||||
import org.junit.runners.Parameterized.Parameters;
|
import org.junit.runners.Parameterized.Parameters;
|
||||||
|
import sonia.scm.it.utils.RepositoryUtil;
|
||||||
|
import sonia.scm.it.utils.TestData;
|
||||||
import sonia.scm.repository.PermissionType;
|
import sonia.scm.repository.PermissionType;
|
||||||
import sonia.scm.repository.client.api.RepositoryClient;
|
import sonia.scm.repository.client.api.RepositoryClient;
|
||||||
import sonia.scm.repository.client.api.RepositoryClientException;
|
import sonia.scm.repository.client.api.RepositoryClientException;
|
||||||
@@ -51,11 +53,11 @@ import java.util.Objects;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static sonia.scm.it.RepositoryUtil.addAndCommitRandomFile;
|
import static sonia.scm.it.utils.RepositoryUtil.addAndCommitRandomFile;
|
||||||
import static sonia.scm.it.RestUtil.given;
|
import static sonia.scm.it.utils.RestUtil.given;
|
||||||
import static sonia.scm.it.ScmTypes.availableScmTypes;
|
import static sonia.scm.it.utils.ScmTypes.availableScmTypes;
|
||||||
import static sonia.scm.it.TestData.USER_SCM_ADMIN;
|
import static sonia.scm.it.utils.TestData.USER_SCM_ADMIN;
|
||||||
import static sonia.scm.it.TestData.callRepository;
|
import static sonia.scm.it.utils.TestData.callRepository;
|
||||||
|
|
||||||
@RunWith(Parameterized.class)
|
@RunWith(Parameterized.class)
|
||||||
public class PermissionsITCase {
|
public class PermissionsITCase {
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ import org.junit.rules.TemporaryFolder;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.junit.runners.Parameterized;
|
||||||
import org.junit.runners.Parameterized.Parameters;
|
import org.junit.runners.Parameterized.Parameters;
|
||||||
|
import sonia.scm.it.utils.RepositoryUtil;
|
||||||
|
import sonia.scm.it.utils.TestData;
|
||||||
import sonia.scm.repository.client.api.RepositoryClient;
|
import sonia.scm.repository.client.api.RepositoryClient;
|
||||||
import sonia.scm.web.VndMediaType;
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
@@ -53,11 +55,11 @@ import static org.hamcrest.Matchers.equalTo;
|
|||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.nullValue;
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static sonia.scm.it.RegExMatcher.matchesPattern;
|
import static sonia.scm.it.utils.RegExMatcher.matchesPattern;
|
||||||
import static sonia.scm.it.RestUtil.createResourceUrl;
|
import static sonia.scm.it.utils.RestUtil.createResourceUrl;
|
||||||
import static sonia.scm.it.RestUtil.given;
|
import static sonia.scm.it.utils.RestUtil.given;
|
||||||
import static sonia.scm.it.ScmTypes.availableScmTypes;
|
import static sonia.scm.it.utils.ScmTypes.availableScmTypes;
|
||||||
import static sonia.scm.it.TestData.repositoryJson;
|
import static sonia.scm.it.utils.TestData.repositoryJson;
|
||||||
|
|
||||||
@RunWith(Parameterized.class)
|
@RunWith(Parameterized.class)
|
||||||
public class RepositoriesITCase {
|
public class RepositoriesITCase {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import io.restassured.response.ExtractableResponse;
|
|||||||
import io.restassured.response.Response;
|
import io.restassured.response.Response;
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
import org.assertj.core.util.Lists;
|
import org.assertj.core.util.Lists;
|
||||||
import org.assertj.core.util.Maps;
|
|
||||||
import org.junit.Assume;
|
import org.junit.Assume;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
@@ -12,6 +11,9 @@ import org.junit.Test;
|
|||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.junit.runners.Parameterized;
|
||||||
|
import sonia.scm.it.utils.RepositoryUtil;
|
||||||
|
import sonia.scm.it.utils.ScmRequests;
|
||||||
|
import sonia.scm.it.utils.TestData;
|
||||||
import sonia.scm.repository.Changeset;
|
import sonia.scm.repository.Changeset;
|
||||||
import sonia.scm.repository.client.api.ClientCommand;
|
import sonia.scm.repository.client.api.ClientCommand;
|
||||||
import sonia.scm.repository.client.api.RepositoryClient;
|
import sonia.scm.repository.client.api.RepositoryClient;
|
||||||
@@ -29,10 +31,10 @@ import static java.lang.Thread.sleep;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static sonia.scm.it.RestUtil.ADMIN_PASSWORD;
|
import static sonia.scm.it.utils.RestUtil.ADMIN_PASSWORD;
|
||||||
import static sonia.scm.it.RestUtil.ADMIN_USERNAME;
|
import static sonia.scm.it.utils.RestUtil.ADMIN_USERNAME;
|
||||||
import static sonia.scm.it.RestUtil.given;
|
import static sonia.scm.it.utils.RestUtil.given;
|
||||||
import static sonia.scm.it.ScmTypes.availableScmTypes;
|
import static sonia.scm.it.utils.ScmTypes.availableScmTypes;
|
||||||
|
|
||||||
@RunWith(Parameterized.class)
|
@RunWith(Parameterized.class)
|
||||||
public class RepositoryAccessITCase {
|
public class RepositoryAccessITCase {
|
||||||
@@ -42,7 +44,7 @@ public class RepositoryAccessITCase {
|
|||||||
|
|
||||||
private final String repositoryType;
|
private final String repositoryType;
|
||||||
private File folder;
|
private File folder;
|
||||||
private RepositoryRequests.AppliedRepositoryGetRequest repositoryGetRequest;
|
private ScmRequests.AppliedRepositoryRequest repositoryGetRequest;
|
||||||
|
|
||||||
public RepositoryAccessITCase(String repositoryType) {
|
public RepositoryAccessITCase(String repositoryType) {
|
||||||
this.repositoryType = repositoryType;
|
this.repositoryType = repositoryType;
|
||||||
@@ -57,12 +59,17 @@ public class RepositoryAccessITCase {
|
|||||||
public void init() {
|
public void init() {
|
||||||
TestData.createDefault();
|
TestData.createDefault();
|
||||||
folder = tempFolder.getRoot();
|
folder = tempFolder.getRoot();
|
||||||
repositoryGetRequest = RepositoryRequests.start()
|
repositoryGetRequest = ScmRequests.start()
|
||||||
.given()
|
.given()
|
||||||
.url(TestData.getDefaultRepositoryUrl(repositoryType))
|
.url(TestData.getDefaultRepositoryUrl(repositoryType))
|
||||||
.usernameAndPassword(ADMIN_USERNAME, ADMIN_PASSWORD)
|
.usernameAndPassword(ADMIN_USERNAME, ADMIN_PASSWORD)
|
||||||
.get()
|
.getRepositoryResource()
|
||||||
.assertStatusCode(HttpStatus.SC_OK);
|
.assertStatusCode(HttpStatus.SC_OK);
|
||||||
|
ScmRequests.AppliedMeRequest meGetRequest = ScmRequests.start()
|
||||||
|
.given()
|
||||||
|
.url(TestData.getMeUrl())
|
||||||
|
.usernameAndPassword(ADMIN_USERNAME, ADMIN_PASSWORD)
|
||||||
|
.getMeResource();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -1,293 +0,0 @@
|
|||||||
package sonia.scm.it;
|
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
|
||||||
import io.restassured.response.Response;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encapsulate rest requests of a repository in builder pattern
|
|
||||||
* <p>
|
|
||||||
* A Get Request can be applied with the methods request*()
|
|
||||||
* These methods return a AppliedGet*Request object
|
|
||||||
* This object can be used to apply general assertions over the rest Assured response
|
|
||||||
* In the AppliedGet*Request classes there is a using*Response() method
|
|
||||||
* that return the *Response class containing specific operations related to the specific response
|
|
||||||
* the *Response class contains also the request*() method to apply the next GET request from a link in the response.
|
|
||||||
*/
|
|
||||||
public class RepositoryRequests {
|
|
||||||
|
|
||||||
private String url;
|
|
||||||
private String username;
|
|
||||||
private String password;
|
|
||||||
|
|
||||||
static RepositoryRequests start() {
|
|
||||||
return new RepositoryRequests();
|
|
||||||
}
|
|
||||||
|
|
||||||
Given given() {
|
|
||||||
return new Given();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply a GET Request to the extracted url from the given link
|
|
||||||
*
|
|
||||||
* @param linkPropertyName the property name of link
|
|
||||||
* @param response the response containing the link
|
|
||||||
* @return the response of the GET request using the given link
|
|
||||||
*/
|
|
||||||
private Response getResponseFromLink(Response response, String linkPropertyName) {
|
|
||||||
return getResponse(response
|
|
||||||
.then()
|
|
||||||
.extract()
|
|
||||||
.path(linkPropertyName));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply a GET Request to the given <code>url</code> and return the response.
|
|
||||||
*
|
|
||||||
* @param url the url of the GET request
|
|
||||||
* @return the response of the GET request using the given <code>url</code>
|
|
||||||
*/
|
|
||||||
private Response getResponse(String url) {
|
|
||||||
return RestAssured.given()
|
|
||||||
.auth().preemptive().basic(username, password)
|
|
||||||
.when()
|
|
||||||
.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setUrl(String url) {
|
|
||||||
this.url = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setUsername(String username) {
|
|
||||||
this.username = username;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setPassword(String password) {
|
|
||||||
this.password = password;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getUrl() {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getUsername() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getPassword() {
|
|
||||||
return password;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Given {
|
|
||||||
|
|
||||||
GivenUrl url(String url) {
|
|
||||||
setUrl(url);
|
|
||||||
return new GivenUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class GivenWithUrlAndAuth {
|
|
||||||
AppliedRepositoryGetRequest get() {
|
|
||||||
return new AppliedRepositoryGetRequest(
|
|
||||||
getResponse(url)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AppliedGetRequest<SELF extends AppliedGetRequest> {
|
|
||||||
private Response response;
|
|
||||||
|
|
||||||
public AppliedGetRequest(Response response) {
|
|
||||||
this.response = response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* apply custom assertions to the actual response
|
|
||||||
*
|
|
||||||
* @param consumer consume the response in order to assert the content. the header, the payload etc..
|
|
||||||
* @return the self object
|
|
||||||
*/
|
|
||||||
SELF assertResponse(Consumer<Response> consumer) {
|
|
||||||
consumer.accept(response);
|
|
||||||
return (SELF) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* special assertion of the status code
|
|
||||||
*
|
|
||||||
* @param expectedStatusCode the expected status code
|
|
||||||
* @return the self object
|
|
||||||
*/
|
|
||||||
SELF assertStatusCode(int expectedStatusCode) {
|
|
||||||
this.response.then().assertThat().statusCode(expectedStatusCode);
|
|
||||||
return (SELF) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class AppliedRepositoryGetRequest extends AppliedGetRequest<AppliedRepositoryGetRequest> {
|
|
||||||
|
|
||||||
AppliedRepositoryGetRequest(Response response) {
|
|
||||||
super(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
RepositoryResponse usingRepositoryResponse() {
|
|
||||||
return new RepositoryResponse(super.response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RepositoryResponse {
|
|
||||||
|
|
||||||
private Response repositoryResponse;
|
|
||||||
|
|
||||||
public RepositoryResponse(Response repositoryResponse) {
|
|
||||||
this.repositoryResponse = repositoryResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppliedGetSourcesRequest requestSources() {
|
|
||||||
return new AppliedGetSourcesRequest(getResponseFromLink(repositoryResponse, "_links.sources.href"));
|
|
||||||
}
|
|
||||||
|
|
||||||
AppliedGetChangesetsRequest requestChangesets() {
|
|
||||||
return new AppliedGetChangesetsRequest(getResponseFromLink(repositoryResponse, "_links.changesets.href"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AppliedGetChangesetsRequest extends AppliedGetRequest<AppliedGetChangesetsRequest> {
|
|
||||||
|
|
||||||
AppliedGetChangesetsRequest(Response response) {
|
|
||||||
super(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
ChangesetsResponse usingChangesetsResponse() {
|
|
||||||
return new ChangesetsResponse(super.response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ChangesetsResponse {
|
|
||||||
private Response changesetsResponse;
|
|
||||||
|
|
||||||
public ChangesetsResponse(Response changesetsResponse) {
|
|
||||||
this.changesetsResponse = changesetsResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
ChangesetsResponse assertChangesets(Consumer<List<Map>> changesetsConsumer) {
|
|
||||||
List<Map> changesets = changesetsResponse.then().extract().path("_embedded.changesets");
|
|
||||||
changesetsConsumer.accept(changesets);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppliedGetDiffRequest requestDiff(String revision) {
|
|
||||||
return new AppliedGetDiffRequest(getResponseFromLink(changesetsResponse, "_embedded.changesets.find{it.id=='" + revision + "'}._links.diff.href"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public AppliedGetModificationsRequest requestModifications(String revision) {
|
|
||||||
return new AppliedGetModificationsRequest(getResponseFromLink(changesetsResponse, "_embedded.changesets.find{it.id=='" + revision + "'}._links.modifications.href"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AppliedGetSourcesRequest extends AppliedGetRequest<AppliedGetSourcesRequest> {
|
|
||||||
|
|
||||||
public AppliedGetSourcesRequest(Response sourcesResponse) {
|
|
||||||
super(sourcesResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
SourcesResponse usingSourcesResponse() {
|
|
||||||
return new SourcesResponse(super.response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SourcesResponse {
|
|
||||||
|
|
||||||
private Response sourcesResponse;
|
|
||||||
|
|
||||||
SourcesResponse(Response sourcesResponse) {
|
|
||||||
this.sourcesResponse = sourcesResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
SourcesResponse assertRevision(Consumer<String> assertRevision) {
|
|
||||||
String revision = sourcesResponse.then().extract().path("revision");
|
|
||||||
assertRevision.accept(revision);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
SourcesResponse assertFiles(Consumer<List> assertFiles) {
|
|
||||||
List files = sourcesResponse.then().extract().path("files");
|
|
||||||
assertFiles.accept(files);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppliedGetChangesetsRequest requestFileHistory(String fileName) {
|
|
||||||
return new AppliedGetChangesetsRequest(getResponseFromLink(sourcesResponse, "_embedded.files.find{it.name=='" + fileName + "'}._links.history.href"));
|
|
||||||
}
|
|
||||||
|
|
||||||
AppliedGetSourcesRequest requestSelf(String fileName) {
|
|
||||||
return new AppliedGetSourcesRequest(getResponseFromLink(sourcesResponse, "_embedded.files.find{it.name=='" + fileName + "'}._links.self.href"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AppliedGetDiffRequest extends AppliedGetRequest<AppliedGetDiffRequest> {
|
|
||||||
|
|
||||||
AppliedGetDiffRequest(Response response) {
|
|
||||||
super(response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GivenUrl {
|
|
||||||
|
|
||||||
GivenWithUrlAndAuth usernameAndPassword(String username, String password) {
|
|
||||||
setUsername(username);
|
|
||||||
setPassword(password);
|
|
||||||
return new GivenWithUrlAndAuth();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AppliedGetModificationsRequest extends AppliedGetRequest<AppliedGetModificationsRequest> {
|
|
||||||
public AppliedGetModificationsRequest(Response response) { super(response); }
|
|
||||||
ModificationsResponse usingModificationsResponse() {
|
|
||||||
return new ModificationsResponse(super.response);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class ModificationsResponse {
|
|
||||||
private Response resource;
|
|
||||||
|
|
||||||
public ModificationsResponse(Response resource) {
|
|
||||||
this.resource = resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
ModificationsResponse assertRevision(Consumer<String> assertRevision) {
|
|
||||||
String revision = resource.then().extract().path("revision");
|
|
||||||
assertRevision.accept(revision);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ModificationsResponse assertAdded(Consumer<List<String>> assertAdded) {
|
|
||||||
List<String > added = resource.then().extract().path("added");
|
|
||||||
assertAdded.accept(added);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ModificationsResponse assertRemoved(Consumer<List<String>> assertRemoved) {
|
|
||||||
List<String > removed = resource.then().extract().path("removed");
|
|
||||||
assertRemoved.accept(removed);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ModificationsResponse assertModified(Consumer<List<String>> assertModified) {
|
|
||||||
List<String > modified = resource.then().extract().path("modified");
|
|
||||||
assertModified.accept(modified);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
113
scm-it/src/test/java/sonia/scm/it/UserITCase.java
Normal file
113
scm-it/src/test/java/sonia/scm/it/UserITCase.java
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package sonia.scm.it;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import sonia.scm.it.utils.ScmRequests;
|
||||||
|
import sonia.scm.it.utils.TestData;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public class UserITCase {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init(){
|
||||||
|
TestData.cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void adminShouldChangeOwnPassword() {
|
||||||
|
String newUser = "user";
|
||||||
|
String password = "pass";
|
||||||
|
TestData.createUser(newUser, password, true, "xml");
|
||||||
|
String newPassword = "new_password";
|
||||||
|
// admin change the own password
|
||||||
|
ScmRequests.start()
|
||||||
|
.given()
|
||||||
|
.url(TestData.getUserUrl(newUser))
|
||||||
|
.usernameAndPassword(newUser, password)
|
||||||
|
.getUserResource()
|
||||||
|
.assertStatusCode(200)
|
||||||
|
.usingUserResponse()
|
||||||
|
.assertAdmin(aBoolean -> assertThat(aBoolean).isEqualTo(Boolean.TRUE))
|
||||||
|
.assertPassword(Assert::assertNull)
|
||||||
|
.requestChangePassword(newPassword) // the oldPassword is not needed in the user resource
|
||||||
|
.assertStatusCode(204);
|
||||||
|
// assert password is changed -> login with the new Password
|
||||||
|
ScmRequests.start()
|
||||||
|
.given()
|
||||||
|
.url(TestData.getUserUrl(newUser))
|
||||||
|
.usernameAndPassword(newUser, newPassword)
|
||||||
|
.getUserResource()
|
||||||
|
.assertStatusCode(200)
|
||||||
|
.usingUserResponse()
|
||||||
|
.assertAdmin(isAdmin -> assertThat(isAdmin).isEqualTo(Boolean.TRUE))
|
||||||
|
.assertPassword(Assert::assertNull);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void adminShouldChangePasswordOfOtherUser() {
|
||||||
|
String newUser = "user";
|
||||||
|
String password = "pass";
|
||||||
|
TestData.createUser(newUser, password, true, "xml");
|
||||||
|
String newPassword = "new_password";
|
||||||
|
// admin change the password of the user
|
||||||
|
ScmRequests.start()
|
||||||
|
.given()
|
||||||
|
.url(TestData.getUserUrl(newUser))// the admin get the user object
|
||||||
|
.usernameAndPassword(TestData.USER_SCM_ADMIN, TestData.USER_SCM_ADMIN)
|
||||||
|
.getUserResource()
|
||||||
|
.assertStatusCode(200)
|
||||||
|
.usingUserResponse()
|
||||||
|
.assertAdmin(aBoolean -> assertThat(aBoolean).isEqualTo(Boolean.TRUE)) // the user anonymous is not an admin
|
||||||
|
.assertPassword(Assert::assertNull)
|
||||||
|
.requestChangePassword(newPassword) // the oldPassword is not needed in the user resource
|
||||||
|
.assertStatusCode(204);
|
||||||
|
// assert password is changed
|
||||||
|
ScmRequests.start()
|
||||||
|
.given()
|
||||||
|
.url(TestData.getUserUrl(newUser))
|
||||||
|
.usernameAndPassword(newUser, newPassword)
|
||||||
|
.getUserResource()
|
||||||
|
.assertStatusCode(200);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldHidePasswordLinkIfUserTypeIsNotXML() {
|
||||||
|
String newUser = "user";
|
||||||
|
String password = "pass";
|
||||||
|
String type = "not XML Type";
|
||||||
|
TestData.createUser(newUser, password, true, type);
|
||||||
|
ScmRequests.start()
|
||||||
|
.given()
|
||||||
|
.url(TestData.getMeUrl())
|
||||||
|
.usernameAndPassword(newUser, password)
|
||||||
|
.getUserResource()
|
||||||
|
.assertStatusCode(200)
|
||||||
|
.usingUserResponse()
|
||||||
|
.assertAdmin(aBoolean -> assertThat(aBoolean).isEqualTo(Boolean.TRUE))
|
||||||
|
.assertPassword(Assert::assertNull)
|
||||||
|
.assertType(s -> assertThat(s).isEqualTo(type))
|
||||||
|
.assertPasswordLinkDoesNotExists();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldGet403IfUserIsNotAdmin() {
|
||||||
|
String newUser = "user";
|
||||||
|
String password = "pass";
|
||||||
|
String type = "xml";
|
||||||
|
TestData.createUser(newUser, password, false, type);
|
||||||
|
ScmRequests.start()
|
||||||
|
.given()
|
||||||
|
.url(TestData.getMeUrl())
|
||||||
|
.usernameAndPassword(newUser, password)
|
||||||
|
.getUserResource()
|
||||||
|
.assertStatusCode(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package sonia.scm.it.utils;
|
||||||
|
|
||||||
|
import javax.json.JsonArrayBuilder;
|
||||||
|
import javax.json.JsonObject;
|
||||||
|
import javax.json.JsonObjectBuilder;
|
||||||
|
import javax.json.JsonValue;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
public class NullAwareJsonObjectBuilder implements JsonObjectBuilder {
|
||||||
|
public static JsonObjectBuilder wrap(JsonObjectBuilder builder) {
|
||||||
|
if (builder == null) {
|
||||||
|
throw new IllegalArgumentException("Can't wrap nothing.");
|
||||||
|
}
|
||||||
|
return new NullAwareJsonObjectBuilder(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final JsonObjectBuilder builder;
|
||||||
|
|
||||||
|
private NullAwareJsonObjectBuilder(JsonObjectBuilder builder) {
|
||||||
|
this.builder = builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonObjectBuilder add(String name, JsonValue value) {
|
||||||
|
return builder.add(name, (value == null) ? JsonValue.NULL : value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonObjectBuilder add(String name, String value) {
|
||||||
|
if (value != null){
|
||||||
|
return builder.add(name, value );
|
||||||
|
}else{
|
||||||
|
return builder.addNull(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonObjectBuilder add(String name, BigInteger value) {
|
||||||
|
if (value != null){
|
||||||
|
return builder.add(name, value );
|
||||||
|
}else{
|
||||||
|
return builder.addNull(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonObjectBuilder add(String name, BigDecimal value) {
|
||||||
|
if (value != null){
|
||||||
|
return builder.add(name, value );
|
||||||
|
}else{
|
||||||
|
return builder.addNull(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonObjectBuilder add(String s, int i) {
|
||||||
|
return builder.add(s, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonObjectBuilder add(String s, long l) {
|
||||||
|
return builder.add(s, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonObjectBuilder add(String s, double v) {
|
||||||
|
return builder.add(s, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonObjectBuilder add(String s, boolean b) {
|
||||||
|
return builder.add(s, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonObjectBuilder addNull(String s) {
|
||||||
|
return builder.addNull(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonObjectBuilder add(String s, JsonObjectBuilder jsonObjectBuilder) {
|
||||||
|
return builder.add(s, jsonObjectBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonObjectBuilder add(String s, JsonArrayBuilder jsonArrayBuilder) {
|
||||||
|
return builder.add(s, jsonArrayBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonObject build() {
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package sonia.scm.it;
|
package sonia.scm.it.utils;
|
||||||
|
|
||||||
import org.hamcrest.BaseMatcher;
|
import org.hamcrest.BaseMatcher;
|
||||||
import org.hamcrest.Description;
|
import org.hamcrest.Description;
|
||||||
@@ -6,7 +6,7 @@ import org.hamcrest.Matcher;
|
|||||||
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
class RegExMatcher extends BaseMatcher<String> {
|
public class RegExMatcher extends BaseMatcher<String> {
|
||||||
public static Matcher<String> matchesPattern(String pattern) {
|
public static Matcher<String> matchesPattern(String pattern) {
|
||||||
return new RegExMatcher(pattern);
|
return new RegExMatcher(pattern);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package sonia.scm.it;
|
package sonia.scm.it.utils;
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
@@ -24,11 +24,11 @@ public class RepositoryUtil {
|
|||||||
|
|
||||||
private static final RepositoryClientFactory REPOSITORY_CLIENT_FACTORY = new RepositoryClientFactory();
|
private static final RepositoryClientFactory REPOSITORY_CLIENT_FACTORY = new RepositoryClientFactory();
|
||||||
|
|
||||||
static RepositoryClient createRepositoryClient(String repositoryType, File folder) throws IOException {
|
public static RepositoryClient createRepositoryClient(String repositoryType, File folder) throws IOException {
|
||||||
return createRepositoryClient(repositoryType, folder, "scmadmin", "scmadmin");
|
return createRepositoryClient(repositoryType, folder, "scmadmin", "scmadmin");
|
||||||
}
|
}
|
||||||
|
|
||||||
static RepositoryClient createRepositoryClient(String repositoryType, File folder, String username, String password) throws IOException {
|
public static RepositoryClient createRepositoryClient(String repositoryType, File folder, String username, String password) throws IOException {
|
||||||
String httpProtocolUrl = TestData.callRepository(username, password, repositoryType, HttpStatus.SC_OK)
|
String httpProtocolUrl = TestData.callRepository(username, password, repositoryType, HttpStatus.SC_OK)
|
||||||
.extract()
|
.extract()
|
||||||
.path("_links.protocol.find{it.name=='http'}.href");
|
.path("_links.protocol.find{it.name=='http'}.href");
|
||||||
@@ -36,14 +36,14 @@ public class RepositoryUtil {
|
|||||||
return REPOSITORY_CLIENT_FACTORY.create(repositoryType, httpProtocolUrl, username, password, folder);
|
return REPOSITORY_CLIENT_FACTORY.create(repositoryType, httpProtocolUrl, username, password, folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
static String addAndCommitRandomFile(RepositoryClient client, String username) throws IOException {
|
public static String addAndCommitRandomFile(RepositoryClient client, String username) throws IOException {
|
||||||
String uuid = UUID.randomUUID().toString();
|
String uuid = UUID.randomUUID().toString();
|
||||||
String name = "file-" + uuid + ".uuid";
|
String name = "file-" + uuid + ".uuid";
|
||||||
createAndCommitFile(client, username, name, uuid);
|
createAndCommitFile(client, username, name, uuid);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Changeset createAndCommitFile(RepositoryClient repositoryClient, String username, String fileName, String content) throws IOException {
|
public static Changeset createAndCommitFile(RepositoryClient repositoryClient, String username, String fileName, String content) throws IOException {
|
||||||
writeAndAddFile(repositoryClient, fileName, content);
|
writeAndAddFile(repositoryClient, fileName, content);
|
||||||
return commit(repositoryClient, username, "added " + fileName);
|
return commit(repositoryClient, username, "added " + fileName);
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ public class RepositoryUtil {
|
|||||||
* @return the changeset with all modifications
|
* @return the changeset with all modifications
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
static Changeset commitMultipleFileModifications(RepositoryClient repositoryClient, String username, Map<String, String> addedFiles, Map<String, String> modifiedFiles, List<String> removedFiles) throws IOException {
|
public static Changeset commitMultipleFileModifications(RepositoryClient repositoryClient, String username, Map<String, String> addedFiles, Map<String, String> modifiedFiles, List<String> removedFiles) throws IOException {
|
||||||
for (String fileName : addedFiles.keySet()) {
|
for (String fileName : addedFiles.keySet()) {
|
||||||
writeAndAddFile(repositoryClient, fileName, addedFiles.get(fileName));
|
writeAndAddFile(repositoryClient, fileName, addedFiles.get(fileName));
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,7 @@ public class RepositoryUtil {
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Changeset removeAndCommitFile(RepositoryClient repositoryClient, String username, String fileName) throws IOException {
|
public static Changeset removeAndCommitFile(RepositoryClient repositoryClient, String username, String fileName) throws IOException {
|
||||||
deleteFileAndApplyRemoveCommand(repositoryClient, fileName);
|
deleteFileAndApplyRemoveCommand(repositoryClient, fileName);
|
||||||
return commit(repositoryClient, username, "removed " + fileName);
|
return commit(repositoryClient, username, "removed " + fileName);
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,7 @@ public class RepositoryUtil {
|
|||||||
return changeset;
|
return changeset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Tag addTag(RepositoryClient repositoryClient, String revision, String tagName) throws IOException {
|
public static Tag addTag(RepositoryClient repositoryClient, String revision, String tagName) throws IOException {
|
||||||
if (repositoryClient.isCommandSupported(ClientCommand.TAG)) {
|
if (repositoryClient.isCommandSupported(ClientCommand.TAG)) {
|
||||||
Tag tag = repositoryClient.getTagCommand().setRevision(revision).tag(tagName, TestData.USER_SCM_ADMIN);
|
Tag tag = repositoryClient.getTagCommand().setRevision(revision).tag(tagName, TestData.USER_SCM_ADMIN);
|
||||||
if (repositoryClient.isCommandSupported(ClientCommand.PUSH)) {
|
if (repositoryClient.isCommandSupported(ClientCommand.PUSH)) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package sonia.scm.it;
|
package sonia.scm.it.utils;
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.specification.RequestSpecification;
|
import io.restassured.specification.RequestSpecification;
|
||||||
465
scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java
Normal file
465
scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java
Normal file
@@ -0,0 +1,465 @@
|
|||||||
|
package sonia.scm.it.utils;
|
||||||
|
|
||||||
|
import io.restassured.RestAssured;
|
||||||
|
import io.restassured.response.Response;
|
||||||
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static sonia.scm.it.utils.TestData.createPasswordChangeJson;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulate rest requests of a repository in builder pattern
|
||||||
|
* <p>
|
||||||
|
* A Get Request can be applied with the methods request*()
|
||||||
|
* These methods return a AppliedGet*Request object
|
||||||
|
* This object can be used to apply general assertions over the rest Assured response
|
||||||
|
* In the AppliedGet*Request classes there is a using*Response() method
|
||||||
|
* that return the *Response class containing specific operations related to the specific response
|
||||||
|
* the *Response class contains also the request*() method to apply the next GET request from a link in the response.
|
||||||
|
*/
|
||||||
|
public class ScmRequests {
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
public static ScmRequests start() {
|
||||||
|
return new ScmRequests();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Given given() {
|
||||||
|
return new Given();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a GET Request to the extracted url from the given link
|
||||||
|
*
|
||||||
|
* @param linkPropertyName the property name of link
|
||||||
|
* @param response the response containing the link
|
||||||
|
* @return the response of the GET request using the given link
|
||||||
|
*/
|
||||||
|
private Response applyGETRequestFromLink(Response response, String linkPropertyName) {
|
||||||
|
return applyGETRequest(response
|
||||||
|
.then()
|
||||||
|
.extract()
|
||||||
|
.path(linkPropertyName));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a GET Request to the given <code>url</code> and return the response.
|
||||||
|
*
|
||||||
|
* @param url the url of the GET request
|
||||||
|
* @return the response of the GET request using the given <code>url</code>
|
||||||
|
*/
|
||||||
|
private Response applyGETRequest(String url) {
|
||||||
|
return RestAssured.given()
|
||||||
|
.auth().preemptive().basic(username, password)
|
||||||
|
.when()
|
||||||
|
.get(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a PUT Request to the extracted url from the given link
|
||||||
|
*
|
||||||
|
* @param response the response containing the link
|
||||||
|
* @param linkPropertyName the property name of link
|
||||||
|
* @param body
|
||||||
|
* @return the response of the PUT request using the given link
|
||||||
|
*/
|
||||||
|
private Response applyPUTRequestFromLink(Response response, String linkPropertyName, String content, String body) {
|
||||||
|
return applyPUTRequest(response
|
||||||
|
.then()
|
||||||
|
.extract()
|
||||||
|
.path(linkPropertyName), content, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a PUT Request to the given <code>url</code> and return the response.
|
||||||
|
*
|
||||||
|
* @param url the url of the PUT request
|
||||||
|
* @param mediaType
|
||||||
|
* @param body
|
||||||
|
* @return the response of the PUT request using the given <code>url</code>
|
||||||
|
*/
|
||||||
|
private Response applyPUTRequest(String url, String mediaType, String body) {
|
||||||
|
return RestAssured.given()
|
||||||
|
.auth().preemptive().basic(username, password)
|
||||||
|
.when()
|
||||||
|
.contentType(mediaType)
|
||||||
|
.accept(mediaType)
|
||||||
|
.body(body)
|
||||||
|
.put(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Given {
|
||||||
|
|
||||||
|
public GivenUrl url(String url) {
|
||||||
|
setUrl(url);
|
||||||
|
return new GivenUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GivenUrl url(URI url) {
|
||||||
|
setUrl(url.toString());
|
||||||
|
return new GivenUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GivenWithUrlAndAuth {
|
||||||
|
public AppliedMeRequest getMeResource() {
|
||||||
|
return new AppliedMeRequest(applyGETRequest(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppliedUserRequest getUserResource() {
|
||||||
|
return new AppliedUserRequest(applyGETRequest(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppliedRepositoryRequest getRepositoryResource() {
|
||||||
|
return new AppliedRepositoryRequest(
|
||||||
|
applyGETRequest(url)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AppliedRequest<SELF extends AppliedRequest> {
|
||||||
|
private Response response;
|
||||||
|
|
||||||
|
public AppliedRequest(Response response) {
|
||||||
|
this.response = response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* apply custom assertions to the actual response
|
||||||
|
*
|
||||||
|
* @param consumer consume the response in order to assert the content. the header, the payload etc..
|
||||||
|
* @return the self object
|
||||||
|
*/
|
||||||
|
public SELF assertResponse(Consumer<Response> consumer) {
|
||||||
|
consumer.accept(response);
|
||||||
|
return (SELF) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* special assertion of the status code
|
||||||
|
*
|
||||||
|
* @param expectedStatusCode the expected status code
|
||||||
|
* @return the self object
|
||||||
|
*/
|
||||||
|
public SELF assertStatusCode(int expectedStatusCode) {
|
||||||
|
this.response.then().assertThat().statusCode(expectedStatusCode);
|
||||||
|
return (SELF) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AppliedRepositoryRequest extends AppliedRequest<AppliedRepositoryRequest> {
|
||||||
|
|
||||||
|
public AppliedRepositoryRequest(Response response) {
|
||||||
|
super(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RepositoryResponse usingRepositoryResponse() {
|
||||||
|
return new RepositoryResponse(super.response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RepositoryResponse {
|
||||||
|
|
||||||
|
private Response repositoryResponse;
|
||||||
|
|
||||||
|
public RepositoryResponse(Response repositoryResponse) {
|
||||||
|
this.repositoryResponse = repositoryResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppliedSourcesRequest requestSources() {
|
||||||
|
return new AppliedSourcesRequest(applyGETRequestFromLink(repositoryResponse, "_links.sources.href"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppliedChangesetsRequest requestChangesets() {
|
||||||
|
return new AppliedChangesetsRequest(applyGETRequestFromLink(repositoryResponse, "_links.changesets.href"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AppliedChangesetsRequest extends AppliedRequest<AppliedChangesetsRequest> {
|
||||||
|
|
||||||
|
public AppliedChangesetsRequest(Response response) {
|
||||||
|
super(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChangesetsResponse usingChangesetsResponse() {
|
||||||
|
return new ChangesetsResponse(super.response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChangesetsResponse {
|
||||||
|
private Response changesetsResponse;
|
||||||
|
|
||||||
|
public ChangesetsResponse(Response changesetsResponse) {
|
||||||
|
this.changesetsResponse = changesetsResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChangesetsResponse assertChangesets(Consumer<List<Map>> changesetsConsumer) {
|
||||||
|
List<Map> changesets = changesetsResponse.then().extract().path("_embedded.changesets");
|
||||||
|
changesetsConsumer.accept(changesets);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppliedDiffRequest requestDiff(String revision) {
|
||||||
|
return new AppliedDiffRequest(applyGETRequestFromLink(changesetsResponse, "_embedded.changesets.find{it.id=='" + revision + "'}._links.diff.href"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppliedModificationsRequest requestModifications(String revision) {
|
||||||
|
return new AppliedModificationsRequest(applyGETRequestFromLink(changesetsResponse, "_embedded.changesets.find{it.id=='" + revision + "'}._links.modifications.href"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AppliedSourcesRequest extends AppliedRequest<AppliedSourcesRequest> {
|
||||||
|
|
||||||
|
public AppliedSourcesRequest(Response sourcesResponse) {
|
||||||
|
super(sourcesResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SourcesResponse usingSourcesResponse() {
|
||||||
|
return new SourcesResponse(super.response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SourcesResponse {
|
||||||
|
|
||||||
|
private Response sourcesResponse;
|
||||||
|
|
||||||
|
public SourcesResponse(Response sourcesResponse) {
|
||||||
|
this.sourcesResponse = sourcesResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SourcesResponse assertRevision(Consumer<String> assertRevision) {
|
||||||
|
String revision = sourcesResponse.then().extract().path("revision");
|
||||||
|
assertRevision.accept(revision);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SourcesResponse assertFiles(Consumer<List> assertFiles) {
|
||||||
|
List files = sourcesResponse.then().extract().path("files");
|
||||||
|
assertFiles.accept(files);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppliedChangesetsRequest requestFileHistory(String fileName) {
|
||||||
|
return new AppliedChangesetsRequest(applyGETRequestFromLink(sourcesResponse, "_embedded.files.find{it.name=='" + fileName + "'}._links.history.href"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppliedSourcesRequest requestSelf(String fileName) {
|
||||||
|
return new AppliedSourcesRequest(applyGETRequestFromLink(sourcesResponse, "_embedded.files.find{it.name=='" + fileName + "'}._links.self.href"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AppliedDiffRequest extends AppliedRequest<AppliedDiffRequest> {
|
||||||
|
|
||||||
|
public AppliedDiffRequest(Response response) {
|
||||||
|
super(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GivenUrl {
|
||||||
|
|
||||||
|
public GivenWithUrlAndAuth usernameAndPassword(String username, String password) {
|
||||||
|
setUsername(username);
|
||||||
|
setPassword(password);
|
||||||
|
return new GivenWithUrlAndAuth();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AppliedModificationsRequest extends AppliedRequest<AppliedModificationsRequest> {
|
||||||
|
public AppliedModificationsRequest(Response response) {
|
||||||
|
super(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModificationsResponse usingModificationsResponse() {
|
||||||
|
return new ModificationsResponse(super.response);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ModificationsResponse {
|
||||||
|
private Response resource;
|
||||||
|
|
||||||
|
public ModificationsResponse(Response resource) {
|
||||||
|
this.resource = resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModificationsResponse assertRevision(Consumer<String> assertRevision) {
|
||||||
|
String revision = resource.then().extract().path("revision");
|
||||||
|
assertRevision.accept(revision);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModificationsResponse assertAdded(Consumer<List<String>> assertAdded) {
|
||||||
|
List<String> added = resource.then().extract().path("added");
|
||||||
|
assertAdded.accept(added);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModificationsResponse assertRemoved(Consumer<List<String>> assertRemoved) {
|
||||||
|
List<String> removed = resource.then().extract().path("removed");
|
||||||
|
assertRemoved.accept(removed);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModificationsResponse assertModified(Consumer<List<String>> assertModified) {
|
||||||
|
List<String> modified = resource.then().extract().path("modified");
|
||||||
|
assertModified.accept(modified);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AppliedMeRequest extends AppliedRequest<AppliedMeRequest> {
|
||||||
|
|
||||||
|
public AppliedMeRequest(Response response) {
|
||||||
|
super(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MeResponse usingMeResponse() {
|
||||||
|
return new MeResponse(super.response);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MeResponse extends UserResponse<MeResponse> {
|
||||||
|
|
||||||
|
|
||||||
|
public MeResponse(Response response) {
|
||||||
|
super(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppliedChangePasswordRequest requestChangePassword(String oldPassword, String newPassword) {
|
||||||
|
return new AppliedChangePasswordRequest(applyPUTRequestFromLink(super.response, "_links.password.href", VndMediaType.PASSWORD_CHANGE, createPasswordChangeJson(oldPassword, newPassword)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UserResponse<SELF extends UserResponse> extends ModelResponse<SELF> {
|
||||||
|
|
||||||
|
public static final String LINKS_PASSWORD_HREF = "_links.password.href";
|
||||||
|
|
||||||
|
public UserResponse(Response response) {
|
||||||
|
super(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF assertPassword(Consumer<String> assertPassword) {
|
||||||
|
return super.assertSingleProperty(assertPassword, "password");
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF assertType(Consumer<String> assertType) {
|
||||||
|
return assertSingleProperty(assertType, "type");
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF assertAdmin(Consumer<Boolean> assertAdmin) {
|
||||||
|
return assertSingleProperty(assertAdmin, "admin");
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF assertPasswordLinkDoesNotExists() {
|
||||||
|
return assertPropertyPathDoesNotExists(LINKS_PASSWORD_HREF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF assertPasswordLinkExists() {
|
||||||
|
return assertPropertyPathExists(LINKS_PASSWORD_HREF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppliedChangePasswordRequest requestChangePassword(String newPassword) {
|
||||||
|
return new AppliedChangePasswordRequest(applyPUTRequestFromLink(super.response, LINKS_PASSWORD_HREF, VndMediaType.PASSWORD_CHANGE, createPasswordChangeJson(null, newPassword)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* encapsulate standard assertions over model properties
|
||||||
|
*/
|
||||||
|
public class ModelResponse<SELF extends ModelResponse> {
|
||||||
|
|
||||||
|
protected Response response;
|
||||||
|
|
||||||
|
public ModelResponse(Response response) {
|
||||||
|
this.response = response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> SELF assertSingleProperty(Consumer<T> assertSingleProperty, String propertyJsonPath) {
|
||||||
|
T propertyValue = response.then().extract().path(propertyJsonPath);
|
||||||
|
assertSingleProperty.accept(propertyValue);
|
||||||
|
return (SELF) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF assertPropertyPathExists(String propertyJsonPath) {
|
||||||
|
response.then().assertThat().body("any { it.containsKey('" + propertyJsonPath + "')}", is(true));
|
||||||
|
return (SELF) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF assertPropertyPathDoesNotExists(String propertyJsonPath) {
|
||||||
|
response.then().assertThat().body("this.any { it.containsKey('" + propertyJsonPath + "')}", is(false));
|
||||||
|
return (SELF) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF assertArrayProperty(Consumer<List> assertProperties, String propertyJsonPath) {
|
||||||
|
List properties = response.then().extract().path(propertyJsonPath);
|
||||||
|
assertProperties.accept(properties);
|
||||||
|
return (SELF) this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AppliedChangePasswordRequest extends AppliedRequest<AppliedChangePasswordRequest> {
|
||||||
|
|
||||||
|
public AppliedChangePasswordRequest(Response response) {
|
||||||
|
super(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AppliedUserRequest extends AppliedRequest<AppliedUserRequest> {
|
||||||
|
|
||||||
|
public AppliedUserRequest(Response response) {
|
||||||
|
super(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserResponse usingUserResponse() {
|
||||||
|
return new UserResponse(super.response);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package sonia.scm.it;
|
package sonia.scm.it.utils;
|
||||||
|
|
||||||
import sonia.scm.util.IOUtil;
|
import sonia.scm.util.IOUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
class ScmTypes {
|
public class ScmTypes {
|
||||||
static Collection<String> availableScmTypes() {
|
public static Collection<String> availableScmTypes() {
|
||||||
Collection<String> params = new ArrayList<>();
|
Collection<String> params = new ArrayList<>();
|
||||||
|
|
||||||
params.add("git");
|
params.add("git");
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package sonia.scm.it;
|
package sonia.scm.it.utils;
|
||||||
|
|
||||||
import io.restassured.response.ValidatableResponse;
|
import io.restassured.response.ValidatableResponse;
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
@@ -8,14 +8,16 @@ import sonia.scm.repository.PermissionType;
|
|||||||
import sonia.scm.web.VndMediaType;
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
import javax.json.Json;
|
import javax.json.Json;
|
||||||
|
import javax.json.JsonObjectBuilder;
|
||||||
|
import java.net.URI;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static sonia.scm.it.RestUtil.createResourceUrl;
|
import static sonia.scm.it.utils.RestUtil.createResourceUrl;
|
||||||
import static sonia.scm.it.RestUtil.given;
|
import static sonia.scm.it.utils.RestUtil.given;
|
||||||
import static sonia.scm.it.ScmTypes.availableScmTypes;
|
import static sonia.scm.it.utils.ScmTypes.availableScmTypes;
|
||||||
|
|
||||||
public class TestData {
|
public class TestData {
|
||||||
|
|
||||||
@@ -26,6 +28,7 @@ public class TestData {
|
|||||||
private static final List<String> PROTECTED_USERS = asList(USER_SCM_ADMIN, USER_ANONYMOUS);
|
private static final List<String> PROTECTED_USERS = asList(USER_SCM_ADMIN, USER_ANONYMOUS);
|
||||||
|
|
||||||
private static Map<String, String> DEFAULT_REPOSITORIES = new HashMap<>();
|
private static Map<String, String> DEFAULT_REPOSITORIES = new HashMap<>();
|
||||||
|
public static final JsonObjectBuilder JSON_BUILDER = NullAwareJsonObjectBuilder.wrap(Json.createObjectBuilder());
|
||||||
|
|
||||||
public static void createDefault() {
|
public static void createDefault() {
|
||||||
cleanup();
|
cleanup();
|
||||||
@@ -44,27 +47,31 @@ public class TestData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void createUser(String username, String password) {
|
public static void createUser(String username, String password) {
|
||||||
|
createUser(username, password, false, "xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void createUser(String username, String password, boolean isAdmin, String type) {
|
||||||
LOG.info("create user with username: {}", username);
|
LOG.info("create user with username: {}", username);
|
||||||
|
String admin = isAdmin ? "true" : "false";
|
||||||
given(VndMediaType.USER)
|
given(VndMediaType.USER)
|
||||||
.when()
|
.when()
|
||||||
.content(" {\n" +
|
.content(new StringBuilder()
|
||||||
" \"active\": true,\n" +
|
.append(" {\n")
|
||||||
" \"admin\": false,\n" +
|
.append(" \"active\": true,\n")
|
||||||
" \"creationDate\": \"2018-08-21T12:26:46.084Z\",\n" +
|
.append(" \"admin\": ").append(admin).append(",\n")
|
||||||
" \"displayName\": \"" + username + "\",\n" +
|
.append(" \"creationDate\": \"2018-08-21T12:26:46.084Z\",\n")
|
||||||
" \"mail\": \"user1@scm-manager.org\",\n" +
|
.append(" \"displayName\": \"").append(username).append("\",\n")
|
||||||
" \"name\": \"" + username + "\",\n" +
|
.append(" \"mail\": \"user1@scm-manager.org\",\n")
|
||||||
" \"password\": \"" + password + "\",\n" +
|
.append(" \"name\": \"").append(username).append("\",\n")
|
||||||
" \"type\": \"xml\"\n" +
|
.append(" \"password\": \"").append(password).append("\",\n")
|
||||||
" \n" +
|
.append(" \"type\": \"").append(type).append("\"\n")
|
||||||
" }")
|
.append(" }").toString())
|
||||||
.post(createResourceUrl("users"))
|
.post(getUsersUrl())
|
||||||
.then()
|
.then()
|
||||||
.statusCode(HttpStatus.SC_CREATED)
|
.statusCode(HttpStatus.SC_CREATED)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void createUserPermission(String name, PermissionType permissionType, String repositoryType) {
|
public static void createUserPermission(String name, PermissionType permissionType, String repositoryType) {
|
||||||
String defaultPermissionUrl = TestData.getDefaultPermissionUrl(USER_SCM_ADMIN, USER_SCM_ADMIN, repositoryType);
|
String defaultPermissionUrl = TestData.getDefaultPermissionUrl(USER_SCM_ADMIN, USER_SCM_ADMIN, repositoryType);
|
||||||
LOG.info("create permission with name {} and type: {} using the endpoint: {}", name, permissionType, defaultPermissionUrl);
|
LOG.info("create permission with name {} and type: {} using the endpoint: {}", name, permissionType, defaultPermissionUrl);
|
||||||
@@ -183,7 +190,7 @@ public class TestData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String repositoryJson(String repositoryType) {
|
public static String repositoryJson(String repositoryType) {
|
||||||
return Json.createObjectBuilder()
|
return JSON_BUILDER
|
||||||
.add("contact", "zaphod.beeblebrox@hitchhiker.com")
|
.add("contact", "zaphod.beeblebrox@hitchhiker.com")
|
||||||
.add("description", "Heart of Gold")
|
.add("description", "Heart of Gold")
|
||||||
.add("name", "HeartOfGold-" + repositoryType)
|
.add("name", "HeartOfGold-" + repositoryType)
|
||||||
@@ -192,6 +199,29 @@ public class TestData {
|
|||||||
.build().toString();
|
.build().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static URI getMeUrl() {
|
||||||
|
return RestUtil.createResourceUrl("me/");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static URI getUsersUrl() {
|
||||||
|
return RestUtil.createResourceUrl("users/");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static URI getUserUrl(String username) {
|
||||||
|
return getUsersUrl().resolve(username);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String createPasswordChangeJson(String oldPassword, String newPassword) {
|
||||||
|
return JSON_BUILDER
|
||||||
|
.add("oldPassword", oldPassword)
|
||||||
|
.add("newPassword", newPassword)
|
||||||
|
.build().toString();
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
@@ -278,7 +278,6 @@ public class GitRepositoryViewer
|
|||||||
{
|
{
|
||||||
//J-
|
//J-
|
||||||
ChangesetPagingResult cpr = service.getLogCommand()
|
ChangesetPagingResult cpr = service.getLogCommand()
|
||||||
.setDisableEscaping(true)
|
|
||||||
.setBranch(name)
|
.setBranch(name)
|
||||||
.setPagingLimit(CHANGESET_PER_BRANCH)
|
.setPagingLimit(CHANGESET_PER_BRANCH)
|
||||||
.getChangesets();
|
.getChangesets();
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ const userZaphod = {
|
|||||||
displayName: "Z. Beeblebrox",
|
displayName: "Z. Beeblebrox",
|
||||||
mail: "president@heartofgold.universe",
|
mail: "president@heartofgold.universe",
|
||||||
name: "zaphod",
|
name: "zaphod",
|
||||||
password: "__dummypassword__",
|
password: "",
|
||||||
type: "xml",
|
type: "xml",
|
||||||
properties: {},
|
properties: {},
|
||||||
_links: {
|
_links: {
|
||||||
@@ -79,7 +79,7 @@ const userFord = {
|
|||||||
displayName: "F. Prefect",
|
displayName: "F. Prefect",
|
||||||
mail: "ford@prefect.universe",
|
mail: "ford@prefect.universe",
|
||||||
name: "ford",
|
name: "ford",
|
||||||
password: "__dummypassword__",
|
password: "",
|
||||||
type: "xml",
|
type: "xml",
|
||||||
properties: {},
|
properties: {},
|
||||||
_links: {
|
_links: {
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import sonia.scm.user.ChangePasswordNotAllowedException;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.ext.ExceptionMapper;
|
||||||
|
import javax.ws.rs.ext.Provider;
|
||||||
|
|
||||||
|
@Provider
|
||||||
|
public class ChangePasswordNotAllowedExceptionMapper implements ExceptionMapper<ChangePasswordNotAllowedException> {
|
||||||
|
@Override
|
||||||
|
public Response toResponse(ChangePasswordNotAllowedException exception) {
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity(exception.getMessage())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import sonia.scm.PageResult;
|
|||||||
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@@ -37,6 +38,15 @@ class IdResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
|
|||||||
return singleAdapter.get(loadBy(id), mapToDto);
|
return singleAdapter.get(loadBy(id), mapToDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Response update(String id, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges, Consumer<MODEL_OBJECT> checker) throws NotFoundException, ConcurrentModificationException {
|
||||||
|
return singleAdapter.update(
|
||||||
|
loadBy(id),
|
||||||
|
applyChanges,
|
||||||
|
idStaysTheSame(id),
|
||||||
|
checker
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public Response update(String id, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges) throws NotFoundException, ConcurrentModificationException {
|
public Response update(String id, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges) throws NotFoundException, ConcurrentModificationException {
|
||||||
return singleAdapter.update(
|
return singleAdapter.update(
|
||||||
loadBy(id),
|
loadBy(id),
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import sonia.scm.user.InvalidPasswordException;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.ext.ExceptionMapper;
|
||||||
|
import javax.ws.rs.ext.Provider;
|
||||||
|
|
||||||
|
@Provider
|
||||||
|
public class InvalidPasswordExceptionMapper implements ExceptionMapper<InvalidPasswordException> {
|
||||||
|
@Override
|
||||||
|
public Response toResponse(InvalidPasswordException exception) {
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity(exception.getMessage())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ public class MapperModule extends AbstractModule {
|
|||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bind(UserDtoToUserMapper.class).to(Mappers.getMapper(UserDtoToUserMapper.class).getClass());
|
bind(UserDtoToUserMapper.class).to(Mappers.getMapper(UserDtoToUserMapper.class).getClass());
|
||||||
|
bind(MeToUserDtoMapper.class).to(Mappers.getMapper(MeToUserDtoMapper.class).getClass());
|
||||||
bind(UserToUserDtoMapper.class).to(Mappers.getMapper(UserToUserDtoMapper.class).getClass());
|
bind(UserToUserDtoMapper.class).to(Mappers.getMapper(UserToUserDtoMapper.class).getClass());
|
||||||
bind(UserCollectionToDtoMapper.class);
|
bind(UserCollectionToDtoMapper.class);
|
||||||
|
|
||||||
|
|||||||
@@ -4,19 +4,27 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
|||||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
||||||
import org.apache.shiro.SecurityUtils;
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
import org.apache.shiro.authc.credential.PasswordService;
|
||||||
|
import sonia.scm.ConcurrentModificationException;
|
||||||
import sonia.scm.NotFoundException;
|
import sonia.scm.NotFoundException;
|
||||||
|
import sonia.scm.user.InvalidPasswordException;
|
||||||
import sonia.scm.user.User;
|
import sonia.scm.user.User;
|
||||||
import sonia.scm.user.UserManager;
|
import sonia.scm.user.UserManager;
|
||||||
import sonia.scm.web.VndMediaType;
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.Request;
|
import javax.ws.rs.core.Request;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import static sonia.scm.user.InvalidPasswordException.INVALID_MATCHING;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,15 +32,20 @@ import javax.ws.rs.core.UriInfo;
|
|||||||
*/
|
*/
|
||||||
@Path(MeResource.ME_PATH_V2)
|
@Path(MeResource.ME_PATH_V2)
|
||||||
public class MeResource {
|
public class MeResource {
|
||||||
static final String ME_PATH_V2 = "v2/me/";
|
public static final String ME_PATH_V2 = "v2/me/";
|
||||||
|
|
||||||
private final UserToUserDtoMapper userToDtoMapper;
|
private final MeToUserDtoMapper meToUserDtoMapper;
|
||||||
|
|
||||||
private final IdResourceManagerAdapter<User, UserDto> adapter;
|
private final IdResourceManagerAdapter<User, UserDto> adapter;
|
||||||
|
private final PasswordService passwordService;
|
||||||
|
private final UserManager userManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public MeResource(UserToUserDtoMapper userToDtoMapper, UserManager manager) {
|
public MeResource(MeToUserDtoMapper meToUserDtoMapper, UserManager manager, PasswordService passwordService) {
|
||||||
this.userToDtoMapper = userToDtoMapper;
|
this.meToUserDtoMapper = meToUserDtoMapper;
|
||||||
this.adapter = new IdResourceManagerAdapter<>(manager, User.class);
|
this.adapter = new IdResourceManagerAdapter<>(manager, User.class);
|
||||||
|
this.passwordService = passwordService;
|
||||||
|
this.userManager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,6 +63,34 @@ public class MeResource {
|
|||||||
public Response get(@Context Request request, @Context UriInfo uriInfo) throws NotFoundException {
|
public Response get(@Context Request request, @Context UriInfo uriInfo) throws NotFoundException {
|
||||||
|
|
||||||
String id = (String) SecurityUtils.getSubject().getPrincipals().getPrimaryPrincipal();
|
String id = (String) SecurityUtils.getSubject().getPrincipals().getPrimaryPrincipal();
|
||||||
return adapter.get(id, userToDtoMapper::map);
|
return adapter.get(id, meToUserDtoMapper::map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change password of the current user
|
||||||
|
*/
|
||||||
|
@PUT
|
||||||
|
@Path("password")
|
||||||
|
@StatusCodes({
|
||||||
|
@ResponseCode(code = 204, condition = "update success"),
|
||||||
|
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||||
|
@ResponseCode(code = 500, condition = "internal server error")
|
||||||
|
})
|
||||||
|
@TypeHint(TypeHint.NO_CONTENT.class)
|
||||||
|
@Consumes(VndMediaType.PASSWORD_CHANGE)
|
||||||
|
public Response changePassword(PasswordChangeDto passwordChangeDto) throws NotFoundException, ConcurrentModificationException {
|
||||||
|
String name = (String) SecurityUtils.getSubject().getPrincipals().getPrimaryPrincipal();
|
||||||
|
return adapter.update(name, user -> user.changePassword(passwordService.encryptPassword(passwordChangeDto.getNewPassword())), userManager.getUserTypeChecker().andThen(getOldOriginalPasswordChecker(passwordChangeDto.getOldPassword())));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match given old password from the dto with the stored password before updating
|
||||||
|
*/
|
||||||
|
private Consumer<User> getOldOriginalPasswordChecker(String oldPassword) {
|
||||||
|
return user -> {
|
||||||
|
if (!user.getPassword().equals(passwordService.encryptPassword(oldPassword))) {
|
||||||
|
throw new InvalidPasswordException(INVALID_MATCHING);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import de.otto.edison.hal.Links;
|
||||||
|
import org.mapstruct.AfterMapping;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
import sonia.scm.user.User;
|
||||||
|
import sonia.scm.user.UserManager;
|
||||||
|
import sonia.scm.user.UserPermissions;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static de.otto.edison.hal.Link.link;
|
||||||
|
import static de.otto.edison.hal.Links.linkingTo;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public abstract class MeToUserDtoMapper extends UserToUserDtoMapper{
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private UserManager userManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ResourceLinks resourceLinks;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@AfterMapping
|
||||||
|
protected void appendLinks(User user, @MappingTarget UserDto target) {
|
||||||
|
Links.Builder linksBuilder = linkingTo().self(resourceLinks.me().self());
|
||||||
|
if (UserPermissions.delete(user).isPermitted()) {
|
||||||
|
linksBuilder.single(link("delete", resourceLinks.me().delete(target.getName())));
|
||||||
|
}
|
||||||
|
if (UserPermissions.modify(user).isPermitted()) {
|
||||||
|
linksBuilder.single(link("update", resourceLinks.me().update(target.getName())));
|
||||||
|
}
|
||||||
|
if (userManager.isTypeDefault(user)) {
|
||||||
|
linksBuilder.single(link("password", resourceLinks.me().passwordChange()));
|
||||||
|
}
|
||||||
|
target.add(linksBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
import org.hibernate.validator.constraints.NotEmpty;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@ToString
|
||||||
|
public class PasswordChangeDto {
|
||||||
|
|
||||||
|
private String oldPassword;
|
||||||
|
|
||||||
|
@NotEmpty
|
||||||
|
private String newPassword;
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package sonia.scm.api.v2.resources;
|
|||||||
import sonia.scm.repository.NamespaceAndName;
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
class ResourceLinks {
|
class ResourceLinks {
|
||||||
@@ -85,7 +86,42 @@ class ResourceLinks {
|
|||||||
String update(String name) {
|
String update(String name) {
|
||||||
return userLinkBuilder.method("getUserResource").parameters(name).method("update").parameters().href();
|
return userLinkBuilder.method("getUserResource").parameters(name).method("update").parameters().href();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String passwordChange(String name) {
|
||||||
|
return userLinkBuilder.method("getUserResource").parameters(name).method("changePassword").parameters().href();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MeLinks me() {
|
||||||
|
return new MeLinks(scmPathInfoStore.get(), this.user());
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MeLinks {
|
||||||
|
private final LinkBuilder meLinkBuilder;
|
||||||
|
private UserLinks userLinks;
|
||||||
|
|
||||||
|
MeLinks(ScmPathInfo pathInfo, UserLinks user) {
|
||||||
|
meLinkBuilder = new LinkBuilder(pathInfo, MeResource.class);
|
||||||
|
userLinks = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
String self() {
|
||||||
|
return meLinkBuilder.method("get").parameters().href();
|
||||||
|
}
|
||||||
|
|
||||||
|
String delete(String name) {
|
||||||
|
return userLinks.delete(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
String update(String name) {
|
||||||
|
return userLinks.update(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String passwordChange() {
|
||||||
|
return meLinkBuilder.method("changePassword").parameters().href();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
UserCollectionLinks userCollection() {
|
UserCollectionLinks userCollection() {
|
||||||
return new UserCollectionLinks(scmPathInfoStore.get());
|
return new UserCollectionLinks(scmPathInfoStore.get());
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import javax.ws.rs.core.GenericEntity;
|
|||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@@ -53,6 +54,11 @@ class SingleResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
|
|||||||
.map(Response.ResponseBuilder::build)
|
.map(Response.ResponseBuilder::build)
|
||||||
.orElseThrow(NotFoundException::new);
|
.orElseThrow(NotFoundException::new);
|
||||||
}
|
}
|
||||||
|
public Response update(Supplier<Optional<MODEL_OBJECT>> reader, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges, Predicate<MODEL_OBJECT> hasSameKey, Consumer<MODEL_OBJECT> checker) throws NotFoundException, ConcurrentModificationException {
|
||||||
|
MODEL_OBJECT existingModelObject = reader.get().orElseThrow(NotFoundException::new);
|
||||||
|
checker.accept(existingModelObject);
|
||||||
|
return update(reader,applyChanges,hasSameKey);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the model object for the given id according to the given function and returns a corresponding http response.
|
* Update the model object for the given id according to the given function and returns a corresponding http response.
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.webcohesion.enunciate.metadata.rs.ResponseHeader;
|
|||||||
import com.webcohesion.enunciate.metadata.rs.ResponseHeaders;
|
import com.webcohesion.enunciate.metadata.rs.ResponseHeaders;
|
||||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
||||||
|
import org.apache.shiro.authc.credential.PasswordService;
|
||||||
import sonia.scm.AlreadyExistsException;
|
import sonia.scm.AlreadyExistsException;
|
||||||
import sonia.scm.user.User;
|
import sonia.scm.user.User;
|
||||||
import sonia.scm.user.UserManager;
|
import sonia.scm.user.UserManager;
|
||||||
@@ -29,14 +30,16 @@ public class UserCollectionResource {
|
|||||||
private final ResourceLinks resourceLinks;
|
private final ResourceLinks resourceLinks;
|
||||||
|
|
||||||
private final IdResourceManagerAdapter<User, UserDto> adapter;
|
private final IdResourceManagerAdapter<User, UserDto> adapter;
|
||||||
|
private final PasswordService passwordService;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public UserCollectionResource(UserManager manager, UserDtoToUserMapper dtoToUserMapper,
|
public UserCollectionResource(UserManager manager, UserDtoToUserMapper dtoToUserMapper,
|
||||||
UserCollectionToDtoMapper userCollectionToDtoMapper, ResourceLinks resourceLinks) {
|
UserCollectionToDtoMapper userCollectionToDtoMapper, ResourceLinks resourceLinks, PasswordService passwordService) {
|
||||||
this.dtoToUserMapper = dtoToUserMapper;
|
this.dtoToUserMapper = dtoToUserMapper;
|
||||||
this.userCollectionToDtoMapper = userCollectionToDtoMapper;
|
this.userCollectionToDtoMapper = userCollectionToDtoMapper;
|
||||||
this.adapter = new IdResourceManagerAdapter<>(manager, User.class);
|
this.adapter = new IdResourceManagerAdapter<>(manager, User.class);
|
||||||
this.resourceLinks = resourceLinks;
|
this.resourceLinks = resourceLinks;
|
||||||
|
this.passwordService = passwordService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,8 +92,6 @@ public class UserCollectionResource {
|
|||||||
@TypeHint(TypeHint.NO_CONTENT.class)
|
@TypeHint(TypeHint.NO_CONTENT.class)
|
||||||
@ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created user"))
|
@ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created user"))
|
||||||
public Response create(@Valid UserDto userDto) throws AlreadyExistsException {
|
public Response create(@Valid UserDto userDto) throws AlreadyExistsException {
|
||||||
return adapter.create(userDto,
|
return adapter.create(userDto, () -> dtoToUserMapper.map(userDto, passwordService.encryptPassword(userDto.getPassword())), user -> resourceLinks.user().self(user.getName()));
|
||||||
() -> dtoToUserMapper.map(userDto, ""),
|
|
||||||
user -> resourceLinks.user().self(user.getName()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public class UserDto extends HalRepresentation {
|
|||||||
private String mail;
|
private String mail;
|
||||||
@Pattern(regexp = "^[A-z0-9\\.\\-_@]|[^ ]([A-z0-9\\.\\-_@ ]*[A-z0-9\\.\\-_@]|[^ ])?$")
|
@Pattern(regexp = "^[A-z0-9\\.\\-_@]|[^ ]([A-z0-9\\.\\-_@ ]*[A-z0-9\\.\\-_@]|[^ ])?$")
|
||||||
private String name;
|
private String name;
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
private String password;
|
private String password;
|
||||||
private String type;
|
private String type;
|
||||||
private Map<String, String> properties;
|
private Map<String, String> properties;
|
||||||
|
|||||||
@@ -1,37 +1,35 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
import org.apache.shiro.authc.credential.PasswordService;
|
import org.mapstruct.AfterMapping;
|
||||||
import org.mapstruct.Context;
|
import org.mapstruct.Context;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.Named;
|
import org.mapstruct.MappingTarget;
|
||||||
import sonia.scm.user.User;
|
import sonia.scm.user.User;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
|
|
||||||
import static sonia.scm.api.rest.resources.UserResource.DUMMY_PASSWORT;
|
|
||||||
|
|
||||||
// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection.
|
// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection.
|
||||||
@SuppressWarnings("squid:S3306")
|
@SuppressWarnings("squid:S3306")
|
||||||
@Mapper
|
@Mapper
|
||||||
public abstract class UserDtoToUserMapper extends BaseDtoMapper {
|
public abstract class UserDtoToUserMapper extends BaseDtoMapper {
|
||||||
|
|
||||||
@Inject
|
|
||||||
private PasswordService passwordService;
|
|
||||||
|
|
||||||
@Mapping(source = "password", target = "password", qualifiedByName = "encrypt")
|
|
||||||
@Mapping(target = "creationDate", ignore = true)
|
@Mapping(target = "creationDate", ignore = true)
|
||||||
public abstract User map(UserDto userDto, @Context String originalPassword);
|
public abstract User map(UserDto userDto, @Context String usedPassword);
|
||||||
|
|
||||||
@Named("encrypt")
|
|
||||||
String encrypt(String password, @Context String originalPassword) {
|
|
||||||
|
|
||||||
if (DUMMY_PASSWORT.equals(password)) {
|
/**
|
||||||
return originalPassword;
|
* depends on the use case the right password will be mapped.
|
||||||
} else {
|
* The given Password in the context parameter will be set.
|
||||||
return passwordService.encryptPassword(password);
|
* The mapper consumer have the control of what password should be set.
|
||||||
}
|
* </p>
|
||||||
|
* eg. for update user action the password will be set to the original password
|
||||||
|
* for create user and change password actions the password is the user input
|
||||||
|
*
|
||||||
|
* @param usedPassword the password to be set
|
||||||
|
* @param user the target
|
||||||
|
*/
|
||||||
|
@AfterMapping
|
||||||
|
void overridePassword(@MappingTarget User user, @Context String usedPassword) {
|
||||||
|
user.setPassword(usedPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package sonia.scm.api.v2.resources;
|
|||||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
||||||
|
import org.apache.shiro.authc.credential.PasswordService;
|
||||||
import sonia.scm.ConcurrentModificationException;
|
import sonia.scm.ConcurrentModificationException;
|
||||||
import sonia.scm.NotFoundException;
|
import sonia.scm.NotFoundException;
|
||||||
import sonia.scm.user.User;
|
import sonia.scm.user.User;
|
||||||
@@ -26,12 +27,16 @@ public class UserResource {
|
|||||||
private final UserToUserDtoMapper userToDtoMapper;
|
private final UserToUserDtoMapper userToDtoMapper;
|
||||||
|
|
||||||
private final IdResourceManagerAdapter<User, UserDto> adapter;
|
private final IdResourceManagerAdapter<User, UserDto> adapter;
|
||||||
|
private final UserManager userManager;
|
||||||
|
private final PasswordService passwordService;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public UserResource(UserDtoToUserMapper dtoToUserMapper, UserToUserDtoMapper userToDtoMapper, UserManager manager) {
|
public UserResource(UserDtoToUserMapper dtoToUserMapper, UserToUserDtoMapper userToDtoMapper, UserManager manager, PasswordService passwordService) {
|
||||||
this.dtoToUserMapper = dtoToUserMapper;
|
this.dtoToUserMapper = dtoToUserMapper;
|
||||||
this.userToDtoMapper = userToDtoMapper;
|
this.userToDtoMapper = userToDtoMapper;
|
||||||
this.adapter = new IdResourceManagerAdapter<>(manager, User.class);
|
this.adapter = new IdResourceManagerAdapter<>(manager, User.class);
|
||||||
|
this.userManager = manager;
|
||||||
|
this.passwordService = passwordService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,7 +45,6 @@ public class UserResource {
|
|||||||
* <strong>Note:</strong> This method requires "user" privilege.
|
* <strong>Note:</strong> This method requires "user" privilege.
|
||||||
*
|
*
|
||||||
* @param id the id/name of the user
|
* @param id the id/name of the user
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Path("")
|
@Path("")
|
||||||
@@ -63,7 +67,6 @@ public class UserResource {
|
|||||||
* <strong>Note:</strong> This method requires "user" privilege.
|
* <strong>Note:</strong> This method requires "user" privilege.
|
||||||
*
|
*
|
||||||
* @param name the name of the user to delete.
|
* @param name the name of the user to delete.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@DELETE
|
@DELETE
|
||||||
@Path("")
|
@Path("")
|
||||||
@@ -80,6 +83,7 @@ public class UserResource {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Modifies the given user.
|
* Modifies the given user.
|
||||||
|
* The given Password in the payload will be ignored. To Change Password use the changePassword endpoint
|
||||||
*
|
*
|
||||||
* <strong>Note:</strong> This method requires "user" privilege.
|
* <strong>Note:</strong> This method requires "user" privilege.
|
||||||
*
|
*
|
||||||
@@ -101,4 +105,30 @@ public class UserResource {
|
|||||||
public Response update(@PathParam("id") String name, @Valid UserDto userDto) throws NotFoundException, ConcurrentModificationException {
|
public Response update(@PathParam("id") String name, @Valid UserDto userDto) throws NotFoundException, ConcurrentModificationException {
|
||||||
return adapter.update(name, existing -> dtoToUserMapper.map(userDto, existing.getPassword()));
|
return adapter.update(name, existing -> dtoToUserMapper.map(userDto, existing.getPassword()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Endpoint is for Admin user to modify a user password.
|
||||||
|
* The oldPassword property of the DTO is not needed here. it will be ignored.
|
||||||
|
* The oldPassword property is needed in the MeResources when the actual user change the own password.
|
||||||
|
*
|
||||||
|
* <strong>Note:</strong> This method requires "user:modify" privilege.
|
||||||
|
* @param name name of the user to be modified
|
||||||
|
* @param passwordChangeDto change password object to modify password. the old password is here not required
|
||||||
|
*/
|
||||||
|
@PUT
|
||||||
|
@Path("password")
|
||||||
|
@Consumes(VndMediaType.PASSWORD_CHANGE)
|
||||||
|
@StatusCodes({
|
||||||
|
@ResponseCode(code = 204, condition = "update success"),
|
||||||
|
@ResponseCode(code = 400, condition = "Invalid body, e.g. the user type is not xml or the given oldPassword do not match the stored one"),
|
||||||
|
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||||
|
@ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"user\" privilege"),
|
||||||
|
@ResponseCode(code = 404, condition = "not found, no user with the specified id/name available"),
|
||||||
|
@ResponseCode(code = 500, condition = "internal server error")
|
||||||
|
})
|
||||||
|
@TypeHint(TypeHint.NO_CONTENT.class)
|
||||||
|
public Response changePassword(@PathParam("id") String name, @Valid PasswordChangeDto passwordChangeDto) throws NotFoundException, ConcurrentModificationException {
|
||||||
|
return adapter.update(name, user -> user.changePassword(passwordService.encryptPassword(passwordChangeDto.getNewPassword())), userManager.getUserTypeChecker());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import com.google.common.annotations.VisibleForTesting;
|
|||||||
import de.otto.edison.hal.Links;
|
import de.otto.edison.hal.Links;
|
||||||
import org.mapstruct.AfterMapping;
|
import org.mapstruct.AfterMapping;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.MappingTarget;
|
import org.mapstruct.MappingTarget;
|
||||||
import sonia.scm.api.rest.resources.UserResource;
|
|
||||||
import sonia.scm.user.User;
|
import sonia.scm.user.User;
|
||||||
|
import sonia.scm.user.UserManager;
|
||||||
import sonia.scm.user.UserPermissions;
|
import sonia.scm.user.UserPermissions;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -19,19 +20,17 @@ import static de.otto.edison.hal.Links.linkingTo;
|
|||||||
@Mapper
|
@Mapper
|
||||||
public abstract class UserToUserDtoMapper extends BaseMapper<User, UserDto> {
|
public abstract class UserToUserDtoMapper extends BaseMapper<User, UserDto> {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private UserManager userManager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Mapping(target = "attributes", ignore = true)
|
||||||
|
@Mapping(target = "password", ignore = true)
|
||||||
|
public abstract UserDto map(User modelObject);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private ResourceLinks resourceLinks;
|
private ResourceLinks resourceLinks;
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void setResourceLinks(ResourceLinks resourceLinks) {
|
|
||||||
this.resourceLinks = resourceLinks;
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterMapping
|
|
||||||
void removePassword(@MappingTarget UserDto target) {
|
|
||||||
target.setPassword(UserResource.DUMMY_PASSWORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterMapping
|
@AfterMapping
|
||||||
protected void appendLinks(User user, @MappingTarget UserDto target) {
|
protected void appendLinks(User user, @MappingTarget UserDto target) {
|
||||||
Links.Builder linksBuilder = linkingTo().self(resourceLinks.user().self(target.getName()));
|
Links.Builder linksBuilder = linkingTo().self(resourceLinks.user().self(target.getName()));
|
||||||
@@ -41,6 +40,9 @@ public abstract class UserToUserDtoMapper extends BaseMapper<User, UserDto> {
|
|||||||
if (UserPermissions.modify(user).isPermitted()) {
|
if (UserPermissions.modify(user).isPermitted()) {
|
||||||
linksBuilder.single(link("update", resourceLinks.user().update(target.getName())));
|
linksBuilder.single(link("update", resourceLinks.user().update(target.getName())));
|
||||||
}
|
}
|
||||||
|
if (userManager.isTypeDefault(user)) {
|
||||||
|
linksBuilder.single(link("password", resourceLinks.user().passwordChange(target.getName())));
|
||||||
|
}
|
||||||
target.add(linksBuilder.build());
|
target.add(linksBuilder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ public class DispatcherMock {
|
|||||||
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
|
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
|
||||||
dispatcher.getProviderFactory().registerProvider(ConcurrentModificationExceptionMapper.class);
|
dispatcher.getProviderFactory().registerProvider(ConcurrentModificationExceptionMapper.class);
|
||||||
dispatcher.getProviderFactory().registerProvider(InternalRepositoryExceptionMapper.class);
|
dispatcher.getProviderFactory().registerProvider(InternalRepositoryExceptionMapper.class);
|
||||||
|
dispatcher.getProviderFactory().registerProvider(ChangePasswordNotAllowedExceptionMapper.class);
|
||||||
|
dispatcher.getProviderFactory().registerProvider(InvalidPasswordExceptionMapper.class);
|
||||||
return dispatcher;
|
return dispatcher;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ package sonia.scm.api.v2.resources;
|
|||||||
|
|
||||||
import com.github.sdorra.shiro.ShiroRule;
|
import com.github.sdorra.shiro.ShiroRule;
|
||||||
import com.github.sdorra.shiro.SubjectAware;
|
import com.github.sdorra.shiro.SubjectAware;
|
||||||
|
import org.apache.shiro.authc.credential.PasswordService;
|
||||||
import org.jboss.resteasy.core.Dispatcher;
|
import org.jboss.resteasy.core.Dispatcher;
|
||||||
import org.jboss.resteasy.mock.MockDispatcherFactory;
|
|
||||||
import org.jboss.resteasy.mock.MockHttpRequest;
|
import org.jboss.resteasy.mock.MockHttpRequest;
|
||||||
import org.jboss.resteasy.mock.MockHttpResponse;
|
import org.jboss.resteasy.mock.MockHttpResponse;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
@@ -22,11 +23,17 @@ import java.net.URISyntaxException;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.doNothing;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.mockito.MockitoAnnotations.initMocks;
|
import static org.mockito.MockitoAnnotations.initMocks;
|
||||||
|
import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher;
|
||||||
|
|
||||||
@SubjectAware(
|
@SubjectAware(
|
||||||
|
username = "trillian",
|
||||||
|
password = "secret",
|
||||||
configuration = "classpath:sonia/scm/repository/shiro.ini"
|
configuration = "classpath:sonia/scm/repository/shiro.ini"
|
||||||
)
|
)
|
||||||
public class MeResourceTest {
|
public class MeResourceTest {
|
||||||
@@ -34,8 +41,7 @@ public class MeResourceTest {
|
|||||||
@Rule
|
@Rule
|
||||||
public ShiroRule shiro = new ShiroRule();
|
public ShiroRule shiro = new ShiroRule();
|
||||||
|
|
||||||
private Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
|
private Dispatcher dispatcher;
|
||||||
|
|
||||||
|
|
||||||
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(URI.create("/"));
|
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(URI.create("/"));
|
||||||
@Mock
|
@Mock
|
||||||
@@ -47,22 +53,28 @@ public class MeResourceTest {
|
|||||||
private UserManager userManager;
|
private UserManager userManager;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private UserToUserDtoMapperImpl userToDtoMapper;
|
private MeToUserDtoMapperImpl userToDtoMapper;
|
||||||
|
|
||||||
private ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
|
private ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PasswordService passwordService;
|
||||||
|
private User originalUser;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void prepareEnvironment() throws Exception {
|
public void prepareEnvironment() throws Exception {
|
||||||
initMocks(this);
|
initMocks(this);
|
||||||
createDummyUser("trillian");
|
originalUser = createDummyUser("trillian");
|
||||||
when(userManager.create(userCaptor.capture())).thenAnswer(invocation -> invocation.getArguments()[0]);
|
when(userManager.create(userCaptor.capture())).thenAnswer(invocation -> invocation.getArguments()[0]);
|
||||||
doNothing().when(userManager).modify(userCaptor.capture());
|
doNothing().when(userManager).modify(userCaptor.capture());
|
||||||
doNothing().when(userManager).delete(userCaptor.capture());
|
doNothing().when(userManager).delete(userCaptor.capture());
|
||||||
userToDtoMapper.setResourceLinks(resourceLinks);
|
when(userManager.isTypeDefault(userCaptor.capture())).thenCallRealMethod();
|
||||||
MeResource meResource = new MeResource(userToDtoMapper, userManager);
|
when(userManager.getUserTypeChecker()).thenCallRealMethod();
|
||||||
dispatcher.getRegistry().addSingletonResource(meResource);
|
when(userManager.getDefaultType()).thenReturn("xml");
|
||||||
|
MeResource meResource = new MeResource(userToDtoMapper, userManager, passwordService);
|
||||||
when(uriInfo.getApiRestUri()).thenReturn(URI.create("/"));
|
when(uriInfo.getApiRestUri()).thenReturn(URI.create("/"));
|
||||||
when(scmPathInfoStore.get()).thenReturn(uriInfo);
|
when(scmPathInfoStore.get()).thenReturn(uriInfo);
|
||||||
|
dispatcher = createDispatcher(meResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -76,14 +88,77 @@ public class MeResourceTest {
|
|||||||
|
|
||||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||||
assertTrue(response.getContentAsString().contains("\"name\":\"trillian\""));
|
assertTrue(response.getContentAsString().contains("\"name\":\"trillian\""));
|
||||||
assertTrue(response.getContentAsString().contains("\"password\":\"__dummypassword__\""));
|
assertTrue(response.getContentAsString().contains("\"self\":{\"href\":\"/v2/me/\"}"));
|
||||||
assertTrue(response.getContentAsString().contains("\"self\":{\"href\":\"/v2/users/trillian\"}"));
|
|
||||||
assertTrue(response.getContentAsString().contains("\"delete\":{\"href\":\"/v2/users/trillian\"}"));
|
assertTrue(response.getContentAsString().contains("\"delete\":{\"href\":\"/v2/users/trillian\"}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SubjectAware(username = "trillian", password = "secret")
|
||||||
|
public void shouldEncryptPasswordBeforeChanging() throws Exception {
|
||||||
|
String newPassword = "pwd123";
|
||||||
|
String encryptedNewPassword = "encrypted123";
|
||||||
|
String oldPassword = "notEncriptedSecret";
|
||||||
|
String content = String.format("{ \"oldPassword\": \"%s\" , \"newPassword\": \"%s\" }", oldPassword, newPassword);
|
||||||
|
MockHttpRequest request = MockHttpRequest
|
||||||
|
.put("/" + MeResource.ME_PATH_V2 + "password")
|
||||||
|
.contentType(VndMediaType.PASSWORD_CHANGE)
|
||||||
|
.content(content.getBytes());
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
when(passwordService.encryptPassword(eq(newPassword))).thenReturn(encryptedNewPassword);
|
||||||
|
when(passwordService.encryptPassword(eq(oldPassword))).thenReturn("secret");
|
||||||
|
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
|
||||||
|
assertEquals(HttpServletResponse.SC_NO_CONTENT, response.getStatus());
|
||||||
|
verify(userManager).modify(any(User.class));
|
||||||
|
User updatedUser = userCaptor.getValue();
|
||||||
|
assertEquals(encryptedNewPassword, updatedUser.getPassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SubjectAware(username = "trillian", password = "secret")
|
||||||
|
public void shouldGet400OnChangePasswordOfUserWithNonDefaultType() throws Exception {
|
||||||
|
originalUser.setType("not an xml type");
|
||||||
|
String newPassword = "pwd123";
|
||||||
|
String oldPassword = "notEncriptedSecret";
|
||||||
|
String content = String.format("{ \"oldPassword\": \"%s\" , \"newPassword\": \"%s\" }", oldPassword, newPassword);
|
||||||
|
MockHttpRequest request = MockHttpRequest
|
||||||
|
.put("/" + MeResource.ME_PATH_V2 + "password")
|
||||||
|
.contentType(VndMediaType.PASSWORD_CHANGE)
|
||||||
|
.content(content.getBytes());
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
when(passwordService.encryptPassword(newPassword)).thenReturn("encrypted123");
|
||||||
|
when(passwordService.encryptPassword(eq(oldPassword))).thenReturn("secret");
|
||||||
|
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
|
||||||
|
assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SubjectAware(username = "trillian", password = "secret")
|
||||||
|
public void shouldGet400OnChangePasswordIfOldPasswordDoesNotMatchOriginalPassword() throws Exception {
|
||||||
|
String newPassword = "pwd123";
|
||||||
|
String oldPassword = "notEncriptedSecret";
|
||||||
|
String content = String.format("{ \"oldPassword\": \"%s\" , \"newPassword\": \"%s\" }", oldPassword, newPassword);
|
||||||
|
MockHttpRequest request = MockHttpRequest
|
||||||
|
.put("/" + MeResource.ME_PATH_V2 + "password")
|
||||||
|
.contentType(VndMediaType.PASSWORD_CHANGE)
|
||||||
|
.content(content.getBytes());
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
when(passwordService.encryptPassword(newPassword)).thenReturn("encrypted123");
|
||||||
|
when(passwordService.encryptPassword(eq(oldPassword))).thenReturn("differentThanSecret");
|
||||||
|
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
|
||||||
|
assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private User createDummyUser(String name) {
|
private User createDummyUser(String name) {
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setName(name);
|
user.setName(name);
|
||||||
|
user.setType("xml");
|
||||||
user.setPassword("secret");
|
user.setPassword("secret");
|
||||||
user.setCreationDate(System.currentTimeMillis());
|
user.setCreationDate(System.currentTimeMillis());
|
||||||
when(userManager.get(name)).thenReturn(user);
|
when(userManager.get(name)).thenReturn(user);
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import org.apache.shiro.subject.Subject;
|
||||||
|
import org.apache.shiro.subject.support.SubjectThreadState;
|
||||||
|
import org.apache.shiro.util.ThreadContext;
|
||||||
|
import org.apache.shiro.util.ThreadState;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import sonia.scm.user.User;
|
||||||
|
import sonia.scm.user.UserManager;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.mockito.MockitoAnnotations.initMocks;
|
||||||
|
|
||||||
|
public class MeToUserDtoMapperTest {
|
||||||
|
|
||||||
|
private final URI baseUri = URI.create("http://example.com/base/");
|
||||||
|
@SuppressWarnings("unused") // Is injected
|
||||||
|
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private UserManager userManager;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private MeToUserDtoMapperImpl mapper;
|
||||||
|
|
||||||
|
private final Subject subject = mock(Subject.class);
|
||||||
|
private final ThreadState subjectThreadState = new SubjectThreadState(subject);
|
||||||
|
|
||||||
|
private URI expectedBaseUri;
|
||||||
|
private URI expectedUserBaseUri;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() {
|
||||||
|
initMocks(this);
|
||||||
|
when(userManager.getDefaultType()).thenReturn("xml");
|
||||||
|
expectedBaseUri = baseUri.resolve(MeResource.ME_PATH_V2 + "/");
|
||||||
|
expectedUserBaseUri = baseUri.resolve(UserRootResource.USERS_PATH_V2 + "/");
|
||||||
|
subjectThreadState.bind();
|
||||||
|
ThreadContext.bind(subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void unbindSubject() {
|
||||||
|
ThreadContext.unbindSubject();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldMapTheUpdateLink() {
|
||||||
|
User user = createDefaultUser();
|
||||||
|
when(subject.isPermitted("user:modify:abc")).thenReturn(true);
|
||||||
|
|
||||||
|
UserDto userDto = mapper.map(user);
|
||||||
|
assertEquals("expected update link", expectedUserBaseUri.resolve("abc").toString(), userDto.getLinks().getLinkBy("update").get().getHref());
|
||||||
|
|
||||||
|
when(subject.isPermitted("user:modify:abc")).thenReturn(false);
|
||||||
|
userDto = mapper.map(user);
|
||||||
|
assertFalse("expected no update link", userDto.getLinks().getLinkBy("update").isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldMapTheSelfLink() {
|
||||||
|
User user = createDefaultUser();
|
||||||
|
when(subject.isPermitted("user:modify:abc")).thenReturn(true);
|
||||||
|
|
||||||
|
UserDto userDto = mapper.map(user);
|
||||||
|
assertEquals("expected self link", expectedBaseUri.toString(), userDto.getLinks().getLinkBy("self").get().getHref());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldMapTheDeleteLink() {
|
||||||
|
User user = createDefaultUser();
|
||||||
|
when(subject.isPermitted("user:delete:abc")).thenReturn(true);
|
||||||
|
|
||||||
|
UserDto userDto = mapper.map(user);
|
||||||
|
assertEquals("expected update link", expectedUserBaseUri.resolve("abc").toString(), userDto.getLinks().getLinkBy("delete").get().getHref());
|
||||||
|
|
||||||
|
when(subject.isPermitted("user:delete:abc")).thenReturn(false);
|
||||||
|
userDto = mapper.map(user);
|
||||||
|
assertFalse("expected no delete link", userDto.getLinks().getLinkBy("delete").isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldGetPasswordLinkOnlyForDefaultUserType() {
|
||||||
|
User user = createDefaultUser();
|
||||||
|
when(subject.isPermitted("user:modify:abc")).thenReturn(true);
|
||||||
|
when(userManager.isTypeDefault(eq(user))).thenReturn(true);
|
||||||
|
|
||||||
|
UserDto userDto = mapper.map(user);
|
||||||
|
|
||||||
|
assertEquals("expected password link with modify permission", expectedBaseUri.resolve("password").toString(), userDto.getLinks().getLinkBy("password").get().getHref());
|
||||||
|
|
||||||
|
when(subject.isPermitted("user:modify:abc")).thenReturn(false);
|
||||||
|
userDto = mapper.map(user);
|
||||||
|
assertEquals("expected password link on mission modify permission", expectedBaseUri.resolve("password").toString(), userDto.getLinks().getLinkBy("password").get().getHref());
|
||||||
|
|
||||||
|
when(userManager.isTypeDefault(eq(user))).thenReturn(false);
|
||||||
|
|
||||||
|
userDto = mapper.map(user);
|
||||||
|
|
||||||
|
assertFalse("expected no password link", userDto.getLinks().getLinkBy("password").isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldGetEmptyPasswordProperty() {
|
||||||
|
User user = createDefaultUser();
|
||||||
|
user.setPassword("myHighSecurePassword");
|
||||||
|
when(subject.isPermitted("user:modify:abc")).thenReturn(true);
|
||||||
|
|
||||||
|
UserDto userDto = mapper.map(user);
|
||||||
|
|
||||||
|
assertThat(userDto.getPassword()).as("hide password for the me resource").isBlank();
|
||||||
|
}
|
||||||
|
|
||||||
|
private User createDefaultUser() {
|
||||||
|
User user = new User();
|
||||||
|
user.setName("abc");
|
||||||
|
user.setCreationDate(1L);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -12,7 +12,9 @@ public class ResourceLinksMock {
|
|||||||
ScmPathInfo uriInfo = mock(ScmPathInfo.class);
|
ScmPathInfo uriInfo = mock(ScmPathInfo.class);
|
||||||
when(uriInfo.getApiRestUri()).thenReturn(baseUri);
|
when(uriInfo.getApiRestUri()).thenReturn(baseUri);
|
||||||
|
|
||||||
when(resourceLinks.user()).thenReturn(new ResourceLinks.UserLinks(uriInfo));
|
ResourceLinks.UserLinks userLinks = new ResourceLinks.UserLinks(uriInfo);
|
||||||
|
when(resourceLinks.user()).thenReturn(userLinks);
|
||||||
|
when(resourceLinks.me()).thenReturn(new ResourceLinks.MeLinks(uriInfo,userLinks));
|
||||||
when(resourceLinks.userCollection()).thenReturn(new ResourceLinks.UserCollectionLinks(uriInfo));
|
when(resourceLinks.userCollection()).thenReturn(new ResourceLinks.UserCollectionLinks(uriInfo));
|
||||||
when(resourceLinks.group()).thenReturn(new ResourceLinks.GroupLinks(uriInfo));
|
when(resourceLinks.group()).thenReturn(new ResourceLinks.GroupLinks(uriInfo));
|
||||||
when(resourceLinks.groupCollection()).thenReturn(new ResourceLinks.GroupCollectionLinks(uriInfo));
|
when(resourceLinks.groupCollection()).thenReturn(new ResourceLinks.GroupCollectionLinks(uriInfo));
|
||||||
|
|||||||
@@ -23,18 +23,9 @@ public class UserDtoToUserMapperTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldMapFields() {
|
public void shouldMapFields() {
|
||||||
UserDto dto = createDefaultDto();
|
UserDto dto = createDefaultDto();
|
||||||
User user = mapper.map(dto, "original password");
|
User user = mapper.map(dto, "used password");
|
||||||
assertEquals("abc" , user.getName());
|
assertEquals("abc" , user.getName());
|
||||||
}
|
assertEquals("used password" , user.getPassword());
|
||||||
|
|
||||||
@Test
|
|
||||||
public void shouldEncodePassword() {
|
|
||||||
when(passwordService.encryptPassword("unencrypted")).thenReturn("encrypted");
|
|
||||||
|
|
||||||
UserDto dto = createDefaultDto();
|
|
||||||
dto.setPassword("unencrypted");
|
|
||||||
User user = mapper.map(dto, "original password");
|
|
||||||
assertEquals("encrypted" , user.getPassword());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
@@ -61,19 +62,23 @@ public class UserRootResourceTest {
|
|||||||
private UserToUserDtoMapperImpl userToDtoMapper;
|
private UserToUserDtoMapperImpl userToDtoMapper;
|
||||||
|
|
||||||
private ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
|
private ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
|
||||||
|
private User originalUser;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void prepareEnvironment() throws Exception {
|
public void prepareEnvironment() throws Exception {
|
||||||
initMocks(this);
|
initMocks(this);
|
||||||
User dummyUser = createDummyUser("Neo");
|
originalUser = createDummyUser("Neo");
|
||||||
when(userManager.create(userCaptor.capture())).thenAnswer(invocation -> invocation.getArguments()[0]);
|
when(userManager.create(userCaptor.capture())).thenAnswer(invocation -> invocation.getArguments()[0]);
|
||||||
|
when(userManager.isTypeDefault(userCaptor.capture())).thenCallRealMethod();
|
||||||
|
when(userManager.getUserTypeChecker()).thenCallRealMethod();
|
||||||
doNothing().when(userManager).modify(userCaptor.capture());
|
doNothing().when(userManager).modify(userCaptor.capture());
|
||||||
doNothing().when(userManager).delete(userCaptor.capture());
|
doNothing().when(userManager).delete(userCaptor.capture());
|
||||||
|
when(userManager.getDefaultType()).thenReturn("xml");
|
||||||
|
|
||||||
UserCollectionToDtoMapper userCollectionToDtoMapper = new UserCollectionToDtoMapper(userToDtoMapper, resourceLinks);
|
UserCollectionToDtoMapper userCollectionToDtoMapper = new UserCollectionToDtoMapper(userToDtoMapper, resourceLinks);
|
||||||
UserCollectionResource userCollectionResource = new UserCollectionResource(userManager, dtoToUserMapper,
|
UserCollectionResource userCollectionResource = new UserCollectionResource(userManager, dtoToUserMapper,
|
||||||
userCollectionToDtoMapper, resourceLinks);
|
userCollectionToDtoMapper, resourceLinks, passwordService);
|
||||||
UserResource userResource = new UserResource(dtoToUserMapper, userToDtoMapper, userManager);
|
UserResource userResource = new UserResource(dtoToUserMapper, userToDtoMapper, userManager, passwordService);
|
||||||
UserRootResource userRootResource = new UserRootResource(Providers.of(userCollectionResource),
|
UserRootResource userRootResource = new UserRootResource(Providers.of(userCollectionResource),
|
||||||
Providers.of(userResource));
|
Providers.of(userResource));
|
||||||
|
|
||||||
@@ -89,7 +94,6 @@ public class UserRootResourceTest {
|
|||||||
|
|
||||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||||
assertTrue(response.getContentAsString().contains("\"name\":\"Neo\""));
|
assertTrue(response.getContentAsString().contains("\"name\":\"Neo\""));
|
||||||
assertTrue(response.getContentAsString().contains("\"password\":\"__dummypassword__\""));
|
|
||||||
assertTrue(response.getContentAsString().contains("\"self\":{\"href\":\"/v2/users/Neo\"}"));
|
assertTrue(response.getContentAsString().contains("\"self\":{\"href\":\"/v2/users/Neo\"}"));
|
||||||
assertTrue(response.getContentAsString().contains("\"delete\":{\"href\":\"/v2/users/Neo\"}"));
|
assertTrue(response.getContentAsString().contains("\"delete\":{\"href\":\"/v2/users/Neo\"}"));
|
||||||
}
|
}
|
||||||
@@ -104,13 +108,48 @@ public class UserRootResourceTest {
|
|||||||
|
|
||||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||||
assertTrue(response.getContentAsString().contains("\"name\":\"Neo\""));
|
assertTrue(response.getContentAsString().contains("\"name\":\"Neo\""));
|
||||||
assertTrue(response.getContentAsString().contains("\"password\":\"__dummypassword__\""));
|
|
||||||
assertTrue(response.getContentAsString().contains("\"self\":{\"href\":\"/v2/users/Neo\"}"));
|
assertTrue(response.getContentAsString().contains("\"self\":{\"href\":\"/v2/users/Neo\"}"));
|
||||||
assertFalse(response.getContentAsString().contains("\"delete\":{\"href\":\"/v2/users/Neo\"}"));
|
assertFalse(response.getContentAsString().contains("\"delete\":{\"href\":\"/v2/users/Neo\"}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldCreateNewUserWithEncryptedPassword() throws Exception {
|
public void shouldEncryptPasswordBeforeChanging() throws Exception {
|
||||||
|
String newPassword = "pwd123";
|
||||||
|
String content = String.format("{\"newPassword\": \"%s\"}", newPassword);
|
||||||
|
MockHttpRequest request = MockHttpRequest
|
||||||
|
.put("/" + UserRootResource.USERS_PATH_V2 + "Neo/password")
|
||||||
|
.contentType(VndMediaType.PASSWORD_CHANGE)
|
||||||
|
.content(content.getBytes());
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
when(passwordService.encryptPassword(newPassword)).thenReturn("encrypted123");
|
||||||
|
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
|
||||||
|
assertEquals(HttpServletResponse.SC_NO_CONTENT, response.getStatus());
|
||||||
|
verify(userManager).modify(any(User.class));
|
||||||
|
User updatedUser = userCaptor.getValue();
|
||||||
|
assertEquals("encrypted123", updatedUser.getPassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldGet400OnChangePasswordOfUserWithNonDefaultType() throws Exception {
|
||||||
|
originalUser.setType("not an xml type");
|
||||||
|
String newPassword = "pwd123";
|
||||||
|
String content = String.format("{\"newPassword\": \"%s\"}", newPassword);
|
||||||
|
MockHttpRequest request = MockHttpRequest
|
||||||
|
.put("/" + UserRootResource.USERS_PATH_V2 + "Neo/password")
|
||||||
|
.contentType(VndMediaType.PASSWORD_CHANGE)
|
||||||
|
.content(content.getBytes());
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
when(passwordService.encryptPassword(newPassword)).thenReturn("encrypted123");
|
||||||
|
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
|
||||||
|
assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldEncryptPasswordBeforeCreatingUser() throws Exception {
|
||||||
URL url = Resources.getResource("sonia/scm/api/v2/user-test-create.json");
|
URL url = Resources.getResource("sonia/scm/api/v2/user-test-create.json");
|
||||||
byte[] userJson = Resources.toByteArray(url);
|
byte[] userJson = Resources.toByteArray(url);
|
||||||
|
|
||||||
@@ -130,7 +169,7 @@ public class UserRootResourceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldUpdateChangedUserWithEncryptedPassword() throws Exception {
|
public void shouldIgnoreGivenPasswordOnUpdatingUser() throws Exception {
|
||||||
URL url = Resources.getResource("sonia/scm/api/v2/user-test-update.json");
|
URL url = Resources.getResource("sonia/scm/api/v2/user-test-update.json");
|
||||||
byte[] userJson = Resources.toByteArray(url);
|
byte[] userJson = Resources.toByteArray(url);
|
||||||
|
|
||||||
@@ -139,14 +178,13 @@ public class UserRootResourceTest {
|
|||||||
.contentType(VndMediaType.USER)
|
.contentType(VndMediaType.USER)
|
||||||
.content(userJson);
|
.content(userJson);
|
||||||
MockHttpResponse response = new MockHttpResponse();
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
when(passwordService.encryptPassword("pwd123")).thenReturn("encrypted123");
|
|
||||||
|
|
||||||
dispatcher.invoke(request, response);
|
dispatcher.invoke(request, response);
|
||||||
|
|
||||||
assertEquals(HttpServletResponse.SC_NO_CONTENT, response.getStatus());
|
assertEquals(HttpServletResponse.SC_NO_CONTENT, response.getStatus());
|
||||||
verify(userManager).modify(any(User.class));
|
verify(userManager).modify(any(User.class));
|
||||||
User updatedUser = userCaptor.getValue();
|
User updatedUser = userCaptor.getValue();
|
||||||
assertEquals("encrypted123", updatedUser.getPassword());
|
assertEquals(originalUser.getPassword(), updatedUser.getPassword());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -265,6 +303,7 @@ public class UserRootResourceTest {
|
|||||||
private User createDummyUser(String name) {
|
private User createDummyUser(String name) {
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setName(name);
|
user.setName(name);
|
||||||
|
user.setType("xml");
|
||||||
user.setPassword("redpill");
|
user.setPassword("redpill");
|
||||||
user.setCreationDate(System.currentTimeMillis());
|
user.setCreationDate(System.currentTimeMillis());
|
||||||
when(userManager.get(name)).thenReturn(user);
|
when(userManager.get(name)).thenReturn(user);
|
||||||
|
|||||||
@@ -8,14 +8,17 @@ import org.junit.After;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import sonia.scm.api.rest.resources.UserResource;
|
import org.mockito.Mock;
|
||||||
import sonia.scm.user.User;
|
import sonia.scm.user.User;
|
||||||
|
import sonia.scm.user.UserManager;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.mockito.MockitoAnnotations.initMocks;
|
import static org.mockito.MockitoAnnotations.initMocks;
|
||||||
@@ -26,6 +29,9 @@ public class UserToUserDtoMapperTest {
|
|||||||
@SuppressWarnings("unused") // Is injected
|
@SuppressWarnings("unused") // Is injected
|
||||||
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private UserManager userManager;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private UserToUserDtoMapperImpl mapper;
|
private UserToUserDtoMapperImpl mapper;
|
||||||
|
|
||||||
@@ -37,6 +43,7 @@ public class UserToUserDtoMapperTest {
|
|||||||
@Before
|
@Before
|
||||||
public void init() {
|
public void init() {
|
||||||
initMocks(this);
|
initMocks(this);
|
||||||
|
when(userManager.getDefaultType()).thenReturn("xml");
|
||||||
expectedBaseUri = baseUri.resolve(UserRootResource.USERS_PATH_V2 + "/");
|
expectedBaseUri = baseUri.resolve(UserRootResource.USERS_PATH_V2 + "/");
|
||||||
subjectThreadState.bind();
|
subjectThreadState.bind();
|
||||||
ThreadContext.bind(subject);
|
ThreadContext.bind(subject);
|
||||||
@@ -53,11 +60,42 @@ public class UserToUserDtoMapperTest {
|
|||||||
when(subject.isPermitted("user:modify:abc")).thenReturn(true);
|
when(subject.isPermitted("user:modify:abc")).thenReturn(true);
|
||||||
|
|
||||||
UserDto userDto = mapper.map(user);
|
UserDto userDto = mapper.map(user);
|
||||||
|
|
||||||
assertEquals("expected self link", expectedBaseUri.resolve("abc").toString(), userDto.getLinks().getLinkBy("self").get().getHref());
|
assertEquals("expected self link", expectedBaseUri.resolve("abc").toString(), userDto.getLinks().getLinkBy("self").get().getHref());
|
||||||
assertEquals("expected update link", expectedBaseUri.resolve("abc").toString(), userDto.getLinks().getLinkBy("update").get().getHref());
|
assertEquals("expected update link", expectedBaseUri.resolve("abc").toString(), userDto.getLinks().getLinkBy("update").get().getHref());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldGetPasswordLinkOnlyForDefaultUserType() {
|
||||||
|
User user = createDefaultUser();
|
||||||
|
when(subject.isPermitted("user:modify:abc")).thenReturn(true);
|
||||||
|
when(userManager.isTypeDefault(eq(user))).thenReturn(true);
|
||||||
|
|
||||||
|
UserDto userDto = mapper.map(user);
|
||||||
|
|
||||||
|
assertEquals("expected password link with modify permission", expectedBaseUri.resolve("abc/password").toString(), userDto.getLinks().getLinkBy("password").get().getHref());
|
||||||
|
|
||||||
|
when(subject.isPermitted("user:modify:abc")).thenReturn(false);
|
||||||
|
userDto = mapper.map(user);
|
||||||
|
assertEquals("expected password link on mission modify permission", expectedBaseUri.resolve("abc/password").toString(), userDto.getLinks().getLinkBy("password").get().getHref());
|
||||||
|
|
||||||
|
when(userManager.isTypeDefault(eq(user))).thenReturn(false);
|
||||||
|
|
||||||
|
userDto = mapper.map(user);
|
||||||
|
|
||||||
|
assertFalse("expected no password link", userDto.getLinks().getLinkBy("password").isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldGetEmptyPasswordProperty() {
|
||||||
|
User user = createDefaultUser();
|
||||||
|
user.setPassword("myHighSecurePassword");
|
||||||
|
when(subject.isPermitted("user:modify:abc")).thenReturn(true);
|
||||||
|
|
||||||
|
UserDto userDto = mapper.map(user);
|
||||||
|
|
||||||
|
assertThat(userDto.getPassword()).isBlank();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldMapLinks_forDelete() {
|
public void shouldMapLinks_forDelete() {
|
||||||
User user = createDefaultUser();
|
User user = createDefaultUser();
|
||||||
@@ -97,16 +135,6 @@ public class UserToUserDtoMapperTest {
|
|||||||
assertEquals("abc", userDto.getName());
|
assertEquals("abc", userDto.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void shouldRemovePassword() {
|
|
||||||
User user = createDefaultUser();
|
|
||||||
user.setPassword("password");
|
|
||||||
|
|
||||||
UserDto userDto = mapper.map(user);
|
|
||||||
|
|
||||||
assertEquals(UserResource.DUMMY_PASSWORT, userDto.getPassword());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldMapTimes() {
|
public void shouldMapTimes() {
|
||||||
User user = createDefaultUser();
|
User user = createDefaultUser();
|
||||||
|
|||||||
Reference in New Issue
Block a user