diff --git a/oap-application/oap-application/src/main/java/oap/application/ModuleHelper.java b/oap-application/oap-application/src/main/java/oap/application/ModuleHelper.java index 0a955e4674..3463018f78 100644 --- a/oap-application/oap-application/src/main/java/oap/application/ModuleHelper.java +++ b/oap-application/oap-application/src/main/java/oap/application/ModuleHelper.java @@ -286,7 +286,10 @@ private static void loadOnlyMainModuleAndDependsOn( ModuleItemTree modules, modules.remove( module ); - loadOnlyMainModuleAndDependsOn( modules, moduleItem.module.dependsOn, loaded ); + if( !moduleItem.module.dependsOn.isEmpty() ) { + log.debug( "[loadOnlyMainModuleAndDependsOn] module {} -> dependsOn {}", moduleItem.getName(), moduleItem.module.dependsOn ); + loadOnlyMainModuleAndDependsOn( modules, moduleItem.module.dependsOn, loaded ); + } } } } diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java index 18d6336abe..3ba39e0ffd 100644 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java +++ b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java @@ -23,29 +23,61 @@ */ package oap.http.test; +import com.google.common.base.Preconditions; import lombok.EqualsAndHashCode; +import lombok.SneakyThrows; import lombok.ToString; import lombok.extern.slf4j.Slf4j; import oap.http.Client; import oap.http.Cookie; +import oap.http.InputStreamRequestBody; +import oap.http.Uri; import oap.json.JsonException; import oap.json.testng.JsonAsserts; +import oap.testng.Asserts; import oap.util.BiStream; +import oap.util.Lists; +import oap.util.Maps; import oap.util.Pair; import oap.util.Stream; +import okhttp3.Dispatcher; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okhttp3.java.net.cookiejar.JavaNetCookieJar; +import org.apache.commons.lang3.StringUtils; +import org.assertj.core.api.AbstractIntegerAssert; import org.assertj.core.api.Assertions; import org.joda.time.DateTime; import org.testng.internal.collections.Ints; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.InputStream; +import java.io.UncheckedIOException; +import java.lang.reflect.Field; +import java.net.CookieManager; +import java.net.CookiePolicy; +import java.net.HttpCookie; +import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.Executors; import java.util.function.Consumer; import java.util.regex.Pattern; import static java.net.HttpURLConnection.HTTP_OK; import static oap.http.Http.ContentType.APPLICATION_JSON; +import static oap.http.Http.ContentType.APPLICATION_OCTET_STREAM; import static oap.http.test.HttpAsserts.HttpAssertion.assertHttpResponse; import static oap.http.test.HttpAsserts.JsonHttpAssertion.assertJsonResponse; import static oap.io.content.ContentReader.ofString; @@ -56,12 +88,32 @@ @Slf4j @SuppressWarnings( "unused" ) public class HttpAsserts { - private static final Client client = Client.custom() - .setMaxConnTotal( 100_000 ) - .setMaxConnPerRoute( 100_000 ) - .withCookieStore( new MockCookieStore() ) - .onError( ( c, e ) -> log.error( e.getMessage() ) ) - .build(); + public static final OkHttpClient OK_HTTP_CLIENT; + + private static final JavaNetCookieJar cookieJar; + private static final CookieManager cookieManager; + + private static Field whenCreatedField; + + static { + cookieManager = new CookieManager(); + cookieManager.setCookiePolicy( CookiePolicy.ACCEPT_ALL ); + + cookieJar = new JavaNetCookieJar( cookieManager ); + OK_HTTP_CLIENT = new OkHttpClient.Builder() + .cookieJar( cookieJar ) + .dispatcher( new Dispatcher( Executors.newVirtualThreadPerTaskExecutor() ) ) + .followRedirects( false ) + .followSslRedirects( false ) + .build(); + + try { + whenCreatedField = HttpCookie.class.getDeclaredField( "whenCreated" ); + whenCreatedField.setAccessible( true ); + } catch( NoSuchFieldException e ) { + throw new RuntimeException( e ); + } + } public static String httpPrefix( int port ) { return "http://localhost:" + port; @@ -72,106 +124,290 @@ public static String httpUrl( int port, String suffix ) { } public static void reset() { - client.reset(); + OK_HTTP_CLIENT.connectionPool().evictAll(); + cookieManager.getCookieStore().removeAll(); } - @SafeVarargs - public static HttpAssertion assertGet( String uri, Pair... params ) { - return new HttpAssertion( client.get( uri, params ) ); + public static HttpAssertion assertGet( String uri, Pair... params ) throws UncheckedIOException { + return assertGet( uri, Maps.of( params ), Map.of() ); } - public static HttpAssertion assertGet( String uri, Map params, Map headers ) { - return assertHttpResponse( client.get( uri, params, headers ) ); + public static HttpAssertion assertGet( String uri, Map params, Map headers ) throws UncheckedIOException { + try { + Request.Builder builder = new Request.Builder(); + + setHeaders( uri, headers, builder ); + + Request request = builder + .url( Uri.uri( uri, params ).toURL() ) + .get() + .build(); + + return getResponseAsHttpAssertion( request ); + } catch( IOException e ) { + throw new UncheckedIOException( e ); + } } - public static HttpAssertion assertPost( String uri, String content, Map headers ) { - return assertPost( uri, content, APPLICATION_JSON, headers ); + private static void setHeaders( String uri, Map headers, Request.Builder builder ) { + headers.forEach( ( k, v ) -> { + if( "Cookie".equalsIgnoreCase( k ) ) { + HttpUrl httpUrl = HttpUrl.parse( uri ); + Preconditions.checkNotNull( httpUrl ); + Preconditions.checkNotNull( v ); + + ArrayList cookies = new ArrayList<>(); + + List list = cookieJar.loadForRequest( httpUrl ); + cookies.addAll( list ); + + String[] setCookies = StringUtils.split( v.toString(), ";" ); + for( String setCookie : setCookies ) { + okhttp3.Cookie cookie = okhttp3.Cookie.parse( httpUrl, setCookie ); + Preconditions.checkNotNull( cookie ); + cookies.add( cookie ); + } + + cookieJar.saveFromResponse( httpUrl, cookies ); + } else { + builder.header( k, v == null ? "" : v.toString() ); + } + } ); } - public static HttpAssertion assertPost( String uri, String content ) { - return assertPost( uri, content, Map.of() ); + public static HttpAssertion assertPost( String uri, InputStream content, @Nullable String contentType, Map headers ) { + try { + Request.Builder builder = new Request.Builder(); + + setHeaders( uri, headers, builder ); + + RequestBody requestBody = new InputStreamRequestBody( contentType != null ? MediaType.get( contentType ) : null, content ); + + Request request = builder + .url( uri ) + .post( requestBody ) + .build(); + + return getResponseAsHttpAssertion( request ); + } catch( IOException e ) { + throw new UncheckedIOException( e ); + } } - public static HttpAssertion assertPost( String uri, String content, String contentType, Map headers ) { - return assertHttpResponse( client.post( uri, content, contentType, headers ) ); + public static HttpAssertion assertPost( String uri, InputStream content, @Nullable String contentType ) { + return assertPost( uri, content, contentType, Maps.of() ); } - public static HttpAssertion assertPost( String uri, String content, String contentType ) { - return assertPost( uri, content, contentType, Map.of() ); + public static HttpAssertion assertPost( String uri, String content, @Nullable String contentType, Map headers ) { + try { + Request.Builder builder = new Request.Builder(); + + setHeaders( uri, headers, builder ); + + RequestBody requestBody = RequestBody.create( content, contentType != null ? MediaType.parse( contentType ) : null ); + + Request request = builder + .url( uri ) + .post( requestBody ) + .build(); + + return getResponseAsHttpAssertion( request ); + } catch( IOException e ) { + throw new UncheckedIOException( e ); + } } - public static HttpAssertion assertPost( String uri, InputStream content, String contentType ) { - return assertHttpResponse( client.post( uri, content, contentType ) ); + public static HttpAssertion assertPost( String uri, String content ) { + return assertPost( uri, content, null, Maps.of() ); } - public static HttpAssertion assertPost( String uri, InputStream content, String contentType, Map headers ) { - return assertHttpResponse( client.post( uri, content, contentType, headers ) ); + public static HttpAssertion assertPost( String uri, String content, Map headers ) { + return assertPost( uri, content, null, headers ); } -// public static HttpAssertion assertUploadFile( String uri, RequestBody body ) { -// return new HttpAssertion( client.uploadFile( uri, body, Map.of() ) ); -// } + public static HttpAssertion assertPost( String uri, String content, String contentType ) { + return assertPost( uri, content, contentType, Maps.of() ); + } -// public static HttpAssertion assertUploadFile( String uri, RequestBody body, Map headers ) { -// return new HttpAssertion( client.uploadFile( uri, body, headers ) ); -// } + private static @Nonnull HttpAssertion getResponseAsHttpAssertion( Request request ) throws IOException { + try( Response response = OK_HTTP_CLIENT.newCall( request ).execute(); + ResponseBody body = response.body() ) { + + Headers responseHeaders = response.headers(); + ArrayList> headers = new ArrayList<>(); + responseHeaders.toMultimap().forEach( ( k, vs ) -> vs.forEach( v -> headers.add( Pair.__( k, v ) ) ) ); + byte[] bytes = body.bytes(); + MediaType mediaType = body.contentType(); + return new HttpAssertion( new Client.Response( + response.request().url().toString(), + response.code(), response.message(), headers, mediaType != null ? mediaType.toString() : APPLICATION_OCTET_STREAM, new ByteArrayInputStream( bytes ) ) ); + } + } public static HttpAssertion assertPut( String uri, String content, String contentType ) { - return assertHttpResponse( client.put( uri, content, contentType ) ); + return assertPut( uri, content, contentType, Maps.of() ); } public static HttpAssertion assertPut( String uri, String content, String contentType, Map headers ) { - return assertHttpResponse( client.put( uri, content, contentType, headers ) ); + try { + Request.Builder builder = new Request.Builder(); + + setHeaders( uri, headers, builder ); + + RequestBody requestBody = RequestBody.create( content, contentType != null ? MediaType.parse( contentType ) : null ); + + Request request = builder + .url( uri ) + .put( requestBody ) + .build(); + + return getResponseAsHttpAssertion( request ); + } catch( IOException e ) { + throw new UncheckedIOException( e ); + } } public static HttpAssertion assertPut( String uri, byte[] content, String contentType ) { - return assertHttpResponse( client.put( uri, content, contentType ) ); + return assertPut( uri, content, contentType, Maps.of() ); } public static HttpAssertion assertPut( String uri, byte[] content, String contentType, Map headers ) { - return assertHttpResponse( client.put( uri, content, contentType, headers ) ); + try { + Request.Builder builder = new Request.Builder(); + + setHeaders( uri, headers, builder ); + + RequestBody requestBody = RequestBody.create( content, contentType != null ? MediaType.parse( contentType ) : null ); + + Request request = builder + .url( uri ) + .put( requestBody ) + .build(); + + return getResponseAsHttpAssertion( request ); + } catch( IOException e ) { + throw new UncheckedIOException( e ); + } } public static HttpAssertion assertPut( String uri, InputStream is, String contentType ) { - return assertHttpResponse( client.put( uri, is, contentType ) ); + return assertPut( uri, is, contentType, Maps.of() ); } public static HttpAssertion assertPut( String uri, InputStream is, String contentType, Map headers ) { - return assertHttpResponse( client.put( uri, is, contentType, headers ) ); + try { + Request.Builder builder = new Request.Builder(); + + setHeaders( uri, headers, builder ); + + InputStreamRequestBody requestBody = new InputStreamRequestBody( contentType != null ? MediaType.parse( contentType ) : null, is ); + + Request request = builder + .url( uri ) + .put( requestBody ) + .build(); + + return getResponseAsHttpAssertion( request ); + } catch( IOException e ) { + throw new UncheckedIOException( e ); + } } public static HttpAssertion assertPatch( String uri, byte[] content, String contentType ) { - return assertHttpResponse( client.patch( uri, content, contentType ) ); + return assertPatch( uri, content, contentType, Maps.of() ); } public static HttpAssertion assertPatch( String uri, byte[] content, String contentType, Map headers ) { - return assertHttpResponse( client.patch( uri, content, contentType, headers ) ); + try { + Request.Builder builder = new Request.Builder(); + + setHeaders( uri, headers, builder ); + + RequestBody requestBody = RequestBody.create( content, contentType != null ? MediaType.parse( contentType ) : null ); + + Request request = builder + .url( uri ) + .patch( requestBody ) + .build(); + + return getResponseAsHttpAssertion( request ); + } catch( IOException e ) { + throw new UncheckedIOException( e ); + } } public static HttpAssertion assertPatch( String uri, String content, String contentType ) { - return assertHttpResponse( client.patch( uri, content, contentType ) ); + return assertPatch( uri, content, contentType, Maps.of() ); } public static HttpAssertion assertPatch( String uri, String content, String contentType, Map headers ) { - return assertHttpResponse( client.patch( uri, content, contentType, headers ) ); + try { + Request.Builder builder = new Request.Builder(); + + setHeaders( uri, headers, builder ); + + RequestBody requestBody = RequestBody.create( content, contentType != null ? MediaType.parse( contentType ) : null ); + + Request request = builder + .url( uri ) + .patch( requestBody ) + .build(); + + return getResponseAsHttpAssertion( request ); + } catch( IOException e ) { + throw new UncheckedIOException( e ); + } } public static HttpAssertion assertPatch( String uri, InputStream is, String contentType ) { - return assertHttpResponse( client.patch( uri, is, contentType ) ); + return assertPatch( uri, is, contentType, Maps.of() ); } public static HttpAssertion assertPatch( String uri, InputStream is, String contentType, Map headers ) { - return assertHttpResponse( client.patch( uri, is, contentType, headers ) ); + try { + Request.Builder builder = new Request.Builder(); + + setHeaders( uri, headers, builder ); + + InputStreamRequestBody requestBody = new InputStreamRequestBody( contentType != null ? MediaType.parse( contentType ) : null, is ); + + Request request = builder + .url( uri ) + .patch( requestBody ) + .build(); + + return getResponseAsHttpAssertion( request ); + } catch( IOException e ) { + throw new UncheckedIOException( e ); + } + } + + public static HttpAssertion assertDelete( String uri, Map headers ) { + try { + Request.Builder builder = new Request.Builder(); + + setHeaders( uri, headers, builder ); + + Request request = builder + .url( uri ) + .delete() + .build(); + + return getResponseAsHttpAssertion( request ); + } catch( IOException e ) { + throw new UncheckedIOException( e ); + } } public static HttpAssertion assertDelete( String uri ) { return assertDelete( uri, Map.of() ); } - public static HttpAssertion assertDelete( String uri, Map headers ) { - return assertHttpResponse( client.delete( uri, headers ) ); + @SneakyThrows + private static long whenCreatedFieldGet( HttpCookie cookie ) { + return ( long ) whenCreatedField.get( cookie ); } @EqualsAndHashCode @@ -271,7 +507,7 @@ public HttpAssertion containsHeader( String name, String value ) { } public HttpAssertion containsCookie( String name, Consumer assertion ) { - Optional cookie = Stream.of( cookies() ).filter( c -> c.getName().equalsIgnoreCase( name ) ).findAny(); + Optional cookie = Stream.of( getCookies() ).filter( c -> c.getName().equalsIgnoreCase( name ) ).findAny(); Assertions.assertThat( cookie ) .isNotEmpty() .withFailMessage( "no such cookie: " + name ) @@ -281,7 +517,7 @@ public HttpAssertion containsCookie( String name, Consumer assertion ) { } public HttpAssertion containsCookie( Cookie cookie ) { - Assertions.assertThat( cookies() ).contains( cookie ); + Assertions.assertThat( getCookies() ).contains( cookie ); return this; } @@ -289,7 +525,16 @@ public HttpAssertion containsCookie( String cookie ) { return containsCookie( Cookie.parseSetCookieHeader( cookie ) ); } - private List cookies() { + public HttpAssertion cookies( Consumer cons ) { + HttpUrl httpUrl = HttpUrl.parse( response.url ); + assertThat( httpUrl ).isNotNull(); + List cookies = cookieManager.getCookieStore().get( httpUrl.uri() ); + + cons.accept( new CookiesHttpAssertion( cookies ) ); + return this; + } + + private List getCookies() { return BiStream.of( response.headers ) .filter( ( name, value ) -> "Set-Cookie".equalsIgnoreCase( name ) ) .mapToObj( ( name, value ) -> Cookie.parseSetCookieHeader( value ) ) @@ -349,6 +594,36 @@ public T unmarshal( Class clazz ) { throw Assertions.fail( e.getMessage(), e ); } } + + public Asserts.StringAssertion body() { + return assertString( response.contentString() ); + } + } + + public static final class CookiesHttpAssertion { + private final List cookies; + + @SneakyThrows + public CookiesHttpAssertion( List cookies ) { + this.cookies = Lists.map( cookies, c -> Cookie.builder( c.getName(), c.getValue() ) + .withPath( c.getPath() ) + .withDomain( c.getDomain() ) + .withMaxAge( c.getMaxAge() < 0 ? null : ( int ) c.getMaxAge() ) + .withExpires( c.getMaxAge() <= 0 ? null : new Date( ( whenCreatedFieldGet( c ) + c.getMaxAge() ) * 1000L ) ) + .withDiscard( c.getDiscard() ) + .withSecure( c.getSecure() ) + .withHttpOnly( c.isHttpOnly() ) + .withVersion( c.getVersion() ) + .withComment( c.getComment() ) + .build() ); + } + + public CookieHttpAssertion cookie( String name ) { + Optional cookie = Stream.of( cookies ).filter( c -> c.getName().equalsIgnoreCase( name ) ).findAny(); + + assertThat( cookie ).isPresent(); + return CookieHttpAssertion.assertCookie( cookie.get() ); + } } public static final class CookieHttpAssertion { @@ -391,6 +666,10 @@ public CookieHttpAssertion hasPath( String path ) { return this; } + public AbstractIntegerAssert maxAge() { + return assertThat( cookie.getMaxAge() ); + } + public CookieHttpAssertion hasNotMaxAge() { return hasMaxAge( -1 ); } diff --git a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/health/HealthHttpHandlerTest.java b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/health/HealthHttpHandlerTest.java index 1863c4c76c..da0fd1fa5e 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/health/HealthHttpHandlerTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/health/HealthHttpHandlerTest.java @@ -24,9 +24,6 @@ package oap.http.server.nio.health; -//import oap.application.module.Module; -//import oap.application.testng.KernelFixture; - import oap.http.server.nio.NioHttpServer; import oap.http.test.HttpAsserts; import oap.testng.Fixtures; @@ -40,16 +37,6 @@ import static oap.http.test.HttpAsserts.assertGet; public class HealthHttpHandlerTest extends Fixtures { - public HealthHttpHandlerTest() { -// fixture( new KernelFixture( -// urlOfTestResource( getClass(), "application.test.conf" ), -// Lists.concat( -// List.of( urlOfTestResource( getClass(), "oap-module.oap" ) ), -// Module.CONFIGURATION.urlsFromClassPath() -// ) -// ) ); - } - @Test public void health() throws IOException { int httpPort = Ports.getFreePort( getClass() ); diff --git a/oap-http/oap-http/pom.xml b/oap-http/oap-http/pom.xml index 849d0fbcea..d5eb23fe59 100644 --- a/oap-http/oap-http/pom.xml +++ b/oap-http/oap-http/pom.xml @@ -81,10 +81,11 @@ com.squareup.okhttp3 okhttp-jvm - - 5.1.0 - + + com.squareup.okhttp3 + okhttp-java-net-cookiejar + org.projectlombok lombok diff --git a/oap-http/oap-http/src/main/java/oap/http/Client.java b/oap-http/oap-http/src/main/java/oap/http/Client.java index 5c327d904b..d6b85e0d16 100644 --- a/oap-http/oap-http/src/main/java/oap/http/Client.java +++ b/oap-http/oap-http/src/main/java/oap/http/Client.java @@ -441,17 +441,18 @@ private CompletableFuture execute( HttpUriRequest request, Map request.setHeader( name, value == null ? "" : value.toString() ) ); - var completableFuture = new CompletableFuture(); + CompletableFuture completableFuture = new CompletableFuture(); client.execute( request, new FutureCallback<>() { @Override public void completed( HttpResponse response ) { try { - var responseHeaders = headers( response ); + List> responseHeaders = headers( response ); Response result; if( response.getEntity() != null ) { var entity = response.getEntity(); result = new Response( + request.getURI().toString(), response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase(), responseHeaders, @@ -461,6 +462,7 @@ public void completed( HttpResponse response ) { entity.getContent() ); } else result = new Response( + request.getURI().toString(), response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase(), responseHeaders @@ -560,6 +562,7 @@ public void close() { @ToString( exclude = { "inputStream", "content" }, doNotUseGetters = true ) public static class Response implements Closeable, AutoCloseable { + public final String url; public final int code; public final String reasonPhrase; public final String contentType; @@ -567,7 +570,8 @@ public static class Response implements Closeable, AutoCloseable { private InputStream inputStream; private volatile byte[] content = null; - public Response( int code, String reasonPhrase, List> headers, @Nonnull String contentType, InputStream inputStream ) { + public Response( String url, int code, String reasonPhrase, List> headers, @Nonnull String contentType, InputStream inputStream ) { + this.url = url; this.code = code; this.reasonPhrase = reasonPhrase; this.headers = headers; @@ -575,8 +579,8 @@ public Response( int code, String reasonPhrase, List> heade this.inputStream = inputStream; } - public Response( int code, String reasonPhrase, List> headers ) { - this( code, reasonPhrase, headers, BiStream.of( headers ) + public Response( String url, int code, String reasonPhrase, List> headers ) { + this( url, code, reasonPhrase, headers, BiStream.of( headers ) .filter( ( name, value ) -> "Content-type".equalsIgnoreCase( name ) ) .mapToObj( ( name, value ) -> value ) .findAny() diff --git a/oap-http/oap-http/src/main/java/oap/http/InputStreamRequestBody.java b/oap-http/oap-http/src/main/java/oap/http/InputStreamRequestBody.java new file mode 100644 index 0000000000..110f2530b4 --- /dev/null +++ b/oap-http/oap-http/src/main/java/oap/http/InputStreamRequestBody.java @@ -0,0 +1,35 @@ +package oap.http; + +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; + +import java.io.IOException; +import java.io.InputStream; + +public class InputStreamRequestBody extends RequestBody { + private final MediaType mediaType; + private final InputStream inputStream; + + public InputStreamRequestBody( MediaType mediaType, InputStream inputStream ) { + this.mediaType = mediaType; + this.inputStream = inputStream; + } + + @Override + public MediaType contentType() { + return mediaType; + } + + // You can override contentLength() if you know the length in advance for better performance/features (e.g., S3 uploads). + // If you don't override it, OkHttp will use chunked transfer encoding for large bodies. + + @Override + public void writeTo( BufferedSink sink ) throws IOException { + try( Source source = Okio.source( inputStream ) ) { + sink.writeAll( source ); + } + } +} diff --git a/oap-http/oap-pnio-v3/src/main/java/oap/http/pniov3/PnioResponseBuffer.java b/oap-http/oap-pnio-v3/src/main/java/oap/http/pniov3/PnioResponseBuffer.java index a3fff4745e..c7aa99907d 100644 --- a/oap-http/oap-pnio-v3/src/main/java/oap/http/pniov3/PnioResponseBuffer.java +++ b/oap-http/oap-pnio-v3/src/main/java/oap/http/pniov3/PnioResponseBuffer.java @@ -28,13 +28,14 @@ import javax.annotation.concurrent.NotThreadSafe; import java.io.OutputStream; +import java.nio.ByteBuffer; import static java.nio.charset.StandardCharsets.UTF_8; @NotThreadSafe public class PnioResponseBuffer { public int length; - byte[] buffer; + public byte[] buffer; public PnioResponseBuffer( int capacity ) { this.buffer = new byte[capacity]; @@ -42,7 +43,11 @@ public PnioResponseBuffer( int capacity ) { } public String string() { - return new String( buffer, 0, length ); + return new String( buffer, 0, length, UTF_8 ); + } + + public ByteBuffer byteBuffer() { + return ByteBuffer.wrap( buffer, 0, length ); } public boolean isEmpty() { diff --git a/oap-jpath/src/main/java/oap/jpath/JPath.java b/oap-jpath/src/main/java/oap/jpath/JPath.java index 9749a78049..c0ade4ccbe 100644 --- a/oap-jpath/src/main/java/oap/jpath/JPath.java +++ b/oap-jpath/src/main/java/oap/jpath/JPath.java @@ -43,7 +43,7 @@ public static void evaluate( String expression, Map variables, J } public void evaluate( String expression, JPathOutput output ) { - var jPathParser = new JPathParser( new BufferedTokenStream( new JPathLexer( new ANTLRInputStream( expression ) ) ) ); + JPathParser jPathParser = new JPathParser( new BufferedTokenStream( new JPathLexer( new ANTLRInputStream( expression ) ) ) ); jPathParser.expr().expression.evaluate( variables, output ); } diff --git a/oap-jpath/src/main/java/oap/jpath/MapPointer.java b/oap-jpath/src/main/java/oap/jpath/MapPointer.java index 4c4a19f63a..c02d4de456 100644 --- a/oap-jpath/src/main/java/oap/jpath/MapPointer.java +++ b/oap-jpath/src/main/java/oap/jpath/MapPointer.java @@ -37,7 +37,7 @@ public MapPointer( Map map ) { @Override public Pointer resolve( AbstractPathNode n ) { if( n.type == PathType.FIELD ) { - var ret = v.get( n.name ); + Object ret = v.get( n.name ); return ret != null ? Pointer.get( ret ) : NullPointer.INSTANCE; } return super.resolve( n ); diff --git a/oap-jpath/src/main/java/oap/jpath/PathExpression.java b/oap-jpath/src/main/java/oap/jpath/PathExpression.java index cb3c23ffb6..950fa832cd 100644 --- a/oap-jpath/src/main/java/oap/jpath/PathExpression.java +++ b/oap-jpath/src/main/java/oap/jpath/PathExpression.java @@ -40,7 +40,7 @@ public void add( AbstractPathNode path ) { @SuppressWarnings( "unchecked" ) public void evaluate( Map variables, JPathOutput output ) { Pointer pointer = new MapPointer( ( Map ) ( Object ) variables ); - for( var n : list ) pointer = pointer.resolve( n ); + for( AbstractPathNode n : list ) pointer = pointer.resolve( n ); output.write( pointer ); } } diff --git a/oap-stdlib-test/src/main/java/oap/json/testng/JsonAsserts.java b/oap-stdlib-test/src/main/java/oap/json/testng/JsonAsserts.java index d147c7a42a..64e5aeb858 100644 --- a/oap-stdlib-test/src/main/java/oap/json/testng/JsonAsserts.java +++ b/oap-stdlib-test/src/main/java/oap/json/testng/JsonAsserts.java @@ -43,9 +43,9 @@ import static oap.io.content.ContentReader.ofJson; import static oap.io.content.ContentReader.ofString; import static oap.json.Binder.json; +import static oap.testng.Asserts.assertString; import static oap.testng.Asserts.contentOfTestResource; import static oap.util.Pair.__; -import static org.assertj.core.api.Assertions.assertThat; public class JsonAsserts { @Deprecated @@ -143,7 +143,7 @@ public JsonAssertion isEqualTo( String expected, Map substitutio .map( JsonAssertion::deepSort, JsonAssertion::deepSort ) .map( e -> json.marshal( e.isLeft() ? e.leftValue : e.rightValue, true ) ); - assertThat( actualJson ).isEqualTo( expectedJson ); + assertString( actualJson ).isEqualTo( expectedJson ); return this; } @@ -167,7 +167,7 @@ public JsonAssertion isEqualTo( String expected, Function substi .map( JsonAssertion::deepSort, JsonAssertion::deepSort ) .map( e -> json.marshal( e.isLeft() ? e.leftValue : e.rightValue, true ) ); - assertThat( actualJson ).isEqualTo( expectedJson ); + assertString( actualJson ).isEqualTo( expectedJson ); return this; } @@ -189,13 +189,13 @@ public JsonAssertion isStructurallyEqualToResource( Class contextClass, Strin } private JsonAssertion isEqualCanonically( Class clazz, String actual, String expected ) { - assertThat( json.canonicalizeWithDefaultPrettyPrinter( clazz, actual ) ) + assertString( json.canonicalizeWithDefaultPrettyPrinter( clazz, actual ) ) .isEqualTo( json.canonicalizeWithDefaultPrettyPrinter( clazz, expected ) ); return this; } private JsonAssertion isEqualCanonically( TypeRef typeRef, String actual, String expected ) { - assertThat( json.canonicalizeWithDefaultPrettyPrinter( typeRef, actual ) ) + assertString( json.canonicalizeWithDefaultPrettyPrinter( typeRef, actual ) ) .isEqualTo( json.canonicalizeWithDefaultPrettyPrinter( typeRef, expected ) ); return this; } diff --git a/oap-stdlib/pom.xml b/oap-stdlib/pom.xml index bd56aee4ea..833d605823 100644 --- a/oap-stdlib/pom.xml +++ b/oap-stdlib/pom.xml @@ -305,5 +305,26 @@ spotbugs-annotations ${oap.deps.spotbugs} + + + io.netty + netty-common + ${oap.deps.netty.version} + + + io.netty + netty-transport + ${oap.deps.netty.version} + + + io.netty + netty-buffer + ${oap.deps.netty.version} + + + io.netty + netty-handler + ${oap.deps.netty.version} + diff --git a/oap-storage/oap-storage-mongo-test/src/main/java/oap/storage/mongo/MongoFixture.java b/oap-storage/oap-storage-mongo-test/src/main/java/oap/storage/mongo/MongoFixture.java index 2ee96a7d05..b7f2b2a60c 100644 --- a/oap-storage/oap-storage-mongo-test/src/main/java/oap/storage/mongo/MongoFixture.java +++ b/oap-storage/oap-storage-mongo-test/src/main/java/oap/storage/mongo/MongoFixture.java @@ -29,9 +29,12 @@ import de.bwaldvogel.mongo.backend.memory.MemoryBackend; import lombok.extern.slf4j.Slf4j; import oap.testng.AbstractFixture; +import oap.util.Lists; import org.bson.Document; import org.jetbrains.annotations.NotNull; +import java.net.InetSocketAddress; +import java.util.List; import java.util.Map; import static oap.testng.Asserts.contentOfTestResource; @@ -71,7 +74,7 @@ protected void before() { this.server = createMongoServer(); log.info( "mongo port = {}", port ); - this.server.bind( HOST, port ); + this.server.bind( new InetSocketAddress( port ) ); this.mongoClient = createMongoClient(); } @@ -81,8 +84,8 @@ public MongoClient createMongoClient() { } @NotNull - public MongoClient createMongoClient( String migrationPackage ) { - return new MongoClient( getConnectionString(), migrationPackage ); + public MongoClient createMongoClient( String migrationPackage, String... migrationPackages ) { + return new MongoClient( getConnectionString(), Lists.concat( List.of( migrationPackage ), List.of( migrationPackages ) ) ); } @NotNull @@ -97,7 +100,7 @@ public String getConnectionString( String database ) { @NotNull protected MongoServer createMongoServer() { - return new MongoServer( new MemoryBackend().version( ServerVersion.MONGO_4_0 ) ); + return new MongoServer( new MemoryBackend().version( ServerVersion.MONGO_5_0 ) ); } @Override diff --git a/oap-storage/oap-storage-mongo/pom.xml b/oap-storage/oap-storage-mongo/pom.xml index d5dacf58bc..d6ddb34e46 100644 --- a/oap-storage/oap-storage-mongo/pom.xml +++ b/oap-storage/oap-storage-mongo/pom.xml @@ -56,16 +56,6 @@ org.apache.commons commons-exec - - io.netty - netty-common - ${oap.deps.netty.version} - - - io.netty - netty-buffer - ${oap.deps.netty.version} - io.mongock mongock-standalone @@ -77,6 +67,13 @@ ${oap.deps.mongock.version} + + oap + oap-stdlib-test + ${project.version} + test + + org.projectlombok lombok diff --git a/oap-storage/oap-storage-mongo/src/main/java/oap/storage/MongoPersistence.java b/oap-storage/oap-storage-mongo/src/main/java/oap/storage/MongoPersistence.java index 0d8f75befe..206fed21b1 100644 --- a/oap-storage/oap-storage-mongo/src/main/java/oap/storage/MongoPersistence.java +++ b/oap-storage/oap-storage-mongo/src/main/java/oap/storage/MongoPersistence.java @@ -136,7 +136,7 @@ protected void processRecords( CountDownLatch cdl ) { @Override protected void load() { log.debug( "loading data from {}", collection.getNamespace() ); - Consumer> cons = metadata -> storage.memory.put( storage.identifier.get( metadata.object ), metadata ); + Consumer> cons = metadata -> storage.memory.put( storage.identifier.getOrInit( metadata.object, _ -> false ), metadata ); log.info( "Loading documents from [{}] MongoDB table", collection.getNamespace() ); collection.find().forEach( cons ); log.info( storage.size() + " object(s) loaded." ); diff --git a/oap-storage/oap-storage-mongo/src/main/java/oap/storage/mongo/MigrationUtils.java b/oap-storage/oap-storage-mongo/src/main/java/oap/storage/mongo/MigrationUtils.java new file mode 100644 index 0000000000..2596ff9b6b --- /dev/null +++ b/oap-storage/oap-storage-mongo/src/main/java/oap/storage/mongo/MigrationUtils.java @@ -0,0 +1,27 @@ +package oap.storage.mongo; + +import org.apache.commons.lang3.StringUtils; +import org.bson.Document; + +public class MigrationUtils { + public static String getString( Document document, String key ) { + String[] keys = StringUtils.split( key, "." ); + + Document current = document; + Object lastValue = null; + + for( String field : keys ) { + if( lastValue != null ) { + if( lastValue instanceof Document doc ) { + current = doc; + } else { + return null; + } + } + + lastValue = current.get( field ); + } + + return ( String ) lastValue; + } +} diff --git a/oap-storage/oap-storage-mongo/src/main/java/oap/storage/mongo/MongoClient.java b/oap-storage/oap-storage-mongo/src/main/java/oap/storage/mongo/MongoClient.java index 28d0b11e08..b18e127286 100644 --- a/oap-storage/oap-storage-mongo/src/main/java/oap/storage/mongo/MongoClient.java +++ b/oap-storage/oap-storage-mongo/src/main/java/oap/storage/mongo/MongoClient.java @@ -42,6 +42,7 @@ import javax.annotation.Nonnull; import java.io.Closeable; import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -52,7 +53,7 @@ public class MongoClient implements Closeable { final com.mongodb.client.MongoClient mongoClient; private final MongoDatabase database; - private final String migrationPackage; + private final List migrationPackage; public boolean throwIfMigrationFailed = true; private ConnectionString connectionString; @@ -60,7 +61,7 @@ public MongoClient( String connectionString ) { this( connectionString, null ); } - public MongoClient( String connectionString, @Nonnull String migrationPackage ) { + public MongoClient( String connectionString, @Nonnull List migrationPackage ) { this.connectionString = new ConnectionString( connectionString ); this.migrationPackage = migrationPackage; @@ -70,8 +71,7 @@ public MongoClient( String connectionString, @Nonnull String migrationPackage ) .applyConnectionString( this.connectionString ); this.mongoClient = MongoClients.create( settingsBuilder.build() ); this.database = mongoClient.getDatabase( this.connectionString.getDatabase() ); - log.debug( "creating connectionString {} migrationPackage {}", - this.connectionString, migrationPackage ); + log.debug( "creating connectionString {} migrationPackage {}", this.connectionString, migrationPackage ); } private MongoClientSettings.Builder defaultBuilder() { @@ -114,7 +114,7 @@ public void preStart() { if( migrationPackage != null ) { MongockStandalone .builder() - .addMigrationScanPackage( migrationPackage ) + .addMigrationScanPackages( migrationPackage ) .setDriver( driver ) .buildRunner() .execute(); diff --git a/oap-storage/oap-storage-mongo/src/main/resources/META-INF/oap-module.oap b/oap-storage/oap-storage-mongo/src/main/resources/META-INF/oap-module.oap index 384a075f42..806b9c6abe 100644 --- a/oap-storage/oap-storage-mongo/src/main/resources/META-INF/oap-module.oap +++ b/oap-storage/oap-storage-mongo/src/main/resources/META-INF/oap-module.oap @@ -5,7 +5,7 @@ services { parameters { connectionString = "mongodb://:/" throwIfMigrationFailed = true -// migrationPackage = "my,migrations" +// migrationPackage = ["my.migrations"] // port = ... // host = ... // database = ... diff --git a/oap-storage/oap-storage-mongo/src/test/java/oap/storage/mongo/MigrationUtilsTest.java b/oap-storage/oap-storage-mongo/src/test/java/oap/storage/mongo/MigrationUtilsTest.java new file mode 100644 index 0000000000..64e23815ee --- /dev/null +++ b/oap-storage/oap-storage-mongo/src/test/java/oap/storage/mongo/MigrationUtilsTest.java @@ -0,0 +1,31 @@ +package oap.storage.mongo; + +import org.bson.Document; +import org.testng.annotations.Test; + +import static oap.testng.Asserts.assertString; + +public class MigrationUtilsTest { + @Test + public void testGetString() { + Document document = Document.parse( """ + { + a: "123", + b: { + ba: "12", + bb: { + bbv: "123+" + } + } + } + """ ); + + + assertString( MigrationUtils.getString( document, "a" ) ).isEqualTo( "123" ); + assertString( MigrationUtils.getString( document, "unknown" ) ).isNull(); + assertString( MigrationUtils.getString( document, "a.b.c" ) ).isNull(); + assertString( MigrationUtils.getString( document, "b.bb.bbv" ) ).isEqualTo( "123+" ); + assertString( MigrationUtils.getString( document, "b.bb.unk" ) ).isNull(); + assertString( MigrationUtils.getString( document, "b.unk.unk" ) ).isNull(); + } +} diff --git a/oap-storage/pom.xml b/oap-storage/pom.xml index 14590617a2..0775a2896c 100644 --- a/oap-storage/pom.xml +++ b/oap-storage/pom.xml @@ -13,7 +13,6 @@ 5.4.0 - 4.2.4.Final 5.5.1 2.23.17 diff --git a/oap-ws/oap-ws-admin-ws/pom.xml b/oap-ws/oap-ws-admin-ws/pom.xml index a016c31b6f..cc0a711c48 100644 --- a/oap-ws/oap-ws-admin-ws/pom.xml +++ b/oap-ws/oap-ws-admin-ws/pom.xml @@ -34,6 +34,12 @@ ${project.version} test + + oap + oap-stdlib-test + ${project.version} + test + org.projectlombok diff --git a/oap-ws/oap-ws-admin-ws/src/main/java/oap/ws/admin/JPathWS.java b/oap-ws/oap-ws-admin-ws/src/main/java/oap/ws/admin/JPathWS.java index 757b2c4b62..d8cd15859c 100644 --- a/oap-ws/oap-ws-admin-ws/src/main/java/oap/ws/admin/JPathWS.java +++ b/oap-ws/oap-ws-admin-ws/src/main/java/oap/ws/admin/JPathWS.java @@ -29,9 +29,11 @@ import oap.application.Kernel; import oap.http.Http; import oap.jpath.JPath; +import oap.jpath.NullPointer; import oap.ws.Response; import oap.ws.WsMethod; import oap.ws.WsParam; +import org.apache.commons.lang3.StringUtils; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; @@ -53,7 +55,34 @@ public Response get( @WsParam( from = QUERY ) String query ) { log.debug( "query = {}", query ); try { AtomicReference result = new AtomicReference<>(); - JPath.evaluate( query, ( Map ) ( Object ) kernel.services.moduleMap, pointer -> result.set( pointer.get() ) ); + + String[] fields = StringUtils.split( query, '.' ); + if( fields.length > 0 ) { + JPath.evaluate( "${" + fields[0] + "}", ( Map ) ( Object ) kernel.services.moduleMap, pointer -> { + if( !( pointer instanceof NullPointer ) ) { + result.set( fields[0] ); + } + } ); + } + + if( result.get() == null ) { + return new Response( Http.StatusCode.BAD_REQUEST ).withBody( "unknown module " + fields[0] ); + } + result.set( null ); + + if( fields.length > 1 ) { + JPath.evaluate( "${" + fields[0] + "." + fields[1] + "}", ( Map ) ( Object ) kernel.services.moduleMap, pointer -> { + if( !( pointer instanceof NullPointer ) ) { + result.set( fields[1] ); + } + } ); + } + if( result.get() == null ) { + return new Response( Http.StatusCode.BAD_REQUEST ).withBody( "unknown module service " + fields[0] + "." + fields[1] ); + } + result.set( null ); + + JPath.evaluate( "${" + query + "}", ( Map ) ( Object ) kernel.services.moduleMap, pointer -> result.set( pointer.get() ) ); return Response.jsonOk().withBody( result.get(), false ); } catch( Exception e ) { log.error( e.getMessage(), e ); diff --git a/oap-ws/oap-ws-admin-ws/src/test/java/oap/ws/admin/JPathWSTest.java b/oap-ws/oap-ws-admin-ws/src/test/java/oap/ws/admin/JPathWSTest.java index 4a9b2cfdb5..341a18fd9e 100644 --- a/oap-ws/oap-ws-admin-ws/src/test/java/oap/ws/admin/JPathWSTest.java +++ b/oap-ws/oap-ws-admin-ws/src/test/java/oap/ws/admin/JPathWSTest.java @@ -27,13 +27,12 @@ import oap.application.testng.KernelFixture; import oap.testng.Fixtures; import oap.testng.TestDirectoryFixture; -import org.testng.annotations.Ignore; import org.testng.annotations.Test; +import static oap.http.Http.StatusCode.BAD_REQUEST; import static oap.http.test.HttpAsserts.assertGet; import static oap.io.Resources.urlOrThrow; -@Ignore public class JPathWSTest extends Fixtures { private final KernelFixture kernel; @@ -58,4 +57,14 @@ public void testPublicFieldAccess() { .isOk() .hasBody( "\"testv\"" ); } + + @Test + public void testUnknownModule() { + assertGet( kernel.httpUrl( "/system/admin/jpath?query=unknown-module.test-service.instance.value" ) ) + .hasCode( BAD_REQUEST ) + .hasBody( "unknown module unknown-module" ); + assertGet( kernel.httpUrl( "/system/admin/jpath?query=oap-ws-admin-ws-test.unknown-service.instance.value" ) ) + .hasCode( BAD_REQUEST ) + .hasBody( "unknown module service oap-ws-admin-ws-test.unknown-service" ); + } } diff --git a/oap-ws/oap-ws-api-ws/src/test/java/oap/ws/api/ApiWSTest.java b/oap-ws/oap-ws-api-ws/src/test/java/oap/ws/api/ApiWSTest.java index 8b5334ec33..73295edb74 100644 --- a/oap-ws/oap-ws-api-ws/src/test/java/oap/ws/api/ApiWSTest.java +++ b/oap-ws/oap-ws-api-ws/src/test/java/oap/ws/api/ApiWSTest.java @@ -30,6 +30,8 @@ import oap.testng.TestDirectoryFixture; import org.testng.annotations.Test; +import java.io.IOException; + import static oap.http.Http.ContentType.TEXT_PLAIN; import static oap.http.Http.StatusCode.OK; import static oap.http.test.HttpAsserts.assertGet; @@ -47,7 +49,7 @@ public ApiWSTest() { } @Test - public void api() { + public void api() throws IOException { assertGet( kernel.httpUrl( "/system/api" ) ) .responded( OK, "OK", TEXT_PLAIN, contentOfTestResource( getClass(), "api.txt", ofString() ) ); diff --git a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/JwtToken.java b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/JwtToken.java index 9175515b05..e34af8cd39 100644 --- a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/JwtToken.java +++ b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/JwtToken.java @@ -18,6 +18,11 @@ public JwtToken( DecodedJWT decodedJWT, SecurityRoles roles ) { this.roles = roles; } + public String getUserId() { + final Claim id = decodedJWT.getClaims().get( "id" ); + return id != null ? id.asString() : null; + } + public String getUserEmail() { final Claim user = decodedJWT.getClaims().get( "user" ); return user != null ? user.asString() : null; diff --git a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/JwtTokenGenerator.java b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/JwtTokenGenerator.java index 216d997713..d89b9eb7b5 100644 --- a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/JwtTokenGenerator.java +++ b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/JwtTokenGenerator.java @@ -53,6 +53,7 @@ public Authentication.Token generateAccessToken( User user ) throws JWTCreationE Algorithm algorithm = Algorithm.HMAC256( accessSecret ); Date expiresAt = new org.joda.time.DateTime( DateTimeUtils.currentTimeMillis() + accessSecretExpiration, UTC ).toDate(); return new Authentication.Token( expiresAt, JWT.create() + .withClaim( "id", user.getId() ) .withClaim( "user", user.getEmail() ) .withClaim( "roles", user.getRoles() ) .withClaim( "counter", user.getCounter() ) @@ -65,6 +66,7 @@ public Authentication.Token generateAccessTokenWithActiveOrgId( User user, Strin Algorithm algorithm = Algorithm.HMAC256( accessSecret ); Date expiresAt = new org.joda.time.DateTime( DateTimeUtils.currentTimeMillis() + accessSecretExpiration, UTC ).toDate(); return new Authentication.Token( expiresAt, JWT.create() + .withClaim( "id", user.getId() ) .withClaim( "user", user.getEmail() ) .withClaim( "roles", user.getRoles() ) .withClaim( "counter", user.getCounter() ) @@ -78,6 +80,7 @@ public Authentication.Token generateRefreshToken( User user ) throws JWTCreation Algorithm algorithm = Algorithm.HMAC256( refreshSecret ); Date expiresAt = new org.joda.time.DateTime( DateTimeUtils.currentTimeMillis() + refreshSecretExpiration, UTC ).toDate(); return new Authentication.Token( expiresAt, JWT.create() + .withClaim( "id", user.getId() ) .withClaim( "user", user.getEmail() ) .withClaim( "counter", user.getCounter() ) .withIssuer( issuer ) diff --git a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java index e6c0e7854d..55f3b314c1 100644 --- a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java +++ b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java @@ -31,6 +31,7 @@ import oap.ws.Response; import oap.ws.SessionManager; import org.joda.time.DateTime; +import org.joda.time.Duration; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -65,13 +66,16 @@ public static Optional getRefreshAuthentication( HttpServerExchange exch } public static Response authenticatedResponse( Authentication authentication, String cookieDomain, Boolean cookieSecure ) { + long accessTokenMaxAge = new Duration( new DateTime( UTC ), new DateTime( authentication.accessToken.expires, UTC ) ).getStandardSeconds(); + long refreshTokenMaxAge = new Duration( new DateTime( UTC ), new DateTime( authentication.refreshToken.expires, UTC ) ).getStandardSeconds(); + return Response .jsonOk() .withHeader( AUTHENTICATION_KEY, authentication.accessToken.jwt ) .withCookie( Cookie.builder( AUTHENTICATION_KEY, authentication.accessToken.jwt ) .withDomain( cookieDomain ) .withPath( "/" ) - .withExpires( new DateTime( authentication.accessToken.expires ) ) + .withMaxAge( ( int ) accessTokenMaxAge ) .withHttpOnly( true ) .withSecure( cookieSecure ) .build() @@ -79,7 +83,7 @@ public static Response authenticatedResponse( Authentication authentication, Str .withCookie( Cookie.builder( REFRESH_TOKEN_KEY, authentication.refreshToken.jwt ) .withDomain( cookieDomain ) .withPath( "/" ) - .withExpires( new DateTime( authentication.refreshToken.expires ) ) + .withMaxAge( ( int ) refreshTokenMaxAge ) .withHttpOnly( true ) .withSecure( cookieSecure ) .build() diff --git a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/User.java b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/User.java index 88ac21b7b6..8afd315238 100644 --- a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/User.java +++ b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/User.java @@ -29,6 +29,8 @@ import java.util.Optional; public interface User extends Serializable { + String getId(); + String getEmail(); Optional getRole( String realm ); diff --git a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/UserProvider.java b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/UserProvider.java index 0ba5747a7a..00b17ace71 100644 --- a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/UserProvider.java +++ b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/UserProvider.java @@ -56,7 +56,7 @@ static String toAccessKey( String email ) { Optional getUser( String email ); Result getAuthenticatedByAccessToken( Optional accessToken, Optional refreshToken, - Optional sessionUser, + Optional sessionUserId, SecurityRoles roles, String realm, String... wssPermissions ); Result getAuthenticated( String email, String password, Optional tfaCode ); diff --git a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/interceptor/JWTSecurityInterceptor.java b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/interceptor/JWTSecurityInterceptor.java index a050f53f7d..81036b5b2c 100644 --- a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/interceptor/JWTSecurityInterceptor.java +++ b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/interceptor/JWTSecurityInterceptor.java @@ -84,7 +84,7 @@ public Optional before( InvocationContext context ) { String realmString = realm.get(); String[] wssPermissions = wss.get().permissions(); - validUser = userProvider.getAuthenticatedByAccessToken( Optional.ofNullable( accessToken ), refreshToken, sessionUserKey.map( User::getEmail ), roles, realmString, wssPermissions ); + validUser = userProvider.getAuthenticatedByAccessToken( Optional.ofNullable( accessToken ), refreshToken, sessionUserKey.map( User::getId ), roles, realmString, wssPermissions ); if( !validUser.isSuccess() ) { return Optional.of( new Response( UNAUTHORIZED, validUser.failureValue ) ); diff --git a/oap-ws/oap-ws-sso-api/src/test/java/oap/ws/sso/AbstractUserTest.java b/oap-ws/oap-ws-sso-api/src/test/java/oap/ws/sso/AbstractUserTest.java index 9b98e7fcf9..e0206cf6d4 100644 --- a/oap-ws/oap-ws-sso-api/src/test/java/oap/ws/sso/AbstractUserTest.java +++ b/oap-ws/oap-ws-sso-api/src/test/java/oap/ws/sso/AbstractUserTest.java @@ -26,6 +26,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; +import oap.id.Identifier; import oap.util.Pair; import org.apache.commons.lang3.RandomStringUtils; @@ -47,6 +48,7 @@ protected TestSecurityRolesProvider() { @ToString @EqualsAndHashCode public static class TestUser implements User { + public final String id; public final String email; public final String password; public final Map roles = new HashMap<>(); @@ -61,12 +63,18 @@ public TestUser( String email, String password, Pair role ) { } public TestUser( String email, String password, Pair role, boolean tfaEnabled ) { + this.id = Identifier.generate( email, 20, s -> false, 0 ); this.email = email; this.password = password; this.roles.put( role._1, role._2 ); this.tfaEnabled = tfaEnabled; } + @Override + public String getId() { + return id; + } + @Override public String getEmail() { return email; diff --git a/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesSessionTest.java b/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesSessionTest.java index b1432f4915..368b2bf58b 100644 --- a/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesSessionTest.java +++ b/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesSessionTest.java @@ -26,6 +26,7 @@ import oap.application.testng.KernelFixture; import oap.http.Http; +import oap.http.test.HttpAsserts; import oap.testng.Fixtures; import oap.testng.TestDirectoryFixture; import org.testng.annotations.Test; @@ -49,7 +50,7 @@ public WebServicesSessionTest() { public void sessionViaResponse() { assertGet( kernel.httpUrl( "/session/put" ), Map.of( "value", "vvv" ), Map.of() ) .hasCode( Http.StatusCode.NO_CONTENT ); - assertGet( kernel.httpUrl( "/session/get" ) ) + HttpAsserts.assertGet( kernel.httpUrl( "/session/get" ) ) .isOk() .hasBody( "vvv" ); } @@ -58,7 +59,7 @@ public void sessionViaResponse() { public void sessionDirectly() { assertGet( kernel.httpUrl( "/session/putDirectly" ), Map.of( "value", "vvv" ), Map.of() ) .hasCode( Http.StatusCode.NO_CONTENT ); - assertGet( kernel.httpUrl( "/session/get" ) ) + HttpAsserts.assertGet( kernel.httpUrl( "/session/get" ) ) .isOk() .hasBody( "vvv" ); } @@ -67,7 +68,7 @@ public void sessionDirectly() { public void respondHtmlContentType() { assertGet( kernel.httpUrl( "/session/putDirectly" ), Map.of( "value", "vvv" ), Map.of() ) .hasCode( Http.StatusCode.NO_CONTENT ); - assertGet( kernel.httpUrl( "/session/html" ) ) + HttpAsserts.assertGet( kernel.httpUrl( "/session/html" ) ) .isOk() .hasBody( "vvv" ) .hasContentType( Http.ContentType.TEXT_HTML ); diff --git a/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesTest.java b/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesTest.java index 1ffc594cec..caef53d5a3 100644 --- a/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesTest.java +++ b/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesTest.java @@ -29,6 +29,7 @@ import oap.http.Http; import oap.http.server.nio.HttpHandler; import oap.http.server.nio.HttpServerExchange; +import oap.http.test.HttpAsserts; import oap.testng.Fixtures; import oap.testng.TestDirectoryFixture; import org.apache.commons.io.IOUtils; @@ -50,7 +51,6 @@ import static oap.http.Http.StatusCode.OK; import static oap.http.server.nio.HttpServerExchange.HttpMethod.GET; import static oap.http.test.HttpAsserts.assertGet; -import static oap.http.test.HttpAsserts.assertPost; import static oap.io.Resources.urlOrThrow; import static oap.util.Pair.__; import static oap.ws.WsParam.From.BODY; @@ -133,22 +133,22 @@ public void invocations() { @Test public void invocationBytes() { - assertPost( kernel.httpUrl( "/x/v/math/bytes" ), "1234", Http.ContentType.APPLICATION_OCTET_STREAM ) + HttpAsserts.assertPost( kernel.httpUrl( "/x/v/math/bytes" ), "1234", Http.ContentType.APPLICATION_OCTET_STREAM ) .responded( OK, "OK", APPLICATION_JSON, "\"1234\"" ); } @Test public void invocationString() { - assertPost( kernel.httpUrl( "/x/v/math/string" ), "1234", Http.ContentType.APPLICATION_OCTET_STREAM ) + HttpAsserts.assertPost( kernel.httpUrl( "/x/v/math/string" ), "1234", Http.ContentType.APPLICATION_OCTET_STREAM ) .responded( OK, "OK", APPLICATION_JSON, "\"1234\"" ); - assertPost( kernel.httpUrl( "/x/v/math/string" ), "1234", Http.ContentType.APPLICATION_OCTET_STREAM ) + HttpAsserts.assertPost( kernel.httpUrl( "/x/v/math/string" ), "1234", Http.ContentType.APPLICATION_OCTET_STREAM ) .satisfies( response -> assertThat( response.headers ) - .contains( __( "Content-Type", "application/json" ) ) ); + .contains( __( "content-type", "application/json" ) ) ); } @Test public void invocationInputStream() { - assertPost( kernel.httpUrl( "/x/v/math/inputStream" ), "1234", Http.ContentType.APPLICATION_OCTET_STREAM ) + HttpAsserts.assertPost( kernel.httpUrl( "/x/v/math/inputStream" ), "1234", Http.ContentType.APPLICATION_OCTET_STREAM ) .responded( OK, "OK", APPLICATION_JSON, "\"1234\"" ); } @@ -184,13 +184,13 @@ public void request() { @Test public void json() { - assertPost( kernel.httpUrl( "/x/v/math/json" ), "{\"i\":1,\"s\":\"sss\"}", APPLICATION_JSON ) + HttpAsserts.assertPost( kernel.httpUrl( "/x/v/math/json" ), "{\"i\":1,\"s\":\"sss\"}", APPLICATION_JSON ) .respondedJson( "{\"i\":1,\"s\":\"sss\"}" ); } @Test public void list() { - assertPost( kernel.httpUrl( "/x/v/math/list" ), "[\"1str\", \"2str\"]", APPLICATION_JSON ) + HttpAsserts.assertPost( kernel.httpUrl( "/x/v/math/list" ), "[\"1str\", \"2str\"]", APPLICATION_JSON ) .respondedJson( "[\"1str\",\"2str\"]" ); } @@ -201,7 +201,7 @@ public void shouldVerifyGZIPRequestProcessing() throws Exception { gzip.write( "{\"i\":1,\"s\":\"sss\"}".getBytes( StandardCharsets.UTF_8 ) ); gzip.close(); - var response = Client + Client.Response response = Client .custom() .build() .post( kernel.httpUrl( "/x/v/math/json" ), diff --git a/oap-ws/oap-ws/src/main/java/oap/ws/validate/ValidationErrors.java b/oap-ws/oap-ws/src/main/java/oap/ws/validate/ValidationErrors.java index 3331bf3b02..160dea97f5 100644 --- a/oap-ws/oap-ws/src/main/java/oap/ws/validate/ValidationErrors.java +++ b/oap-ws/oap-ws/src/main/java/oap/ws/validate/ValidationErrors.java @@ -125,10 +125,9 @@ public boolean hasDefaultCode() { return code == DEFAULT_CODE; } - @Deprecated public ValidationErrors throwIfInvalid() throws WsClientException { if( failed() ) - throw new WsClientException( errors.size() > 1 ? "validation failed" : errors.get( 0 ), code, errors ); + throw new WsClientException( errors.size() > 1 ? "validation failed" : errors.getFirst(), code, errors ); return this; } diff --git a/pom.xml b/pom.xml index 322fda5136..3423477e0b 100644 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,14 @@ pom import + + + com.squareup.okhttp3 + okhttp-bom + ${oap.deps.okhttp.version} + pom + import + @@ -57,7 +65,7 @@ - 25.2.1 + 25.3.1 25.0.1 25.0.0 @@ -66,7 +74,7 @@ 5.18.0 1.17.6 - 5.1.0 + 5.3.2 4.4.16 4.5.14 @@ -116,5 +124,7 @@ 4.9.8 + + 4.2.9.Final