From e7ca9b27151f332f2fb6cf159e77686e4bbf83c2 Mon Sep 17 00:00:00 2001 From: Chanaka Lakmal Date: Wed, 9 Jun 2021 18:24:52 +0530 Subject: [PATCH 1/7] Implement declarative auth design for upgrade service --- websocket-ballerina/annotation.bal | 2 + websocket-ballerina/auth_desugar.bal | 119 ++++++++++++++++++ websocket-ballerina/auth_types.bal | 68 ++++++++++ websocket-ballerina/websocket_errors.bal | 6 + .../net/websocket/WebSocketConstants.java | 2 + .../WebSocketResourceDispatcher.java | 8 +- .../net/websocket/WebSocketUtil.java | 15 +++ .../server/OnUpgradeResourceCallback.java | 19 +++ 8 files changed, 237 insertions(+), 2 deletions(-) create mode 100644 websocket-ballerina/auth_desugar.bal diff --git a/websocket-ballerina/annotation.bal b/websocket-ballerina/annotation.bal index 9a2630fc9..21b77b44b 100644 --- a/websocket-ballerina/annotation.bal +++ b/websocket-ballerina/annotation.bal @@ -26,10 +26,12 @@ # in the `websocket:Listener` which is applicable only for the initial HTTP upgrade request. # + maxFrameSize - The maximum payload size of a WebSocket frame in bytes. # If this is not set or is negative or zero, the default frame size which is 65536 will be used. +# + auth - Listener authenticaton configurations public type WSServiceConfig record {| string[] subProtocols = []; decimal idleTimeout = 0; int maxFrameSize = 65536; + ListenerAuthConfig[] auth?; |}; # The annotation which is used to configure a WebSocket service. diff --git a/websocket-ballerina/auth_desugar.bal b/websocket-ballerina/auth_desugar.bal new file mode 100644 index 000000000..d53cf9b56 --- /dev/null +++ b/websocket-ballerina/auth_desugar.bal @@ -0,0 +1,119 @@ +// Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 Inc. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/auth; +import ballerina/http; +import ballerina/jballerina.java; +import ballerina/jwt; +import ballerina/oauth2; + +// This function is used for declarative auth design, where the authentication/authorization decision is taken by +// reading the auth annotations provided in service/resource and the `Authorization` header taken with an interop call. +// This function is injected to the first lines of an websocket resource function. Then the logic will be executed +// during the runtime. +// If this function returns `()`, it will be moved to the execution of business logic, else there will be a 401/403 +// response sent. The execution flow will be broken by panic with a distinct error. +# Uses for declarative auth design, where the authentication/authorization decision is taken +# by reading the auth annotations provided in service/resource and the `Authorization` header of request. +# +# + serviceRef - The service reference where the resource locates +public isolated function authenticateResource(Service serviceRef) { + ListenerAuthConfig[]? authConfig = getServiceAuthConfig(serviceRef); + if (authConfig is ()) { + return; + } + string|http:HeaderNotFoundError header = getAuthorizationHeader(); + if (header is string) { + http:Unauthorized|http:Forbidden? result = tryAuthenticate(authConfig, header); + if (result is http:Unauthorized) { + notifyFailure(result.status.code); + } else if (result is http:Forbidden) { + notifyFailure(result.status.code); + } + } else { + notifyFailure(401); + } +} + +isolated function tryAuthenticate(ListenerAuthConfig[] authConfig, string header) returns http:Unauthorized|http:Forbidden? { + foreach ListenerAuthConfig config in authConfig { + if (config is FileUserStoreConfigWithScopes) { + http:ListenerFileUserStoreBasicAuthHandler handler = new(config.fileUserStoreConfig); + auth:UserDetails|http:Unauthorized authn = handler.authenticate(header); + string|string[]? scopes = config?.scopes; + if (authn is auth:UserDetails) { + if (scopes is string|string[]) { + http:Forbidden? authz = handler.authorize(authn, scopes); + return authz; + } + return; + } + } else if (config is LdapUserStoreConfigWithScopes) { + http:ListenerLdapUserStoreBasicAuthProvider handler = new(config.ldapUserStoreConfig); + auth:UserDetails|http:Unauthorized authn = handler->authenticate(header); + string|string[]? scopes = config?.scopes; + if (authn is auth:UserDetails) { + if (scopes is string|string[]) { + http:Forbidden? authz = handler->authorize(authn, scopes); + return authz; + } + return; + } + } else if (config is JwtValidatorConfigWithScopes) { + http:ListenerJwtAuthHandler handler = new(config.jwtValidatorConfig); + jwt:Payload|http:Unauthorized authn = handler.authenticate(header); + string|string[]? scopes = config?.scopes; + if (authn is jwt:Payload) { + if (scopes is string|string[]) { + http:Forbidden? authz = handler.authorize(authn, scopes); + return authz; + } + return; + } + } else { + // Here, config is OAuth2IntrospectionConfigWithScopes + http:ListenerOAuth2Handler handler = new(config.oauth2IntrospectionConfig); + oauth2:IntrospectionResponse|http:Unauthorized|http:Forbidden auth = handler->authorize(header, config?.scopes); + if (auth is oauth2:IntrospectionResponse) { + return; + } else if (auth is http:Forbidden) { + return auth; + } + } + } + http:Unauthorized unauthorized = {}; + return unauthorized; +} + +isolated function getServiceAuthConfig(Service serviceRef) returns ListenerAuthConfig[]? { + typedesc serviceTypeDesc = typeof serviceRef; + var serviceAnnotation = serviceTypeDesc.@ServiceConfig; + if (serviceAnnotation is ()) { + return; + } + WSServiceConfig serviceConfig = serviceAnnotation; + return serviceConfig?.auth; +} + +isolated function notifyFailure(int responseCode) { + // This panic is added to break the execution of the implementation inside the resource function after there is + // an authn/authz failure and responded with 401/403 internally. + panic error(responseCode.toString() + " received by auth desugar."); +} + +isolated function getAuthorizationHeader() returns string|http:HeaderNotFoundError = @java:Method { + 'class: "org.ballerinalang.net.websocket.WebSocketUtil" +} external; diff --git a/websocket-ballerina/auth_types.bal b/websocket-ballerina/auth_types.bal index cea851797..96c44995c 100644 --- a/websocket-ballerina/auth_types.bal +++ b/websocket-ballerina/auth_types.bal @@ -66,3 +66,71 @@ public type OAuth2RefreshTokenGrantConfig record {| # Represents OAuth2 grant configurations for OAuth2 authentication. public type OAuth2GrantConfig OAuth2ClientCredentialsGrantConfig|OAuth2PasswordGrantConfig|OAuth2RefreshTokenGrantConfig; + +# Represents file user store configurations for Basic Auth authentication. +public type FileUserStoreConfig record {| + *auth:FileUserStoreConfig; +|}; + +# Represents LDAP user store configurations for Basic Auth authentication. +public type LdapUserStoreConfig record {| + *auth:LdapUserStoreConfig; +|}; + +# Represents JWT validator configurations for JWT authentication. +# +# + scopeKey - The key used to fetch the scopes +public type JwtValidatorConfig record {| + *jwt:ValidatorConfig; + string scopeKey = "scope"; +|}; + +# Represents OAuth2 introspection server configurations for OAuth2 authentication. +# +# + scopeKey - The key used to fetch the scopes +public type OAuth2IntrospectionConfig record {| + *oauth2:IntrospectionConfig; + string scopeKey = "scope"; +|}; + +# Represents the auth annotation for file user store configurations with scopes. +# +# + fileUserStoreConfig - File user store configurations for Basic Auth authentication +# + scopes - Scopes allowed for authorization +public type FileUserStoreConfigWithScopes record {| + FileUserStoreConfig fileUserStoreConfig; + string|string[] scopes?; +|}; + +# Represents the auth annotation for LDAP user store configurations with scopes. +# +# + ldapUserStoreConfig - LDAP user store configurations for Basic Auth authentication +# + scopes - Scopes allowed for authorization +public type LdapUserStoreConfigWithScopes record {| + LdapUserStoreConfig ldapUserStoreConfig; + string|string[] scopes?; +|}; + +# Represents the auth annotation for JWT validator configurations with scopes. +# +# + jwtValidatorConfig - JWT validator configurations for JWT authentication +# + scopes - Scopes allowed for authorization +public type JwtValidatorConfigWithScopes record {| + JwtValidatorConfig jwtValidatorConfig; + string|string[] scopes?; +|}; + +# Represents the auth annotation for OAuth2 introspection server configurations with scopes. +# +# + oauth2IntrospectionConfig - OAuth2 introspection server configurations for OAuth2 authentication +# + scopes - Scopes allowed for authorization +public type OAuth2IntrospectionConfigWithScopes record {| + OAuth2IntrospectionConfig oauth2IntrospectionConfig; + string|string[] scopes?; +|}; + +# Defines the authentication configurations for the WebSocket listener. +public type ListenerAuthConfig FileUserStoreConfigWithScopes| + LdapUserStoreConfigWithScopes| + JwtValidatorConfigWithScopes| + OAuth2IntrospectionConfigWithScopes; diff --git a/websocket-ballerina/websocket_errors.bal b/websocket-ballerina/websocket_errors.bal index 2eeadee8b..cb8bdb221 100644 --- a/websocket-ballerina/websocket_errors.bal +++ b/websocket-ballerina/websocket_errors.bal @@ -47,5 +47,11 @@ public type ReadTimedOutError distinct Error; # Defines the Auth error types that returned from the client public type AuthError distinct Error; +# Defines the authentication error type that returned from the listener +public type AuthnError distinct Error; + +# Defines the authorization error type that returned from the listener +public type AuthzError distinct Error; + # Raised when the SSL handshake fails public type SslError distinct Error; diff --git a/websocket-native/src/main/java/org/ballerinalang/net/websocket/WebSocketConstants.java b/websocket-native/src/main/java/org/ballerinalang/net/websocket/WebSocketConstants.java index a1dbd1d3e..3788c8e53 100644 --- a/websocket-native/src/main/java/org/ballerinalang/net/websocket/WebSocketConstants.java +++ b/websocket-native/src/main/java/org/ballerinalang/net/websocket/WebSocketConstants.java @@ -147,6 +147,8 @@ public enum ErrorCode { HandshakeTimedOut("HandshakeTimedOut"), ReadTimedOutError("ReadTimedOutError"), SslError("SslError"), + AuthzError("AuthzError"), + AuthnError("AuthnError"), Error("Error"); private String errorCode; diff --git a/websocket-native/src/main/java/org/ballerinalang/net/websocket/WebSocketResourceDispatcher.java b/websocket-native/src/main/java/org/ballerinalang/net/websocket/WebSocketResourceDispatcher.java index 45a81b948..ef34d0e47 100644 --- a/websocket-native/src/main/java/org/ballerinalang/net/websocket/WebSocketResourceDispatcher.java +++ b/websocket-native/src/main/java/org/ballerinalang/net/websocket/WebSocketResourceDispatcher.java @@ -150,9 +150,13 @@ public static void dispatchUpgrade(WebSocketHandshaker webSocketHandshaker, WebS break; } } + Map properties = new HashMap<>(); + properties.put(HttpConstants.INBOUND_MESSAGE, httpCarbonMessage); wsService.getRuntime().invokeMethodAsync(wsService.getBalService(), resourceFunction.getName(), null, - ModuleUtils.getOnUpgradeMetaData(), - new OnUpgradeResourceCallback(webSocketHandshaker, wsService, connectionManager), bValues); + ModuleUtils.getOnUpgradeMetaData(), + new OnUpgradeResourceCallback(webSocketHandshaker, wsService, + connectionManager), + properties, PredefinedTypes.TYPE_ANY, bValues); } private static String sanitizeSubPath(String subPath) { diff --git a/websocket-native/src/main/java/org/ballerinalang/net/websocket/WebSocketUtil.java b/websocket-native/src/main/java/org/ballerinalang/net/websocket/WebSocketUtil.java index 5154dd8d3..0766eef23 100644 --- a/websocket-native/src/main/java/org/ballerinalang/net/websocket/WebSocketUtil.java +++ b/websocket-native/src/main/java/org/ballerinalang/net/websocket/WebSocketUtil.java @@ -18,6 +18,7 @@ package org.ballerinalang.net.websocket; +import io.ballerina.runtime.api.Environment; import io.ballerina.runtime.api.Future; import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.Runtime; @@ -33,12 +34,16 @@ import io.netty.channel.ChannelFuture; import io.netty.handler.codec.CodecException; import io.netty.handler.codec.TooLongFrameException; +import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.websocketx.CorruptedWebSocketFrameException; import io.netty.handler.codec.http.websocketx.WebSocketCloseStatus; import io.netty.handler.codec.http.websocketx.WebSocketHandshakeException; +import org.ballerinalang.net.http.HttpConstants; +import org.ballerinalang.net.http.HttpUtil; import org.ballerinalang.net.transport.contract.websocket.ClientHandshakeFuture; import org.ballerinalang.net.transport.contract.websocket.WebSocketClientConnector; import org.ballerinalang.net.transport.contract.websocket.WebSocketConnection; +import org.ballerinalang.net.transport.message.HttpCarbonMessage; import org.ballerinalang.net.websocket.client.listener.ClientHandshakeListener; import org.ballerinalang.net.websocket.client.listener.ExtendedConnectorListener; import org.ballerinalang.net.websocket.client.listener.ExtendedHandshakeListener; @@ -59,6 +64,7 @@ import javax.net.ssl.SSLException; +import static org.ballerinalang.net.http.HttpErrorType.HEADER_NOT_FOUND_ERROR; import static org.ballerinalang.net.websocket.WebSocketConstants.INITIALIZED_BY_SERVICE; import static org.ballerinalang.net.websocket.WebSocketConstants.NATIVE_DATA_MAX_FRAME_SIZE; import static org.ballerinalang.net.websocket.WebSocketConstants.SYNC_CLIENT; @@ -394,6 +400,15 @@ public static BError createWebsocketError(String message, WebSocketConstants.Err null, null); } + public static Object getAuthorizationHeader(Environment env) { + HttpCarbonMessage inboundMessage = (HttpCarbonMessage) env.getStrandLocal(HttpConstants.INBOUND_MESSAGE); + String authorizationHeader = inboundMessage.getHeader(HttpHeaderNames.AUTHORIZATION.toString()); + if (authorizationHeader == null) { + return HttpUtil.createHttpError("Http header does not exist", HEADER_NOT_FOUND_ERROR); + } + return StringUtils.fromString(authorizationHeader); + } + private WebSocketUtil() { } } diff --git a/websocket-native/src/main/java/org/ballerinalang/net/websocket/server/OnUpgradeResourceCallback.java b/websocket-native/src/main/java/org/ballerinalang/net/websocket/server/OnUpgradeResourceCallback.java index 4b6d38cfd..840ae280d 100644 --- a/websocket-native/src/main/java/org/ballerinalang/net/websocket/server/OnUpgradeResourceCallback.java +++ b/websocket-native/src/main/java/org/ballerinalang/net/websocket/server/OnUpgradeResourceCallback.java @@ -27,6 +27,7 @@ import io.netty.handler.codec.http.HttpHeaders; import org.ballerinalang.net.transport.contract.websocket.ServerHandshakeFuture; import org.ballerinalang.net.transport.contract.websocket.WebSocketHandshaker; +import org.ballerinalang.net.websocket.WebSocketConstants; import org.ballerinalang.net.websocket.WebSocketUtil; import static org.ballerinalang.net.websocket.WebSocketConstants.CUSTOM_HEADERS; @@ -49,6 +50,14 @@ public OnUpgradeResourceCallback(WebSocketHandshaker webSocketHandshaker, WebSoc @Override public void notifySuccess(Object result) { if (result instanceof BError) { + if (((BError) result).getType().getName().equals(WebSocketConstants.ErrorCode.AuthnError.errorCode())) { + webSocketHandshaker.cancelHandshake(401, ((BError) result).getErrorMessage().toString()); + return; + } + if (((BError) result).getType().getName().equals(WebSocketConstants.ErrorCode.AuthzError.errorCode())) { + webSocketHandshaker.cancelHandshake(403, ((BError) result).getErrorMessage().toString()); + return; + } webSocketHandshaker.cancelHandshake(400, ((BError) result).getErrorMessage().toString()); return; } @@ -67,6 +76,16 @@ public void notifySuccess(Object result) { @Override public void notifyFailure(BError error) { + // These checks are added to release the failure path since there is an authn/authz failure and responded + // with 401/403 internally. + if (error.getMessage().equals("401 received by auth desugar.")) { + webSocketHandshaker.cancelHandshake(401, error.getMessage()); + return; + } + if (error.getMessage().equals("403 received by auth desugar.")) { + webSocketHandshaker.cancelHandshake(403, error.getMessage()); + return; + } error.printStackTrace(); WebSocketConnectionInfo connectionInfo = connectionManager.getConnectionInfo(webSocketHandshaker.getChannelId()); From 24bc11b269ed40e0199404957356006b1ea20d57 Mon Sep 17 00:00:00 2001 From: Chanaka Lakmal Date: Wed, 9 Jun 2021 22:23:42 +0530 Subject: [PATCH 2/7] Update auth test cases --- websocket-ballerina/tests/Config.toml | 13 + .../tests/auth_test_commons.bal | 266 ++++++++++++++++++ .../tests/basic_auth_sync_client.bal | 40 +-- .../tests/bearer_token_sync_client.bal | 29 +- .../tests/jwt_auth_sync_client.bal | 93 +++--- .../tests/mock_http_oauth2_service.bal | 51 ---- .../tests/sync_client_oauth2_test.bal | 163 ++++++----- .../tests/websocket_ssl_proxy.bal | 2 - 8 files changed, 452 insertions(+), 205 deletions(-) create mode 100644 websocket-ballerina/tests/Config.toml create mode 100644 websocket-ballerina/tests/auth_test_commons.bal delete mode 100644 websocket-ballerina/tests/mock_http_oauth2_service.bal diff --git a/websocket-ballerina/tests/Config.toml b/websocket-ballerina/tests/Config.toml new file mode 100644 index 000000000..d8275b017 --- /dev/null +++ b/websocket-ballerina/tests/Config.toml @@ -0,0 +1,13 @@ +[[ballerina.auth.users]] +username="alice" +password="xxx" +scopes=["write", "update"] + +[[ballerina.auth.users]] +username="bob" +password="yyy" +scopes=["read"] + +[[ballerina.auth.users]] +username="eve" +password="123" diff --git a/websocket-ballerina/tests/auth_test_commons.bal b/websocket-ballerina/tests/auth_test_commons.bal new file mode 100644 index 000000000..fadbde3fb --- /dev/null +++ b/websocket-ballerina/tests/auth_test_commons.bal @@ -0,0 +1,266 @@ +// Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 Inc. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// NOTE: All the tokens/credentials used in this test are dummy tokens/credentials and used only for testing purposes. + +import ballerina/http; +import ballerina/regex; +//import ballerina/test; + +const string KEYSTORE_PATH = "tests/certsandkeys/ballerinaKeystore.p12"; +const string TRUSTSTORE_PATH = "tests/certsandkeys/ballerinaTruststore.p12"; + +//{ +// "alg": "RS256", +// "typ": "JWT", +// "kid": "NTAxZmMxNDMyZDg3MTU1ZGM0MzEzODJhZWI4NDNlZDU1OGFkNjFiMQ" +//} +//{ +// "sub": "admin", +// "iss": "wso2", +// "exp": 1925955724, +// "jti": "100078234ba23", +// "aud": [ +// "ballerina" +// ], +// "scp": "write" +//} +const string JWT1 = "eyJhbGciOiJSUzI1NiIsICJ0eXAiOiJKV1QiLCAia2lkIjoiTlRBeFptTXhORE15WkRnM01UVTFaR00wTXpFek9ESmhaV0k" + + "0TkRObFpEVTFPR0ZrTmpGaU1RIn0.eyJzdWIiOiJhZG1pbiIsICJpc3MiOiJ3c28yIiwgImV4cCI6MTkyNTk1NTcyNCwgIm" + + "p0aSI6IjEwMDA3ODIzNGJhMjMiLCAiYXVkIjpbImJhbGxlcmluYSJdLCAic2NwIjoid3JpdGUifQ.H99ufLvCLFA5i1gfCt" + + "klVdPrBvEl96aobNvtpEaCsO4v6_EgEZYz8Pg0B1Y7yJPbgpuAzXEg_CzowtfCTu3jUFf5FH_6M1fWGko5vpljtCb5Xknt_" + + "YPqvbk5fJbifKeXqbkCGfM9c0GS0uQO5ss8StquQcofxNgvImRV5eEGcDdybkKBNkbA-sJFHd1jEhb8rMdT0M0SZFLnhrPL" + + "8edbFZ-oa-ffLLls0vlEjUA7JiOSpnMbxRmT-ac6QjPxTQgNcndvIZVP2BHueQ1upyNorFKSMv8HZpATYHZjgnJQSpmt3Oa" + + "oFJ6pgzbFuniVNuqYghikCQIizqzQNfC7JUD8wA"; + +//{ +// "alg": "RS256", +// "typ": "JWT", +// "kid": "NTAxZmMxNDMyZDg3MTU1ZGM0MzEzODJhZWI4NDNlZDU1OGFkNjFiMQ" +//} +//{ +// "sub": "admin", +// "iss": "wso2", +// "exp": 1925955876, +// "jti": "100078234ba23", +// "aud": [ +// "ballerina" +// ], +// "scp": "read" +//} +const string JWT2 = "eyJhbGciOiJSUzI1NiIsICJ0eXAiOiJKV1QiLCAia2lkIjoiTlRBeFptTXhORE15WkRnM01UVTFaR00wTXpFek9ESmhaV0k" + + "0TkRObFpEVTFPR0ZrTmpGaU1RIn0.eyJzdWIiOiJhZG1pbiIsICJpc3MiOiJ3c28yIiwgImV4cCI6MTkyNTk1NTg3NiwgIm" + + "p0aSI6IjEwMDA3ODIzNGJhMjMiLCAiYXVkIjpbImJhbGxlcmluYSJdLCAic2NwIjoicmVhZCJ9.MVx_bJJpRyQryrTZ1-WC" + + "1BkJdeBulX2CnxYN5Y4r1XbVd0-rgbCQ86jEbWvLZOybQ8Hx7MB9thKaBvidBnctgMM1JzG-ULahl-afoyTCv_qxMCS-5B7" + + "AUA1f-sOQHzq-n7T3b0FKsWtmOEXbGmRxQFv89_v8xwUzIItXtZ6IjkoiZn5GerGrozX0DEBDAeG-2BOj8gSlsFENdPB5Sn" + + "5oEM6-Chrn6KFLXo3GFTwLQELgYkIGjgnMQfbyLLaw5oyJUyOCCsdMZ4oeVLO2rdKZs1L8ZDnolUfcdm5mTxxP9A4mTOTd-" + + "xC404MKwxkRhkgI4EJkcEwMHce2iCInZer10Q"; + +//{ +// "alg": "HS256", +// "typ": "JWT" +//} +//{ +// "sub": "1234567890", +// "name": "John Doe", +// "iat": 1516239022 +//} +const string JWT3 = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0Ij" + + "oxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; + +const string ACCESS_TOKEN_1 = "2YotnFZFEjr1zCsicMWpAA"; +const string ACCESS_TOKEN_2 = "1zCsicMWpAA2YotnFZFEjr"; +const string ACCESS_TOKEN_3 = "invalid-token"; +// +//isolated function createDummyRequest() returns http:Request { +// http:Request request = new; +// request.rawPath = "/helloWorld/sayHello"; +// request.method = "GET"; +// request.httpVersion = "1.1"; +// return request; +//} +// +//isolated function createSecureRequest(string headerValue) returns http:Request { +// http:Request request = createDummyRequest(); +// request.addHeader(http:AUTH_HEADER, headerValue); +// return request; +//} +// +//isolated function sendNoTokenRequest(string path) returns http:Response|http:ClientError { +// http:Client clientEP = checkpanic new("https://localhost:" + securedListenerPort.toString(), { +// secureSocket: { +// cert: { +// path: TRUSTSTORE_PATH, +// password: "ballerina" +// } +// } +// }); +// return <@untainted> clientEP->get(path); +//} +// +//isolated function sendBasicTokenRequest(string path, string username, string password) returns http:Response|http:ClientError { +// http:Client clientEP = checkpanic new("https://localhost:" + securedListenerPort.toString(), { +// auth: { +// username: username, +// password: password +// }, +// secureSocket: { +// cert: { +// path: TRUSTSTORE_PATH, +// password: "ballerina" +// } +// } +// }); +// return <@untainted> clientEP->get(path); +//} +// +//isolated function sendBearerTokenRequest(string path, string token) returns http:Response|http:ClientError { +// http:Client clientEP = checkpanic new("https://localhost:" + securedListenerPort.toString(), { +// auth: { +// token: token +// }, +// secureSocket: { +// cert: { +// path: TRUSTSTORE_PATH, +// password: "ballerina" +// } +// } +// }); +// return <@untainted> clientEP->get(path); +//} +// +//isolated function sendJwtRequest(string path) returns http:Response|http:ClientError { +// http:Client clientEP = checkpanic new("https://localhost:" + securedListenerPort.toString(), { +// auth: { +// username: "admin", +// issuer: "wso2", +// audience: ["ballerina"], +// jwtId: "100078234ba23", +// keyId: "NTAxZmMxNDMyZDg3MTU1ZGM0MzEzODJhZWI4NDNlZDU1OGFkNjFiMQ", +// customClaims: { "scp": "write" }, +// signatureConfig: { +// config: { +// keyStore: { +// path: KEYSTORE_PATH, +// password: "ballerina" +// }, +// keyAlias: "ballerina", +// keyPassword: "ballerina" +// } +// } +// }, +// secureSocket: { +// cert: { +// path: TRUSTSTORE_PATH, +// password: "ballerina" +// } +// } +// }); +// return <@untainted> clientEP->get(path); +//} +// +//isolated function sendOAuth2TokenRequest(string path) returns http:Response|http:ClientError { +// http:Client clientEP = checkpanic new("https://localhost:" + securedListenerPort.toString(), { +// auth: { +// tokenUrl: "https://localhost:" + stsPort.toString() + "/oauth2/token", +// clientId: "3MVG9YDQS5WtC11paU2WcQjBB3L5w4gz52uriT8ksZ3nUVjKvrfQMrU4uvZohTftxStwNEW4cfStBEGRxRL68", +// clientSecret: "9205371918321623741", +// clientConfig: { +// secureSocket: { +// cert: { +// path: TRUSTSTORE_PATH, +// password: "ballerina" +// } +// } +// } +// }, +// secureSocket: { +// cert: { +// path: TRUSTSTORE_PATH, +// password: "ballerina" +// } +// } +// }); +// return <@untainted> clientEP->get(path); +//} +// +//isolated function assertSuccess(http:Response|http:ClientError response) { +// if (response is http:Response) { +// test:assertEquals(response.statusCode, 200); +// } else { +// test:assertFail(msg = "Test Failed!"); +// } +//} +// +//isolated function assertForbidden(http:Response|http:ClientError response) { +// if (response is http:Response) { +// test:assertEquals(response.statusCode, 403); +// } else { +// test:assertFail(msg = "Test Failed!"); +// } +//} +// +//isolated function assertUnauthorized(http:Response|http:ClientError response) { +// if (response is http:Response) { +// test:assertEquals(response.statusCode, 401); +// } else { +// test:assertFail(msg = "Test Failed!"); +// } +//} + +// The mock authorization server, based with https://hub.docker.com/repository/docker/ldclakmal/ballerina-sts +listener http:Listener sts = new(9445, { + secureSocket: { + key: { + path: KEYSTORE_PATH, + password: "ballerina" + } + } +}); + +service /oauth2 on sts { + resource function post token() returns json { + json response = { + "access_token": ACCESS_TOKEN_1, + "token_type": "example", + "expires_in": 3600, + "example_parameter": "example_value" + }; + return response; + } + + resource function post introspect(http:Request request) returns json { + string|http:ClientError payload = request.getTextPayload(); + if (payload is string) { + string[] parts = regex:split(payload, "&"); + foreach string part in parts { + if (part.indexOf("token=") is int) { + string token = regex:split(part, "=")[1]; + if (token == ACCESS_TOKEN_1) { + json response = { "active": true, "exp": 3600, "scp": "write update" }; + return response; + } else if (token == ACCESS_TOKEN_2) { + json response = { "active": true, "exp": 3600, "scp": "read" }; + return response; + } else { + json response = { "active": false }; + return response; + } + } + } + } + } +} diff --git a/websocket-ballerina/tests/basic_auth_sync_client.bal b/websocket-ballerina/tests/basic_auth_sync_client.bal index ef1db6c46..954e98209 100644 --- a/websocket-ballerina/tests/basic_auth_sync_client.bal +++ b/websocket-ballerina/tests/basic_auth_sync_client.bal @@ -14,41 +14,43 @@ // specific language governing permissions and limitations // under the License. -import ballerina/http; import ballerina/lang.runtime as runtime; import ballerina/test; -string authHeader = ""; listener Listener l49 = new(21318); +string basicAuthSecuredServiceResponse = ""; -service /basicAuthSyncService on l49 { - resource function get .(http:Request req) returns Service|UpgradeError { - string|error header = req.getHeader("Authorization"); - if (header is string) { - authHeader = header; - return new WsService49(); - } else { - authHeader = "Header not found"; - return error UpgradeError("Authentication failed"); +@ServiceConfig { + auth: [ + { + fileUserStoreConfig: {}, + scopes: ["write", "update"] } + ] +} +service /basicAuthSyncService on l49 { + resource function get .() returns Service { + return new WsService49(); } } service class WsService49 { *Service; - remote function onTextMessage(Caller caller, string data) returns Error? { + remote function onTextMessage(string data) returns Error? { + basicAuthSecuredServiceResponse = data; } } @test:Config {} public function testSyncBasicAuth() returns Error? { - Client wsClient = check new("ws://localhost:21318/basicAuthSyncService/", config = { - auth: { - username: "alice2", - password: "1234" - } - }); + Client wsClient = check new("ws://localhost:21318/basicAuthSyncService/", { + auth: { + username: "alice", + password: "xxx" + } + }); + check wsClient->writeTextMessage("Hello, World!"); runtime:sleep(0.5); - test:assertEquals(authHeader, "Basic YWxpY2UyOjEyMzQ="); + test:assertEquals(basicAuthSecuredServiceResponse, "Hello, World!"); error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); } diff --git a/websocket-ballerina/tests/bearer_token_sync_client.bal b/websocket-ballerina/tests/bearer_token_sync_client.bal index d64a67f4b..e16e2f8e6 100644 --- a/websocket-ballerina/tests/bearer_token_sync_client.bal +++ b/websocket-ballerina/tests/bearer_token_sync_client.bal @@ -14,39 +14,34 @@ // specific language governing permissions and limitations // under the License. -import ballerina/http; import ballerina/lang.runtime as runtime; import ballerina/test; listener Listener l54 = new(21324); +string bearerTokenAuthSecuredServiceResponse = ""; service /bearerTokenSyncService on l54 { - resource function get .(http:Request req) returns Service|UpgradeError { - string|error header = req.getHeader("Authorization"); - if (header is string) { - authHeader = header; - return new WsService54(); - } else { - authHeader = "Header not found"; - return error UpgradeError("Authentication failed"); - } + resource function get .() returns Service { + return new WsService54(); } } service class WsService54 { *Service; - remote function onTextMessage(Caller caller, string data) returns Error? { + remote function onTextMessage(string data) returns Error? { + bearerTokenAuthSecuredServiceResponse = data; } } @test:Config {} public function testSyncBearerToken() returns Error? { - Client wsClient = check new("ws://localhost:21324/bearerTokenSyncService/", config = { - auth: { - token: "JlbmMiOiJBMTI4Q0JDLUhTMjU2Inikn" - } - }); + Client wsClient = check new("ws://localhost:21324/bearerTokenSyncService/", { + auth: { + token: "JlbmMiOiJBMTI4Q0JDLUhTMjU2Inikn" + } + }); + check wsClient->writeTextMessage("Hello, World!"); runtime:sleep(0.5); - test:assertEquals(authHeader, "Bearer JlbmMiOiJBMTI4Q0JDLUhTMjU2Inikn"); + test:assertEquals(bearerTokenAuthSecuredServiceResponse, "Hello, World!"); error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); } diff --git a/websocket-ballerina/tests/jwt_auth_sync_client.bal b/websocket-ballerina/tests/jwt_auth_sync_client.bal index e90b77812..01eea7e09 100644 --- a/websocket-ballerina/tests/jwt_auth_sync_client.bal +++ b/websocket-ballerina/tests/jwt_auth_sync_client.bal @@ -14,73 +14,70 @@ // specific language governing permissions and limitations // under the License. -import ballerina/http; -import ballerina/jwt; import ballerina/lang.runtime as runtime; import ballerina/test; listener Listener l51 = new(21320); -string strSyncData = ""; +string jwtAuthSecuredServiceResponse = ""; -http:JwtValidatorConfig jwtConfig = { - issuer: "ballerina", - audience: ["ballerina", "ballerina.org", "ballerina.io"], - signatureConfig: { - trustStoreConfig: { - trustStore: { - path: "tests/certsAndKeys/ballerinaKeystore.p12", - password: "ballerina" +@ServiceConfig { + auth: [ + { + jwtValidatorConfig: { + issuer: "ballerina", + audience: ["ballerina", "ballerina.org", "ballerina.io"], + signatureConfig: { + trustStoreConfig: { + trustStore: { + path: KEYSTORE_PATH, + password: "ballerina" + }, + certAlias: "ballerina" + } }, - certAlias: "ballerina" - } - }, - scopeKey: "scp" - }; - -http:ListenerJwtAuthHandler handler = new(jwtConfig); - -service /jwtSyncAuthService on l51 { - resource function get .(http:Request req) returns Service|UpgradeError { - jwt:Payload|http:Unauthorized authn1 = handler.authenticate(req); - if (authn1 is jwt:Payload) { - return new WsService51(); - } else { - return error UpgradeError("Authentication failed"); + scopeKey: "scp" + }, + scopes: ["write", "update"] } + ] +} +service /jwtSyncAuthService on l51 { + resource function get .() returns Service { + return new WsService51(); } } service class WsService51 { *Service; - remote function onTextMessage(Caller caller, string data) returns Error? { - strSyncData = data; + remote function onTextMessage(string data) returns Error? { + jwtAuthSecuredServiceResponse = data; } } @test:Config {} public function testSyncJwtAuth() returns Error? { - Client wsClient = check new("ws://localhost:21320/jwtSyncAuthService/", config = { - auth: { - username: "wso2", - issuer: "ballerina", - audience: ["ballerina", "ballerina.org", "ballerina.io"], - keyId: "5a0b754-895f-4279-8843-b745e11a57e9", - customClaims: { "scp": "hello" }, - expTime: 3600, - signatureConfig: { - config: { - keyAlias: "ballerina", - keyPassword: "ballerina", - keyStore: { - path: "tests/certsAndKeys/ballerinaKeystore.p12", - password: "ballerina" - } - } + Client wsClient = check new("ws://localhost:21320/jwtSyncAuthService/", { + auth: { + username: "wso2", + issuer: "ballerina", + audience: ["ballerina", "ballerina.org", "ballerina.io"], + keyId: "5a0b754-895f-4279-8843-b745e11a57e9", + customClaims: { "scp": "write" }, + expTime: 3600, + signatureConfig: { + config: { + keyAlias: "ballerina", + keyPassword: "ballerina", + keyStore: { + path: KEYSTORE_PATH, + password: "ballerina" } } - }); - check wsClient->writeTextMessage("Authentication successful"); + } + } + }); + check wsClient->writeTextMessage("Hello, World!"); runtime:sleep(0.5); - test:assertEquals(strSyncData, "Authentication successful"); + test:assertEquals(jwtAuthSecuredServiceResponse, "Hello, World!"); error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); } diff --git a/websocket-ballerina/tests/mock_http_oauth2_service.bal b/websocket-ballerina/tests/mock_http_oauth2_service.bal deleted file mode 100644 index 6d69b4305..000000000 --- a/websocket-ballerina/tests/mock_http_oauth2_service.bal +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -// -// WSO2 Inc. licenses this file to you under the Apache License, -// Version 2.0 (the "License"); you may not use this file except -// in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -import ballerina/http; - -const string ACCESS_TOKEN = "2YotnFZFEjr1zCsicMWpAA"; - -// Mock OAuth2 authorization server implementation, which treats the APIs with successful responses. -listener http:Listener oauth2Listener = new(9401, { - secureSocket: { - key: { - path: "tests/certsAndKeys/ballerinaKeystore.p12", - password: "ballerina" - } - } -}); - -service /oauth2 on oauth2Listener { - resource function post token() returns json { - json response = { - "access_token": ACCESS_TOKEN, - "token_type": "example", - "expires_in": 3600, - "example_parameter": "example_value" - }; - return response; - } - - resource function post token/refresh() returns json { - json response = { - "access_token": ACCESS_TOKEN, - "token_type": "example", - "expires_in": 3600, - "example_parameter": "example_value" - }; - return response; - } -} diff --git a/websocket-ballerina/tests/sync_client_oauth2_test.bal b/websocket-ballerina/tests/sync_client_oauth2_test.bal index 682801c35..1b874ad55 100644 --- a/websocket-ballerina/tests/sync_client_oauth2_test.bal +++ b/websocket-ballerina/tests/sync_client_oauth2_test.bal @@ -19,95 +19,122 @@ import ballerina/lang.runtime as runtime; import ballerina/test; listener Listener l55 = new(21325); -string oauthHeader = ""; +string oauth2SecuredServiceResponse = ""; -service /oauthService on l55 { - resource function get .(http:Request req) returns Service|UpgradeError { - string|error header = req.getHeader("Authorization"); - if (header is string) { - oauthHeader = header; - return new WsService55(); - } else { - oauthHeader = "Header not found"; - return error UpgradeError("Authentication failed"); +@ServiceConfig { + auth: [ + { + oauth2IntrospectionConfig: { + url: "https://localhost:9445/oauth2/introspect", + tokenTypeHint: "access_token", + scopeKey: "scp", + clientConfig: { + secureSocket: { + cert: { + path: TRUSTSTORE_PATH, + password: "ballerina" + } + } + } + }, + scopes: ["write", "update"] } + ] +} +service /oauthService on l55 { + resource function get .(http:Request req) returns Service { + return new WsService55(); } } service class WsService55 { *Service; - remote function onTextMessage(Caller caller, string data) returns Error? { + remote function onTextMessage(string data) returns Error? { + oauth2SecuredServiceResponse = data; } } -OAuth2ClientCredentialsGrantConfig config1 = { - tokenUrl: "https://localhost:9401/oauth2/token", - clientId: "3MVG9YDQS5WtC11paU2WcQjBB3L5w4gz52uriT8ksZ3nUVjKvrfQMrU4uvZohTftxStwNEW4cfStBEGRxRL68", - clientSecret: "9205371918321623741", - scopes: ["token-scope1", "token-scope2"], - clientConfig: { - secureSocket: { - cert: { - path: "tests/certsAndKeys/ballerinaTruststore.p12", - password: "ballerina" - } - } - } -}; - -OAuth2PasswordGrantConfig config2 = { - tokenUrl: "https://localhost:9401/oauth2/token", - username: "johndoe", - password: "A3ddj3w", - clientId: "3MVG9YDQS5WtC11paU2WcQjBB3L5w4gz52uriT8ksZ3nUVjKvrfQMrU4uvZohTftxStwNEW4cfStBEGRxRL68", - clientSecret: "9205371918321623741", - scopes: ["token-scope1", "token-scope2"], - clientConfig: { - secureSocket: { - cert: { - path: "tests/certsAndKeys/ballerinaTruststore.p12", - password: "ballerina" - } - } - } -}; - -OAuth2RefreshTokenGrantConfig config3 = { - refreshUrl: "https://localhost:9401/oauth2/token/refresh", - refreshToken: "XlfBs91yquexJqDaKEMzVg==", - clientId: "3MVG9YDQS5WtC11paU2WcQjBB3L5w4gz52uriT8ksZ3nUVjKvrfQMrU4uvZohTftxStwNEW4cfStBEGRxRL68", - clientSecret: "9205371918321623741", - scopes: ["token-scope1", "token-scope2"], - clientConfig: { - secureSocket: { - cert: { - path: "tests/certsAndKeys/ballerinaTruststore.p12", - password: "ballerina" - } - } - } -}; - -@test:Config {} +@test:Config { + before: clear +} public function testOAuth2ClientCredentialsGrant() returns Error? { - Client wsClient = check new("ws://localhost:21325/oauthService/", config = {auth: config1}); + Client wsClient = check new("ws://localhost:21325/oauthService/", { + auth: { + tokenUrl: "https://localhost:9445/oauth2/token", + clientId: "3MVG9YDQS5WtC11paU2WcQjBB3L5w4gz52uriT8ksZ3nUVjKvrfQMrU4uvZohTftxStwNEW4cfStBEGRxRL68", + clientSecret: "9205371918321623741", + scopes: ["write", "update"], + clientConfig: { + secureSocket: { + cert: { + path: TRUSTSTORE_PATH, + password: "ballerina" + } + } + } + } + }); + check wsClient->writeTextMessage("Hello, World!"); runtime:sleep(0.5); - test:assertEquals(oauthHeader, "Bearer 2YotnFZFEjr1zCsicMWpAA"); + test:assertEquals(oauth2SecuredServiceResponse, "Hello, World!"); error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); } -@test:Config {} +@test:Config { + before: clear +} public function testOAuth2PasswordGrant() returns Error? { - Client wsClient = check new("ws://localhost:21325/oauthService/", config = {auth: config2}); + Client wsClient = check new("ws://localhost:21325/oauthService/", { + auth: { + tokenUrl: "https://localhost:9445/oauth2/token", + username: "johndoe", + password: "A3ddj3w", + clientId: "3MVG9YDQS5WtC11paU2WcQjBB3L5w4gz52uriT8ksZ3nUVjKvrfQMrU4uvZohTftxStwNEW4cfStBEGRxRL68", + clientSecret: "9205371918321623741", + scopes: ["write", "update"], + clientConfig: { + secureSocket: { + cert: { + path: TRUSTSTORE_PATH, + password: "ballerina" + } + } + } + } + }); + check wsClient->writeTextMessage("Hello, World!"); runtime:sleep(0.5); - test:assertEquals(oauthHeader, "Bearer 2YotnFZFEjr1zCsicMWpAA"); + test:assertEquals(oauth2SecuredServiceResponse, "Hello, World!"); error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); } -@test:Config {} +@test:Config { + before: clear +} public function testOAuth2RefreshTokenGrant() returns Error? { - Client wsClient = check new("ws://localhost:21325/oauthService/", config = {auth: config3}); + Client wsClient = check new("ws://localhost:21325/oauthService/", { + auth: { + refreshUrl: "https://localhost:9445/oauth2/token", + refreshToken: "XlfBs91yquexJqDaKEMzVg==", + clientId: "3MVG9YDQS5WtC11paU2WcQjBB3L5w4gz52uriT8ksZ3nUVjKvrfQMrU4uvZohTftxStwNEW4cfStBEGRxRL68", + clientSecret: "9205371918321623741", + scopes: ["write", "update"], + clientConfig: { + secureSocket: { + cert: { + path: TRUSTSTORE_PATH, + password: "ballerina" + } + } + } + } + }); + check wsClient->writeTextMessage("Hello, World!"); runtime:sleep(0.5); - test:assertEquals(oauthHeader, "Bearer 2YotnFZFEjr1zCsicMWpAA"); + test:assertEquals(oauth2SecuredServiceResponse, "Hello, World!"); error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); } + +function clear() { + oauth2SecuredServiceResponse = ""; +} \ No newline at end of file diff --git a/websocket-ballerina/tests/websocket_ssl_proxy.bal b/websocket-ballerina/tests/websocket_ssl_proxy.bal index 42a6572f8..5da0771bd 100644 --- a/websocket-ballerina/tests/websocket_ssl_proxy.bal +++ b/websocket-ballerina/tests/websocket_ssl_proxy.bal @@ -18,8 +18,6 @@ import ballerina/io; import ballerina/lang.runtime as runtime; import ballerina/test; -final string TRUSTSTORE_PATH = "tests/certsAndKeys/ballerinaTruststore.p12"; -final string KEYSTORE_PATH = "tests/certsAndKeys/ballerinaKeystore.p12"; listener Listener l24 = new(21027, { secureSocket: { key: { From d01d33791c19aeb8e9f2926392f2ffd796851062 Mon Sep 17 00:00:00 2001 From: Chanaka Lakmal Date: Thu, 10 Jun 2021 10:16:47 +0530 Subject: [PATCH 3/7] Update certs directory path --- websocket-ballerina/tests/auth_test_commons.bal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/websocket-ballerina/tests/auth_test_commons.bal b/websocket-ballerina/tests/auth_test_commons.bal index fadbde3fb..c3aea89d0 100644 --- a/websocket-ballerina/tests/auth_test_commons.bal +++ b/websocket-ballerina/tests/auth_test_commons.bal @@ -20,8 +20,8 @@ import ballerina/http; import ballerina/regex; //import ballerina/test; -const string KEYSTORE_PATH = "tests/certsandkeys/ballerinaKeystore.p12"; -const string TRUSTSTORE_PATH = "tests/certsandkeys/ballerinaTruststore.p12"; +const string KEYSTORE_PATH = "tests/certsAndKeys/ballerinaKeystore.p12"; +const string TRUSTSTORE_PATH = "tests/certsAndKeys/ballerinaTruststore.p12"; //{ // "alg": "RS256", From dcd4aa0a43fef2d1f975770907b653735ff8d8b3 Mon Sep 17 00:00:00 2001 From: Chanaka Lakmal Date: Thu, 10 Jun 2021 10:25:17 +0530 Subject: [PATCH 4/7] Rename auth tests files --- ..._auth_sync_client.bal => auth_declarative_basic_auth_test.bal} | 0 ...ync_client.bal => auth_declarative_bearer_token_auth_test.bal} | 0 ...wt_auth_sync_client.bal => auth_declarative_jwt_auth_test.bal} | 0 ...nc_client_oauth2_test.bal => auth_declarative_oauth2_test.bal} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename websocket-ballerina/tests/{basic_auth_sync_client.bal => auth_declarative_basic_auth_test.bal} (100%) rename websocket-ballerina/tests/{bearer_token_sync_client.bal => auth_declarative_bearer_token_auth_test.bal} (100%) rename websocket-ballerina/tests/{jwt_auth_sync_client.bal => auth_declarative_jwt_auth_test.bal} (100%) rename websocket-ballerina/tests/{sync_client_oauth2_test.bal => auth_declarative_oauth2_test.bal} (100%) diff --git a/websocket-ballerina/tests/basic_auth_sync_client.bal b/websocket-ballerina/tests/auth_declarative_basic_auth_test.bal similarity index 100% rename from websocket-ballerina/tests/basic_auth_sync_client.bal rename to websocket-ballerina/tests/auth_declarative_basic_auth_test.bal diff --git a/websocket-ballerina/tests/bearer_token_sync_client.bal b/websocket-ballerina/tests/auth_declarative_bearer_token_auth_test.bal similarity index 100% rename from websocket-ballerina/tests/bearer_token_sync_client.bal rename to websocket-ballerina/tests/auth_declarative_bearer_token_auth_test.bal diff --git a/websocket-ballerina/tests/jwt_auth_sync_client.bal b/websocket-ballerina/tests/auth_declarative_jwt_auth_test.bal similarity index 100% rename from websocket-ballerina/tests/jwt_auth_sync_client.bal rename to websocket-ballerina/tests/auth_declarative_jwt_auth_test.bal diff --git a/websocket-ballerina/tests/sync_client_oauth2_test.bal b/websocket-ballerina/tests/auth_declarative_oauth2_test.bal similarity index 100% rename from websocket-ballerina/tests/sync_client_oauth2_test.bal rename to websocket-ballerina/tests/auth_declarative_oauth2_test.bal From 957a31163ff082a4cf969d0d2eeed40cf9534dc0 Mon Sep 17 00:00:00 2001 From: Chanaka Lakmal Date: Thu, 10 Jun 2021 11:39:16 +0530 Subject: [PATCH 5/7] Improve auth unit test cases --- .../auth_declarative_basic_auth_test.bal | 46 ++++- ...uth_declarative_bearer_token_auth_test.bal | 66 +++++- .../tests/auth_declarative_jwt_auth_test.bal | 74 ++++++- .../tests/auth_declarative_oauth2_test.bal | 25 ++- .../tests/auth_test_commons.bal | 195 ------------------ 5 files changed, 176 insertions(+), 230 deletions(-) diff --git a/websocket-ballerina/tests/auth_declarative_basic_auth_test.bal b/websocket-ballerina/tests/auth_declarative_basic_auth_test.bal index 954e98209..3f2abe495 100644 --- a/websocket-ballerina/tests/auth_declarative_basic_auth_test.bal +++ b/websocket-ballerina/tests/auth_declarative_basic_auth_test.bal @@ -18,7 +18,7 @@ import ballerina/lang.runtime as runtime; import ballerina/test; listener Listener l49 = new(21318); -string basicAuthSecuredServiceResponse = ""; +string wsService49Data = ""; @ServiceConfig { auth: [ @@ -28,7 +28,7 @@ string basicAuthSecuredServiceResponse = ""; } ] } -service /basicAuthSyncService on l49 { +service /basicAuth on l49 { resource function get .() returns Service { return new WsService49(); } @@ -36,14 +36,14 @@ service /basicAuthSyncService on l49 { service class WsService49 { *Service; - remote function onTextMessage(string data) returns Error? { - basicAuthSecuredServiceResponse = data; + remote function onTextMessage(string data) { + wsService49Data = data; } } @test:Config {} -public function testSyncBasicAuth() returns Error? { - Client wsClient = check new("ws://localhost:21318/basicAuthSyncService/", { +public function testBasicAuthServiceAuthSuccess() returns Error? { + Client wsClient = check new("ws://localhost:21318/basicAuth/", { auth: { username: "alice", password: "xxx" @@ -51,6 +51,38 @@ public function testSyncBasicAuth() returns Error? { }); check wsClient->writeTextMessage("Hello, World!"); runtime:sleep(0.5); - test:assertEquals(basicAuthSecuredServiceResponse, "Hello, World!"); + test:assertEquals(wsService49Data, "Hello, World!"); error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); } + +@test:Config {} +public function testBasicAuthServiceAuthzFailure() { + Client|Error wsClient = new("ws://localhost:21318/basicAuth/", { + auth: { + username: "bob", + password: "yyy" + } + }); + if (wsClient is Error) { + test:assertEquals(wsClient.message(), "InvalidHandshakeError: Invalid handshake response getStatus: 403 Forbidden"); + } else { + test:assertFail(msg = "Test Failed!"); + error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); + } +} + +@test:Config {} +public function testBasicAuthServiceAuthnFailure() { + Client|Error wsClient = new("ws://localhost:21318/basicAuth/", { + auth: { + username: "peter", + password: "123" + } + }); + if (wsClient is Error) { + test:assertEquals(wsClient.message(), "InvalidHandshakeError: Invalid handshake response getStatus: 401 Unauthorized"); + } else { + test:assertFail(msg = "Test Failed!"); + error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); + } +} diff --git a/websocket-ballerina/tests/auth_declarative_bearer_token_auth_test.bal b/websocket-ballerina/tests/auth_declarative_bearer_token_auth_test.bal index e16e2f8e6..89ce1f2a6 100644 --- a/websocket-ballerina/tests/auth_declarative_bearer_token_auth_test.bal +++ b/websocket-ballerina/tests/auth_declarative_bearer_token_auth_test.bal @@ -18,9 +18,29 @@ import ballerina/lang.runtime as runtime; import ballerina/test; listener Listener l54 = new(21324); -string bearerTokenAuthSecuredServiceResponse = ""; +string wsService54Data = ""; -service /bearerTokenSyncService on l54 { +@ServiceConfig { + auth: [ + { + oauth2IntrospectionConfig: { + url: "https://localhost:9445/oauth2/introspect", + tokenTypeHint: "access_token", + scopeKey: "scp", + clientConfig: { + secureSocket: { + cert: { + path: TRUSTSTORE_PATH, + password: "ballerina" + } + } + } + }, + scopes: ["write", "update"] + } + ] +} +service /bearerTokenAuth on l54 { resource function get .() returns Service { return new WsService54(); } @@ -28,20 +48,50 @@ service /bearerTokenSyncService on l54 { service class WsService54 { *Service; - remote function onTextMessage(string data) returns Error? { - bearerTokenAuthSecuredServiceResponse = data; + remote function onTextMessage(string data) { + wsService54Data = data; } } @test:Config {} -public function testSyncBearerToken() returns Error? { - Client wsClient = check new("ws://localhost:21324/bearerTokenSyncService/", { +public function testBearerTokenAuthServiceAuthSuccess() returns Error? { + Client wsClient = check new("ws://localhost:21324/bearerTokenAuth/", { auth: { - token: "JlbmMiOiJBMTI4Q0JDLUhTMjU2Inikn" + token: ACCESS_TOKEN_1 } }); check wsClient->writeTextMessage("Hello, World!"); runtime:sleep(0.5); - test:assertEquals(bearerTokenAuthSecuredServiceResponse, "Hello, World!"); + test:assertEquals(wsService54Data, "Hello, World!"); error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); } + +@test:Config {} +public function testBearerTokenAuthServiceAuthzFailure() { + Client|Error wsClient = new("ws://localhost:21324/bearerTokenAuth/", { + auth: { + token: ACCESS_TOKEN_2 + } + }); + if (wsClient is Error) { + test:assertEquals(wsClient.message(), "InvalidHandshakeError: Invalid handshake response getStatus: 403 Forbidden"); + } else { + test:assertFail(msg = "Test Failed!"); + error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); + } +} + +@test:Config {} +public function testBearerTokenAuthServiceAuthnFailure() { + Client|Error wsClient = new("ws://localhost:21324/bearerTokenAuth/", { + auth: { + token: ACCESS_TOKEN_3 + } + }); + if (wsClient is Error) { + test:assertEquals(wsClient.message(), "InvalidHandshakeError: Invalid handshake response getStatus: 401 Unauthorized"); + } else { + test:assertFail(msg = "Test Failed!"); + error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); + } +} diff --git a/websocket-ballerina/tests/auth_declarative_jwt_auth_test.bal b/websocket-ballerina/tests/auth_declarative_jwt_auth_test.bal index 01eea7e09..f2c37b6db 100644 --- a/websocket-ballerina/tests/auth_declarative_jwt_auth_test.bal +++ b/websocket-ballerina/tests/auth_declarative_jwt_auth_test.bal @@ -18,7 +18,7 @@ import ballerina/lang.runtime as runtime; import ballerina/test; listener Listener l51 = new(21320); -string jwtAuthSecuredServiceResponse = ""; +string wsService51Data = ""; @ServiceConfig { auth: [ @@ -41,7 +41,7 @@ string jwtAuthSecuredServiceResponse = ""; } ] } -service /jwtSyncAuthService on l51 { +service /jwtAuth on l51 { resource function get .() returns Service { return new WsService51(); } @@ -49,14 +49,14 @@ service /jwtSyncAuthService on l51 { service class WsService51 { *Service; - remote function onTextMessage(string data) returns Error? { - jwtAuthSecuredServiceResponse = data; + remote function onTextMessage(string data) { + wsService51Data = data; } } @test:Config {} -public function testSyncJwtAuth() returns Error? { - Client wsClient = check new("ws://localhost:21320/jwtSyncAuthService/", { +public function testJwtAuthServiceAuthSuccess() returns Error? { + Client wsClient = check new("ws://localhost:21320/jwtAuth/", { auth: { username: "wso2", issuer: "ballerina", @@ -78,6 +78,66 @@ public function testSyncJwtAuth() returns Error? { }); check wsClient->writeTextMessage("Hello, World!"); runtime:sleep(0.5); - test:assertEquals(jwtAuthSecuredServiceResponse, "Hello, World!"); + test:assertEquals(wsService51Data, "Hello, World!"); error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); } + +@test:Config {} +public function testJwtAuthServiceAuthzFailure() { + Client|Error wsClient = new("ws://localhost:21320/jwtAuth/", { + auth: { + username: "wso2", + issuer: "ballerina", + audience: ["ballerina", "ballerina.org", "ballerina.io"], + keyId: "5a0b754-895f-4279-8843-b745e11a57e9", + customClaims: { "scp": "read" }, + expTime: 3600, + signatureConfig: { + config: { + keyAlias: "ballerina", + keyPassword: "ballerina", + keyStore: { + path: KEYSTORE_PATH, + password: "ballerina" + } + } + } + } + }); + if (wsClient is Error) { + test:assertEquals(wsClient.message(), "InvalidHandshakeError: Invalid handshake response getStatus: 403 Forbidden"); + } else { + test:assertFail(msg = "Test Failed!"); + error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); + } +} + +@test:Config {} +public function testJwtAuthServiceAuthnFailure() { + Client|Error wsClient = new("ws://localhost:21320/jwtAuth/", { + auth: { + username: "wso2", + issuer: "wso2", + audience: ["ballerina", "ballerina.org", "ballerina.io"], + keyId: "5a0b754-895f-4279-8843-b745e11a57e9", + customClaims: { "scp": "write" }, + expTime: 3600, + signatureConfig: { + config: { + keyAlias: "ballerina", + keyPassword: "ballerina", + keyStore: { + path: KEYSTORE_PATH, + password: "ballerina" + } + } + } + } + }); + if (wsClient is Error) { + test:assertEquals(wsClient.message(), "InvalidHandshakeError: Invalid handshake response getStatus: 401 Unauthorized"); + } else { + test:assertFail(msg = "Test Failed!"); + error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); + } +} diff --git a/websocket-ballerina/tests/auth_declarative_oauth2_test.bal b/websocket-ballerina/tests/auth_declarative_oauth2_test.bal index 1b874ad55..fb97e9d0a 100644 --- a/websocket-ballerina/tests/auth_declarative_oauth2_test.bal +++ b/websocket-ballerina/tests/auth_declarative_oauth2_test.bal @@ -14,12 +14,11 @@ // specific language governing permissions and limitations // under the License. -import ballerina/http; import ballerina/lang.runtime as runtime; import ballerina/test; listener Listener l55 = new(21325); -string oauth2SecuredServiceResponse = ""; +string wsService55Data = ""; @ServiceConfig { auth: [ @@ -42,22 +41,22 @@ string oauth2SecuredServiceResponse = ""; ] } service /oauthService on l55 { - resource function get .(http:Request req) returns Service { + resource function get .() returns Service { return new WsService55(); } } service class WsService55 { *Service; - remote function onTextMessage(string data) returns Error? { - oauth2SecuredServiceResponse = data; + remote function onTextMessage(string data) { + wsService55Data = data; } } @test:Config { before: clear } -public function testOAuth2ClientCredentialsGrant() returns Error? { +public function testOAuth2ClientCredentialsGrantAuthSuccess() returns Error? { Client wsClient = check new("ws://localhost:21325/oauthService/", { auth: { tokenUrl: "https://localhost:9445/oauth2/token", @@ -76,14 +75,14 @@ public function testOAuth2ClientCredentialsGrant() returns Error? { }); check wsClient->writeTextMessage("Hello, World!"); runtime:sleep(0.5); - test:assertEquals(oauth2SecuredServiceResponse, "Hello, World!"); + test:assertEquals(wsService55Data, "Hello, World!"); error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); } @test:Config { before: clear } -public function testOAuth2PasswordGrant() returns Error? { +public function testOAuth2PasswordGrantAuthSuccess() returns Error? { Client wsClient = check new("ws://localhost:21325/oauthService/", { auth: { tokenUrl: "https://localhost:9445/oauth2/token", @@ -104,14 +103,14 @@ public function testOAuth2PasswordGrant() returns Error? { }); check wsClient->writeTextMessage("Hello, World!"); runtime:sleep(0.5); - test:assertEquals(oauth2SecuredServiceResponse, "Hello, World!"); + test:assertEquals(wsService55Data, "Hello, World!"); error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); } @test:Config { before: clear } -public function testOAuth2RefreshTokenGrant() returns Error? { +public function testOAuth2RefreshTokenGrantAuthSuccess() returns Error? { Client wsClient = check new("ws://localhost:21325/oauthService/", { auth: { refreshUrl: "https://localhost:9445/oauth2/token", @@ -131,10 +130,10 @@ public function testOAuth2RefreshTokenGrant() returns Error? { }); check wsClient->writeTextMessage("Hello, World!"); runtime:sleep(0.5); - test:assertEquals(oauth2SecuredServiceResponse, "Hello, World!"); + test:assertEquals(wsService55Data, "Hello, World!"); error? result = wsClient->close(statusCode = 1000, reason = "Close the connection", timeout = 0); } function clear() { - oauth2SecuredServiceResponse = ""; -} \ No newline at end of file + wsService55Data = ""; +} diff --git a/websocket-ballerina/tests/auth_test_commons.bal b/websocket-ballerina/tests/auth_test_commons.bal index c3aea89d0..41c52e2bb 100644 --- a/websocket-ballerina/tests/auth_test_commons.bal +++ b/websocket-ballerina/tests/auth_test_commons.bal @@ -18,208 +18,13 @@ import ballerina/http; import ballerina/regex; -//import ballerina/test; const string KEYSTORE_PATH = "tests/certsAndKeys/ballerinaKeystore.p12"; const string TRUSTSTORE_PATH = "tests/certsAndKeys/ballerinaTruststore.p12"; -//{ -// "alg": "RS256", -// "typ": "JWT", -// "kid": "NTAxZmMxNDMyZDg3MTU1ZGM0MzEzODJhZWI4NDNlZDU1OGFkNjFiMQ" -//} -//{ -// "sub": "admin", -// "iss": "wso2", -// "exp": 1925955724, -// "jti": "100078234ba23", -// "aud": [ -// "ballerina" -// ], -// "scp": "write" -//} -const string JWT1 = "eyJhbGciOiJSUzI1NiIsICJ0eXAiOiJKV1QiLCAia2lkIjoiTlRBeFptTXhORE15WkRnM01UVTFaR00wTXpFek9ESmhaV0k" + - "0TkRObFpEVTFPR0ZrTmpGaU1RIn0.eyJzdWIiOiJhZG1pbiIsICJpc3MiOiJ3c28yIiwgImV4cCI6MTkyNTk1NTcyNCwgIm" + - "p0aSI6IjEwMDA3ODIzNGJhMjMiLCAiYXVkIjpbImJhbGxlcmluYSJdLCAic2NwIjoid3JpdGUifQ.H99ufLvCLFA5i1gfCt" + - "klVdPrBvEl96aobNvtpEaCsO4v6_EgEZYz8Pg0B1Y7yJPbgpuAzXEg_CzowtfCTu3jUFf5FH_6M1fWGko5vpljtCb5Xknt_" + - "YPqvbk5fJbifKeXqbkCGfM9c0GS0uQO5ss8StquQcofxNgvImRV5eEGcDdybkKBNkbA-sJFHd1jEhb8rMdT0M0SZFLnhrPL" + - "8edbFZ-oa-ffLLls0vlEjUA7JiOSpnMbxRmT-ac6QjPxTQgNcndvIZVP2BHueQ1upyNorFKSMv8HZpATYHZjgnJQSpmt3Oa" + - "oFJ6pgzbFuniVNuqYghikCQIizqzQNfC7JUD8wA"; - -//{ -// "alg": "RS256", -// "typ": "JWT", -// "kid": "NTAxZmMxNDMyZDg3MTU1ZGM0MzEzODJhZWI4NDNlZDU1OGFkNjFiMQ" -//} -//{ -// "sub": "admin", -// "iss": "wso2", -// "exp": 1925955876, -// "jti": "100078234ba23", -// "aud": [ -// "ballerina" -// ], -// "scp": "read" -//} -const string JWT2 = "eyJhbGciOiJSUzI1NiIsICJ0eXAiOiJKV1QiLCAia2lkIjoiTlRBeFptTXhORE15WkRnM01UVTFaR00wTXpFek9ESmhaV0k" + - "0TkRObFpEVTFPR0ZrTmpGaU1RIn0.eyJzdWIiOiJhZG1pbiIsICJpc3MiOiJ3c28yIiwgImV4cCI6MTkyNTk1NTg3NiwgIm" + - "p0aSI6IjEwMDA3ODIzNGJhMjMiLCAiYXVkIjpbImJhbGxlcmluYSJdLCAic2NwIjoicmVhZCJ9.MVx_bJJpRyQryrTZ1-WC" + - "1BkJdeBulX2CnxYN5Y4r1XbVd0-rgbCQ86jEbWvLZOybQ8Hx7MB9thKaBvidBnctgMM1JzG-ULahl-afoyTCv_qxMCS-5B7" + - "AUA1f-sOQHzq-n7T3b0FKsWtmOEXbGmRxQFv89_v8xwUzIItXtZ6IjkoiZn5GerGrozX0DEBDAeG-2BOj8gSlsFENdPB5Sn" + - "5oEM6-Chrn6KFLXo3GFTwLQELgYkIGjgnMQfbyLLaw5oyJUyOCCsdMZ4oeVLO2rdKZs1L8ZDnolUfcdm5mTxxP9A4mTOTd-" + - "xC404MKwxkRhkgI4EJkcEwMHce2iCInZer10Q"; - -//{ -// "alg": "HS256", -// "typ": "JWT" -//} -//{ -// "sub": "1234567890", -// "name": "John Doe", -// "iat": 1516239022 -//} -const string JWT3 = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0Ij" + - "oxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; - const string ACCESS_TOKEN_1 = "2YotnFZFEjr1zCsicMWpAA"; const string ACCESS_TOKEN_2 = "1zCsicMWpAA2YotnFZFEjr"; const string ACCESS_TOKEN_3 = "invalid-token"; -// -//isolated function createDummyRequest() returns http:Request { -// http:Request request = new; -// request.rawPath = "/helloWorld/sayHello"; -// request.method = "GET"; -// request.httpVersion = "1.1"; -// return request; -//} -// -//isolated function createSecureRequest(string headerValue) returns http:Request { -// http:Request request = createDummyRequest(); -// request.addHeader(http:AUTH_HEADER, headerValue); -// return request; -//} -// -//isolated function sendNoTokenRequest(string path) returns http:Response|http:ClientError { -// http:Client clientEP = checkpanic new("https://localhost:" + securedListenerPort.toString(), { -// secureSocket: { -// cert: { -// path: TRUSTSTORE_PATH, -// password: "ballerina" -// } -// } -// }); -// return <@untainted> clientEP->get(path); -//} -// -//isolated function sendBasicTokenRequest(string path, string username, string password) returns http:Response|http:ClientError { -// http:Client clientEP = checkpanic new("https://localhost:" + securedListenerPort.toString(), { -// auth: { -// username: username, -// password: password -// }, -// secureSocket: { -// cert: { -// path: TRUSTSTORE_PATH, -// password: "ballerina" -// } -// } -// }); -// return <@untainted> clientEP->get(path); -//} -// -//isolated function sendBearerTokenRequest(string path, string token) returns http:Response|http:ClientError { -// http:Client clientEP = checkpanic new("https://localhost:" + securedListenerPort.toString(), { -// auth: { -// token: token -// }, -// secureSocket: { -// cert: { -// path: TRUSTSTORE_PATH, -// password: "ballerina" -// } -// } -// }); -// return <@untainted> clientEP->get(path); -//} -// -//isolated function sendJwtRequest(string path) returns http:Response|http:ClientError { -// http:Client clientEP = checkpanic new("https://localhost:" + securedListenerPort.toString(), { -// auth: { -// username: "admin", -// issuer: "wso2", -// audience: ["ballerina"], -// jwtId: "100078234ba23", -// keyId: "NTAxZmMxNDMyZDg3MTU1ZGM0MzEzODJhZWI4NDNlZDU1OGFkNjFiMQ", -// customClaims: { "scp": "write" }, -// signatureConfig: { -// config: { -// keyStore: { -// path: KEYSTORE_PATH, -// password: "ballerina" -// }, -// keyAlias: "ballerina", -// keyPassword: "ballerina" -// } -// } -// }, -// secureSocket: { -// cert: { -// path: TRUSTSTORE_PATH, -// password: "ballerina" -// } -// } -// }); -// return <@untainted> clientEP->get(path); -//} -// -//isolated function sendOAuth2TokenRequest(string path) returns http:Response|http:ClientError { -// http:Client clientEP = checkpanic new("https://localhost:" + securedListenerPort.toString(), { -// auth: { -// tokenUrl: "https://localhost:" + stsPort.toString() + "/oauth2/token", -// clientId: "3MVG9YDQS5WtC11paU2WcQjBB3L5w4gz52uriT8ksZ3nUVjKvrfQMrU4uvZohTftxStwNEW4cfStBEGRxRL68", -// clientSecret: "9205371918321623741", -// clientConfig: { -// secureSocket: { -// cert: { -// path: TRUSTSTORE_PATH, -// password: "ballerina" -// } -// } -// } -// }, -// secureSocket: { -// cert: { -// path: TRUSTSTORE_PATH, -// password: "ballerina" -// } -// } -// }); -// return <@untainted> clientEP->get(path); -//} -// -//isolated function assertSuccess(http:Response|http:ClientError response) { -// if (response is http:Response) { -// test:assertEquals(response.statusCode, 200); -// } else { -// test:assertFail(msg = "Test Failed!"); -// } -//} -// -//isolated function assertForbidden(http:Response|http:ClientError response) { -// if (response is http:Response) { -// test:assertEquals(response.statusCode, 403); -// } else { -// test:assertFail(msg = "Test Failed!"); -// } -//} -// -//isolated function assertUnauthorized(http:Response|http:ClientError response) { -// if (response is http:Response) { -// test:assertEquals(response.statusCode, 401); -// } else { -// test:assertFail(msg = "Test Failed!"); -// } -//} // The mock authorization server, based with https://hub.docker.com/repository/docker/ldclakmal/ballerina-sts listener http:Listener sts = new(9445, { From a684355de5fee3a20558e138a2ffef204936878a Mon Sep 17 00:00:00 2001 From: Chanaka Lakmal Date: Thu, 10 Jun 2021 12:21:22 +0530 Subject: [PATCH 6/7] Update changelog --- changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog.md b/changelog.md index 76fa67c8a..8f5a7b096 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,11 @@ This file contains all the notable changes done to the Ballerina WebSocket packa The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.2.0-beta.2] - 2021-06-24 + +### Fixed +- [Implement declarative auth design for upgrade service](https://github.com/ballerina-platform/ballerina-standard-library/issues/1405) + ## [1.2.0-beta.1] - 2021-05-06 ### Fixed From f8ecf1b6fd27dc9123963a2fd6064c885b7da8c8 Mon Sep 17 00:00:00 2001 From: Chanaka Lakmal Date: Fri, 11 Jun 2021 10:38:52 +0530 Subject: [PATCH 7/7] Apply suggestions from code review Co-authored-by: Bhashinee --- websocket-ballerina/auth_desugar.bal | 2 +- .../java/org/ballerinalang/net/websocket/WebSocketUtil.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/websocket-ballerina/auth_desugar.bal b/websocket-ballerina/auth_desugar.bal index d53cf9b56..bdf56d7a2 100644 --- a/websocket-ballerina/auth_desugar.bal +++ b/websocket-ballerina/auth_desugar.bal @@ -21,7 +21,7 @@ import ballerina/jwt; import ballerina/oauth2; // This function is used for declarative auth design, where the authentication/authorization decision is taken by -// reading the auth annotations provided in service/resource and the `Authorization` header taken with an interop call. +// reading the auth annotations provided in service and the `Authorization` header taken with an interop call. // This function is injected to the first lines of an websocket resource function. Then the logic will be executed // during the runtime. // If this function returns `()`, it will be moved to the execution of business logic, else there will be a 401/403 diff --git a/websocket-native/src/main/java/org/ballerinalang/net/websocket/WebSocketUtil.java b/websocket-native/src/main/java/org/ballerinalang/net/websocket/WebSocketUtil.java index 0766eef23..81d9f2db0 100644 --- a/websocket-native/src/main/java/org/ballerinalang/net/websocket/WebSocketUtil.java +++ b/websocket-native/src/main/java/org/ballerinalang/net/websocket/WebSocketUtil.java @@ -404,7 +404,7 @@ public static Object getAuthorizationHeader(Environment env) { HttpCarbonMessage inboundMessage = (HttpCarbonMessage) env.getStrandLocal(HttpConstants.INBOUND_MESSAGE); String authorizationHeader = inboundMessage.getHeader(HttpHeaderNames.AUTHORIZATION.toString()); if (authorizationHeader == null) { - return HttpUtil.createHttpError("Http header does not exist", HEADER_NOT_FOUND_ERROR); + return HttpUtil.createHttpError("HTTP header does not exist", HEADER_NOT_FOUND_ERROR); } return StringUtils.fromString(authorizationHeader); }