Merged branch 'jetty-11.0.x' into 'jetty-12.0.x'.
Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
commit
e933116997
|
@ -38,6 +38,7 @@ public interface HttpCookie
|
|||
String PATH_ATTRIBUTE = "Path";
|
||||
String SAME_SITE_ATTRIBUTE = "SameSite";
|
||||
String SECURE_ATTRIBUTE = "Secure";
|
||||
String PARTITIONED_ATTRIBUTE = "Partitioned";
|
||||
|
||||
/**
|
||||
* @return the cookie name
|
||||
|
@ -150,6 +151,15 @@ public interface HttpCookie
|
|||
return Boolean.parseBoolean(getAttributes().get(HTTP_ONLY_ATTRIBUTE));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the {@code Partitioned} attribute is present
|
||||
* @see #PARTITIONED_ATTRIBUTE
|
||||
*/
|
||||
default boolean isPartitioned()
|
||||
{
|
||||
return Boolean.parseBoolean(getAttributes().get(PARTITIONED_ATTRIBUTE));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the cookie hash code
|
||||
* @see #hashCode(HttpCookie)
|
||||
|
@ -260,6 +270,12 @@ public interface HttpCookie
|
|||
return getWrapped().isHttpOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPartitioned()
|
||||
{
|
||||
return getWrapped().isPartitioned();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
|
@ -560,6 +576,12 @@ public interface HttpCookie
|
|||
throw new IllegalArgumentException("Invalid Secure attribute");
|
||||
secure(true);
|
||||
}
|
||||
case "partitioned" ->
|
||||
{
|
||||
if (!isTruthy(value))
|
||||
throw new IllegalArgumentException("Invalid Partitioned attribute");
|
||||
partitioned(true);
|
||||
}
|
||||
default -> _attributes = lazyAttributePut(_attributes, name, value);
|
||||
}
|
||||
return this;
|
||||
|
@ -630,6 +652,15 @@ public interface HttpCookie
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder partitioned(boolean partitioned)
|
||||
{
|
||||
if (partitioned)
|
||||
_attributes = lazyAttributePut(_attributes, PARTITIONED_ATTRIBUTE, Boolean.TRUE.toString());
|
||||
else
|
||||
_attributes = lazyAttributeRemove(_attributes, PARTITIONED_ATTRIBUTE);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an immutable {@link HttpCookie} instance.
|
||||
*/
|
||||
|
@ -657,8 +688,8 @@ public interface HttpCookie
|
|||
* @param value the value of the cookie
|
||||
* @param attributes the map of attributes to use with this cookie (this map is used for field values
|
||||
* such as {@link #getDomain()}, {@link #getPath()}, {@link #getMaxAge()}, {@link #isHttpOnly()},
|
||||
* {@link #isSecure()}, {@link #getComment()}, plus any newly defined attributes unknown to this
|
||||
* code base.
|
||||
* {@link #isSecure()}, {@link #isPartitioned()}, {@link #getComment()}, plus any newly defined
|
||||
* attributes unknown to this code base.
|
||||
*/
|
||||
static HttpCookie from(String name, String value, Map<String, String> attributes)
|
||||
{
|
||||
|
@ -673,8 +704,8 @@ public interface HttpCookie
|
|||
* @param version the version of the cookie (only used in RFC2965 mode)
|
||||
* @param attributes the map of attributes to use with this cookie (this map is used for field values
|
||||
* such as {@link #getDomain()}, {@link #getPath()}, {@link #getMaxAge()}, {@link #isHttpOnly()},
|
||||
* {@link #isSecure()}, {@link #getComment()}, plus any newly defined attributes unknown to this
|
||||
* code base.
|
||||
* {@link #isSecure()}, {@link #isPartitioned()}, {@link #getComment()}, plus any newly defined
|
||||
* attributes unknown to this code base.
|
||||
*/
|
||||
static HttpCookie from(String name, String value, int version, Map<String, String> attributes)
|
||||
{
|
||||
|
@ -786,6 +817,8 @@ public interface HttpCookie
|
|||
{
|
||||
if (httpCookie.getSameSite() != null)
|
||||
throw new IllegalArgumentException("SameSite attribute not supported by " + java.net.HttpCookie.class.getName());
|
||||
if (httpCookie.isPartitioned())
|
||||
throw new IllegalArgumentException("Partitioned attribute not supported by " + java.net.HttpCookie.class.getName());
|
||||
java.net.HttpCookie cookie = new java.net.HttpCookie(httpCookie.getName(), httpCookie.getValue());
|
||||
cookie.setVersion(httpCookie.getVersion());
|
||||
cookie.setComment(httpCookie.getComment());
|
||||
|
|
|
@ -46,6 +46,7 @@ public final class HttpCookieUtils
|
|||
.with(HttpCookie.PATH_ATTRIBUTE)
|
||||
.with(HttpCookie.SAME_SITE_ATTRIBUTE)
|
||||
.with(HttpCookie.SECURE_ATTRIBUTE)
|
||||
.with(HttpCookie.PARTITIONED_ATTRIBUTE)
|
||||
.build();
|
||||
// RFC 1123 format of epoch for the Expires attribute.
|
||||
private static final String EPOCH_EXPIRES = "Thu, 01 Jan 1970 00:00:00 GMT";
|
||||
|
@ -187,6 +188,9 @@ public final class HttpCookieUtils
|
|||
if (httpCookie.isHttpOnly())
|
||||
builder.append(";HttpOnly");
|
||||
|
||||
if (httpCookie.isPartitioned())
|
||||
builder.append(";Partitioned");
|
||||
|
||||
HttpCookie.SameSite sameSite = httpCookie.getSameSite();
|
||||
if (sameSite != null)
|
||||
builder.append(";SameSite=").append(sameSite.getAttributeValue());
|
||||
|
@ -249,6 +253,8 @@ public final class HttpCookieUtils
|
|||
builder.append("; Secure");
|
||||
if (httpCookie.isHttpOnly())
|
||||
builder.append("; HttpOnly");
|
||||
if (httpCookie.isPartitioned())
|
||||
builder.append("; Partitioned");
|
||||
|
||||
Map<String, String> attributes = httpCookie.getAttributes();
|
||||
|
||||
|
|
|
@ -152,6 +152,9 @@ public class HttpCookieTest
|
|||
|
||||
httpCookie = HttpCookie.from("everything", "value", Map.of(HttpCookie.DOMAIN_ATTRIBUTE, "domain", HttpCookie.PATH_ATTRIBUTE, "path", HttpCookie.MAX_AGE_ATTRIBUTE, Long.toString(0), HttpCookie.HTTP_ONLY_ATTRIBUTE, Boolean.toString(true), HttpCookie.SECURE_ATTRIBUTE, Boolean.toString(true), HttpCookie.SAME_SITE_ATTRIBUTE, SameSite.STRICT.getAttributeValue()));
|
||||
assertEquals("everything=value; Path=path; Domain=domain; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Secure; HttpOnly; SameSite=Strict", HttpCookieUtils.getRFC6265SetCookie(httpCookie));
|
||||
|
||||
httpCookie = HttpCookie.from("everything", "value", Map.of(HttpCookie.DOMAIN_ATTRIBUTE, "domain", HttpCookie.PATH_ATTRIBUTE, "path", HttpCookie.MAX_AGE_ATTRIBUTE, Long.toString(0), HttpCookie.HTTP_ONLY_ATTRIBUTE, Boolean.toString(true), HttpCookie.SECURE_ATTRIBUTE, Boolean.toString(true), HttpCookie.SAME_SITE_ATTRIBUTE, SameSite.STRICT.getAttributeValue(), HttpCookie.PARTITIONED_ATTRIBUTE, Boolean.toString(true)));
|
||||
assertEquals("everything=value; Path=path; Domain=domain; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Secure; HttpOnly; Partitioned; SameSite=Strict", HttpCookieUtils.getRFC6265SetCookie(httpCookie));
|
||||
}
|
||||
|
||||
public static Stream<String> rfc6265BadNameSource()
|
||||
|
|
|
@ -834,8 +834,8 @@ public abstract class AbstractSessionManager extends ContainerLifeCycle implemen
|
|||
}
|
||||
|
||||
/**
|
||||
* @return true if session cookies should be HTTP-only (Microsoft extension)
|
||||
* @see org.eclipse.jetty.http.HttpCookie#isHttpOnly()
|
||||
* @return true if session cookies should be HTTP only
|
||||
* @see HttpCookie#isHttpOnly()
|
||||
*/
|
||||
@Override
|
||||
public boolean isHttpOnly()
|
||||
|
@ -855,6 +855,28 @@ public abstract class AbstractSessionManager extends ContainerLifeCycle implemen
|
|||
_sessionCookieAttributes.put(HttpCookie.HTTP_ONLY_ATTRIBUTE, Boolean.toString(httpOnly));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if session cookies should have the {@code Partitioned} attribute
|
||||
* @see HttpCookie#isPartitioned()
|
||||
*/
|
||||
@Override
|
||||
public boolean isPartitioned()
|
||||
{
|
||||
return Boolean.parseBoolean(_sessionCookieAttributes.get(HttpCookie.PARTITIONED_ATTRIBUTE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether session cookies should have the {@code Partitioned} attribute
|
||||
*
|
||||
* @param partitioned whether session cookies should have the {@code Partitioned} attribute
|
||||
* @see HttpCookie
|
||||
*/
|
||||
@Override
|
||||
public void setPartitioned(boolean partitioned)
|
||||
{
|
||||
_sessionCookieAttributes.put(HttpCookie.PARTITIONED_ATTRIBUTE, Boolean.toString(partitioned));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if id is in use by this context
|
||||
*
|
||||
|
|
|
@ -95,6 +95,9 @@ public interface SessionConfig
|
|||
@ManagedAttribute("true if cookies use the http only flag")
|
||||
boolean isHttpOnly();
|
||||
|
||||
@ManagedAttribute("true if cookies have the Partitioned attribute")
|
||||
boolean isPartitioned();
|
||||
|
||||
@ManagedAttribute("if true, secure cookie flag is set on session cookies")
|
||||
boolean isSecureCookies();
|
||||
|
||||
|
@ -115,6 +118,8 @@ public interface SessionConfig
|
|||
|
||||
void setHttpOnly(boolean value);
|
||||
|
||||
void setPartitioned(boolean value);
|
||||
|
||||
void setMaxCookieAge(int value);
|
||||
|
||||
void setMaxInactiveInterval(int value);
|
||||
|
|
|
@ -27,9 +27,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class AbstractSessionManagerTest
|
||||
{
|
||||
@Test
|
||||
|
@ -51,7 +48,8 @@ public class AbstractSessionManagerTest
|
|||
assertEquals("/test", cookie.getPath());
|
||||
assertFalse(cookie.isSecure());
|
||||
assertFalse(cookie.isHttpOnly());
|
||||
|
||||
assertFalse(cookie.isPartitioned());
|
||||
|
||||
//check cookie with httpOnly and secure
|
||||
sessionManager.setHttpOnly(true);
|
||||
sessionManager.setSecureRequestOnly(true);
|
||||
|
|
|
@ -589,6 +589,12 @@ public class ServletApiResponse implements HttpServletResponse
|
|||
return _cookie.isHttpOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPartitioned()
|
||||
{
|
||||
return Boolean.parseBoolean(getAttributes().get(HttpCookie.PARTITIONED_ATTRIBUTE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAttributes()
|
||||
{
|
||||
|
|
|
@ -76,7 +76,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||
@ExtendWith(WorkDirExtension.class)
|
||||
public class SessionHandlerTest
|
||||
{
|
||||
|
||||
public static class SessionConsumer implements Consumer<ManagedSession>
|
||||
{
|
||||
private ManagedSession _session;
|
||||
|
@ -685,6 +684,7 @@ public class SessionHandlerTest
|
|||
sessionCookieConfig.setSecure(false);
|
||||
sessionCookieConfig.setPath("/foo");
|
||||
sessionCookieConfig.setMaxAge(99);
|
||||
sessionCookieConfig.setAttribute("Partitioned", "true");
|
||||
sessionCookieConfig.setAttribute("SameSite", "Strict");
|
||||
sessionCookieConfig.setAttribute("ham", "cheese");
|
||||
|
||||
|
@ -694,11 +694,12 @@ public class SessionHandlerTest
|
|||
assertEquals("/foo", cookie.getPath());
|
||||
assertFalse(cookie.isHttpOnly());
|
||||
assertFalse(cookie.isSecure());
|
||||
assertTrue(cookie.isPartitioned());
|
||||
assertEquals(99, cookie.getMaxAge());
|
||||
assertEquals(HttpCookie.SameSite.STRICT, cookie.getSameSite());
|
||||
|
||||
String cookieStr = HttpCookieUtils.getRFC6265SetCookie(cookie);
|
||||
assertThat(cookieStr, containsString("; SameSite=Strict; ham=cheese"));
|
||||
assertThat(cookieStr, containsString("; Partitioned; SameSite=Strict; ham=cheese"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -73,11 +73,18 @@ public class Response implements HttpServletResponse
|
|||
public static final int USE_KNOWN_CONTENT_LENGTH = -2;
|
||||
|
||||
/**
|
||||
* If this string is found within a cookie comment, then the cookie is HttpOnly
|
||||
* String used in the {@code Comment} attribute of {@link Cookie}
|
||||
* to support the {@code HttpOnly} attribute.
|
||||
**/
|
||||
private static final String HTTP_ONLY_COMMENT = "__HTTP_ONLY__";
|
||||
/**
|
||||
* These strings are used by to check for a SameSite specifier in a cookie comment
|
||||
* String used in the {@code Comment} attribute of {@link Cookie}
|
||||
* to support the {@code Partitioned} attribute.
|
||||
**/
|
||||
private static final String PARTITIONED_COMMENT = "__PARTITIONED__";
|
||||
/**
|
||||
* The strings used in the {@code Comment} attribute of {@link Cookie}
|
||||
* to support the {@code SameSite} attribute.
|
||||
**/
|
||||
private static final String SAME_SITE_COMMENT = "__SAME_SITE_";
|
||||
private static final String SAME_SITE_NONE_COMMENT = SAME_SITE_COMMENT + "NONE__";
|
||||
|
@ -1480,16 +1487,16 @@ public class Response implements HttpServletResponse
|
|||
private final String _comment;
|
||||
private final boolean _httpOnly;
|
||||
private final SameSite _sameSite;
|
||||
private final boolean _partitioned;
|
||||
|
||||
public HttpCookieFacade(Cookie cookie)
|
||||
{
|
||||
_cookie = cookie;
|
||||
|
||||
String comment = cookie.getComment();
|
||||
|
||||
// HttpOnly was supported as a comment in cookie flags before the java.net.HttpCookie implementation so need to check that
|
||||
// HttpOnly was supported as a comment in cookie flags before the Cookie implementation so need to check that.
|
||||
_httpOnly = cookie.isHttpOnly() || isHttpOnlyInComment(comment);
|
||||
_sameSite = getSameSiteFromComment(comment);
|
||||
_partitioned = isPartitionedInComment(comment);
|
||||
_comment = getCommentWithoutAttributes(comment);
|
||||
}
|
||||
|
||||
|
@ -1553,6 +1560,12 @@ public class Response implements HttpServletResponse
|
|||
return _httpOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPartitioned()
|
||||
{
|
||||
return _partitioned;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAttributes()
|
||||
{
|
||||
|
@ -1568,6 +1581,8 @@ public class Response implements HttpServletResponse
|
|||
attributes.put(SAME_SITE_ATTRIBUTE, _sameSite.getAttributeValue());
|
||||
if (isSecure())
|
||||
attributes.put(SECURE_ATTRIBUTE, Boolean.TRUE.toString());
|
||||
if (isPartitioned())
|
||||
attributes.put(PARTITIONED_ATTRIBUTE, Boolean.TRUE.toString());
|
||||
return attributes;
|
||||
}
|
||||
|
||||
|
@ -1589,47 +1604,43 @@ public class Response implements HttpServletResponse
|
|||
return HttpCookie.toString(this);
|
||||
}
|
||||
|
||||
static boolean isHttpOnlyInComment(String comment)
|
||||
private static boolean isHttpOnlyInComment(String comment)
|
||||
{
|
||||
return comment != null && comment.contains(HTTP_ONLY_COMMENT);
|
||||
}
|
||||
|
||||
static SameSite getSameSiteFromComment(String comment)
|
||||
private static boolean isPartitionedInComment(String comment)
|
||||
{
|
||||
if (comment != null)
|
||||
{
|
||||
if (comment.contains(SAME_SITE_STRICT_COMMENT))
|
||||
{
|
||||
return SameSite.STRICT;
|
||||
}
|
||||
if (comment.contains(SAME_SITE_LAX_COMMENT))
|
||||
{
|
||||
return SameSite.LAX;
|
||||
}
|
||||
if (comment.contains(SAME_SITE_NONE_COMMENT))
|
||||
{
|
||||
return SameSite.NONE;
|
||||
}
|
||||
}
|
||||
return comment != null && comment.contains(PARTITIONED_COMMENT);
|
||||
}
|
||||
|
||||
private static SameSite getSameSiteFromComment(String comment)
|
||||
{
|
||||
if (comment == null)
|
||||
return null;
|
||||
if (comment.contains(SAME_SITE_STRICT_COMMENT))
|
||||
return SameSite.STRICT;
|
||||
if (comment.contains(SAME_SITE_LAX_COMMENT))
|
||||
return SameSite.LAX;
|
||||
if (comment.contains(SAME_SITE_NONE_COMMENT))
|
||||
return SameSite.NONE;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getCommentWithoutAttributes(String comment)
|
||||
{
|
||||
if (comment == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
String strippedComment = comment.trim();
|
||||
|
||||
strippedComment = StringUtil.strip(strippedComment, HTTP_ONLY_COMMENT);
|
||||
strippedComment = StringUtil.strip(strippedComment, PARTITIONED_COMMENT);
|
||||
strippedComment = StringUtil.strip(strippedComment, SAME_SITE_NONE_COMMENT);
|
||||
strippedComment = StringUtil.strip(strippedComment, SAME_SITE_LAX_COMMENT);
|
||||
strippedComment = StringUtil.strip(strippedComment, SAME_SITE_STRICT_COMMENT);
|
||||
|
||||
return strippedComment.length() == 0 ? null : strippedComment;
|
||||
return strippedComment.isEmpty() ? null : strippedComment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -316,6 +316,12 @@ public class SessionHandler extends ScopedHandler implements SessionConfig.Mutab
|
|||
return _sessionManager.isHttpOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPartitioned()
|
||||
{
|
||||
return _sessionManager.isPartitioned();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecureCookies()
|
||||
{
|
||||
|
@ -352,6 +358,12 @@ public class SessionHandler extends ScopedHandler implements SessionConfig.Mutab
|
|||
_sessionManager.setHttpOnly(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPartitioned(boolean value)
|
||||
{
|
||||
_sessionManager.setPartitioned(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxCookieAge(int value)
|
||||
{
|
||||
|
|
|
@ -54,6 +54,7 @@ import static org.hamcrest.Matchers.nullValue;
|
|||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class SessionHandlerTest
|
||||
{
|
||||
|
@ -193,6 +194,7 @@ public class SessionHandlerTest
|
|||
//for < ee10, SameSite cannot be set on the SessionCookieConfig, only on the SessionManager, or
|
||||
//a default value on the context attribute org.eclipse.jetty.cookie.sameSiteDefault
|
||||
mgr.setSameSite(HttpCookie.SameSite.STRICT);
|
||||
mgr.setPartitioned(true);
|
||||
|
||||
HttpCookie cookie = mgr.getSessionManager().getSessionCookie(session, false);
|
||||
assertEquals("SPECIAL", cookie.getName());
|
||||
|
@ -200,11 +202,12 @@ public class SessionHandlerTest
|
|||
assertEquals("/foo", cookie.getPath());
|
||||
assertFalse(cookie.isHttpOnly());
|
||||
assertFalse(cookie.isSecure());
|
||||
assertTrue(cookie.isPartitioned());
|
||||
assertEquals(99, cookie.getMaxAge());
|
||||
assertEquals(HttpCookie.SameSite.STRICT, cookie.getSameSite());
|
||||
|
||||
String cookieStr = HttpCookieUtils.getRFC6265SetCookie(cookie);
|
||||
assertThat(cookieStr, containsString("; SameSite=Strict"));
|
||||
assertThat(cookieStr, containsString("; Partitioned; SameSite=Strict"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
12
pom.xml
12
pom.xml
|
@ -126,14 +126,14 @@
|
|||
</modules>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:https://github.com/eclipse/jetty.project.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:eclipse/jetty.project.git</developerConnection>
|
||||
<url>https://github.com/eclipse/jetty.project</url>
|
||||
<connection>scm:git:https://github.com/jetty/jetty.project.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:jetty/jetty.project.git</developerConnection>
|
||||
<url>https://github.com/jetty/jetty.project</url>
|
||||
</scm>
|
||||
|
||||
<issueManagement>
|
||||
<system>github</system>
|
||||
<url>https://github.com/eclipse/jetty.project/issues</url>
|
||||
<url>https://github.com/jetty/jetty.project/issues</url>
|
||||
</issueManagement>
|
||||
|
||||
<distributionManagement>
|
||||
|
@ -1588,8 +1588,8 @@
|
|||
<attributes>
|
||||
<JDURL>https://eclipse.dev/jetty/javadoc/jetty-12</JDURL>
|
||||
<SRCDIR>${basedir}/..</SRCDIR>
|
||||
<GITBROWSEURL>https://github.com/eclipse/jetty.project/tree/jetty-12.0.x</GITBROWSEURL>
|
||||
<GITDOCURL>https://github.com/eclipse/jetty.project/tree/jetty-12.0.x/documentation/jetty-documentation/src/main/asciidoc</GITDOCURL>
|
||||
<GITBROWSEURL>https://github.com/jetty/jetty.project/tree/jetty-12.0.x</GITBROWSEURL>
|
||||
<GITDOCURL>https://github.com/jetty/jetty.project/tree/jetty-12.0.x/documentation/jetty-documentation/src/main/asciidoc</GITDOCURL>
|
||||
<MVNCENTRAL>http://central.maven.org/maven2</MVNCENTRAL>
|
||||
<VERSION>${project.version}</VERSION>
|
||||
<TIMESTAMP>${maven.build.timestamp}</TIMESTAMP>
|
||||
|
|
Loading…
Reference in New Issue