From 315b660e1139ed99a2b7d28d2846f93d8eaedabe Mon Sep 17 00:00:00 2001 From: Ezequiel Valencia Date: Tue, 14 May 2024 07:41:10 -0400 Subject: [PATCH 01/10] Initial Forgot Password Endpoint Add the endpoint for forgetting your password. --- python-restclient/README.md | 1 + python-restclient/docs/UsersResourceApi.md | 65 +++++ python-restclient/pyproject.toml | 4 +- .../vcell_client/api/users_resource_api.py | 251 ++++++++++++++++++ tools/openapi.yaml | 19 ++ .../java/org/vcell/restq/db/UserRestDB.java | 4 + .../vcell/restq/handlers/UsersResource.java | 22 ++ vcell-restclient/README.md | 2 + vcell-restclient/api/openapi.yaml | 23 ++ vcell-restclient/docs/UsersResourceApi.md | 134 ++++++++++ .../restclient/api/UsersResourceApi.java | 85 ++++++ .../openapi/api/users-resource.service.ts | 61 +++++ .../api/users-resource.serviceInterface.ts | 7 + 13 files changed, 675 insertions(+), 3 deletions(-) diff --git a/python-restclient/README.md b/python-restclient/README.md index ec430585c0..4910da6680 100644 --- a/python-restclient/README.md +++ b/python-restclient/README.md @@ -99,6 +99,7 @@ Class | Method | HTTP request | Description *PublicationResourceApi* | [**get_publication_by_id**](docs/PublicationResourceApi.md#get_publication_by_id) | **GET** /api/v1/publications/{id} | Get publication by ID *PublicationResourceApi* | [**get_publications**](docs/PublicationResourceApi.md#get_publications) | **GET** /api/v1/publications | Get all publications *PublicationResourceApi* | [**update_publication**](docs/PublicationResourceApi.md#update_publication) | **PUT** /api/v1/publications | Create publication +*UsersResourceApi* | [**forgot_legacy_password**](docs/UsersResourceApi.md#forgot_legacy_password) | **POST** /api/v1/users/forgotLegacyPassword | The end user has forgotten the legacy password they used for VCell, so they will be emailed it. *UsersResourceApi* | [**get_legacy_api_token**](docs/UsersResourceApi.md#get_legacy_api_token) | **POST** /api/v1/users/bearerToken | Get token for legacy API *UsersResourceApi* | [**get_mapped_user**](docs/UsersResourceApi.md#get_mapped_user) | **GET** /api/v1/users/mappedUser | Get mapped VCell identity *UsersResourceApi* | [**get_me**](docs/UsersResourceApi.md#get_me) | **GET** /api/v1/users/me | Get current user diff --git a/python-restclient/docs/UsersResourceApi.md b/python-restclient/docs/UsersResourceApi.md index a3fa40a974..98bb2ad9d8 100644 --- a/python-restclient/docs/UsersResourceApi.md +++ b/python-restclient/docs/UsersResourceApi.md @@ -4,6 +4,7 @@ All URIs are relative to *https://vcellapi-test.cam.uchc.edu* Method | HTTP request | Description ------------- | ------------- | ------------- +[**forgot_legacy_password**](UsersResourceApi.md#forgot_legacy_password) | **POST** /api/v1/users/forgotLegacyPassword | The end user has forgotten the legacy password they used for VCell, so they will be emailed it. [**get_legacy_api_token**](UsersResourceApi.md#get_legacy_api_token) | **POST** /api/v1/users/bearerToken | Get token for legacy API [**get_mapped_user**](UsersResourceApi.md#get_mapped_user) | **GET** /api/v1/users/mappedUser | Get mapped VCell identity [**get_me**](UsersResourceApi.md#get_me) | **GET** /api/v1/users/me | Get current user @@ -12,6 +13,70 @@ Method | HTTP request | Description [**unmap_user**](UsersResourceApi.md#unmap_user) | **PUT** /api/v1/users/unmapUser/{userName} | remove vcell identity mapping +# **forgot_legacy_password** +> forgot_legacy_password(user_id=user_id) + +The end user has forgotten the legacy password they used for VCell, so they will be emailed it. + +### Example + +```python +import time +import os +import vcell_client +from vcell_client.rest import ApiException +from pprint import pprint + +# Defining the host is optional and defaults to https://vcellapi-test.cam.uchc.edu +# See configuration.py for a list of all supported configuration parameters. +configuration = vcell_client.Configuration( + host = "https://vcellapi-test.cam.uchc.edu" +) + + +# Enter a context with an instance of the API client +with vcell_client.ApiClient(configuration) as api_client: + # Create an instance of the API class + api_instance = vcell_client.UsersResourceApi(api_client) + user_id = 'user_id_example' # str | (optional) + + try: + # The end user has forgotten the legacy password they used for VCell, so they will be emailed it. + api_instance.forgot_legacy_password(user_id=user_id) + except Exception as e: + print("Exception when calling UsersResourceApi->forgot_legacy_password: %s\n" % e) +``` + + + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **user_id** | **str**| | [optional] + +### Return type + +void (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | Legacy password sent in email | - | +**401** | Need to login to Auth0 | - | +**500** | Internal Error | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + # **get_legacy_api_token** > AccesTokenRepresentationRecord get_legacy_api_token() diff --git a/python-restclient/pyproject.toml b/python-restclient/pyproject.toml index 6d29117bf4..ec8675c605 100644 --- a/python-restclient/pyproject.toml +++ b/python-restclient/pyproject.toml @@ -10,14 +10,12 @@ keywords = ["OpenAPI", "OpenAPI-Generator", "VCell API"] include = ["vcell_client/py.typed"] [tool.poetry.dependencies] -python = "^3.9" +python = "^3.7" urllib3 = ">= 1.25.3" python-dateutil = ">=2.8.2" pydantic = ">=2" typing-extensions = ">=4.7.1" -requests-oauth2client = "^1.5.1" -jupyter = "^1.0.0" [tool.poetry.dev-dependencies] pytest = ">=7.2.1" diff --git a/python-restclient/vcell_client/api/users_resource_api.py b/python-restclient/vcell_client/api/users_resource_api.py index 1b534654bf..bb69f16d42 100644 --- a/python-restclient/vcell_client/api/users_resource_api.py +++ b/python-restclient/vcell_client/api/users_resource_api.py @@ -52,6 +52,257 @@ def __init__(self, api_client=None) -> None: self.api_client = api_client + @validate_call + def forgot_legacy_password( + self, + user_id: Optional[StrictStr] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> None: + """The end user has forgotten the legacy password they used for VCell, so they will be emailed it. + + + :param user_id: + :type user_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._forgot_legacy_password_serialize( + user_id=user_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + def forgot_legacy_password_with_http_info( + self, + user_id: Optional[StrictStr] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[None]: + """The end user has forgotten the legacy password they used for VCell, so they will be emailed it. + + + :param user_id: + :type user_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._forgot_legacy_password_serialize( + user_id=user_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + def forgot_legacy_password_without_preload_content( + self, + user_id: Optional[StrictStr] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """The end user has forgotten the legacy password they used for VCell, so they will be emailed it. + + + :param user_id: + :type user_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._forgot_legacy_password_serialize( + user_id=user_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _forgot_legacy_password_serialize( + self, + user_id, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> Tuple: + + _host = None + + _collection_formats: Dict[str, str] = { + + } + + _path_params: Dict[str, str] = {} + _query_params: List[Tuple[str, str]] = [] + _header_params: Dict[str, Optional[str]] = _headers or {} + _form_params: List[Tuple[str, str]] = [] + _files: Dict[str, str] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + # process the query parameters + if user_id is not None: + + _query_params.append(('userID', user_id)) + + # process the header parameters + # process the form parameters + # process the body parameter + + + + + # authentication setting + _auth_settings: List[str] = [ + ] + + return self.api_client.param_serialize( + method='POST', + resource_path='/api/v1/users/forgotLegacyPassword', + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + + + @validate_call def get_legacy_api_token( self, diff --git a/tools/openapi.yaml b/tools/openapi.yaml index 74758808a4..ac83fafcba 100644 --- a/tools/openapi.yaml +++ b/tools/openapi.yaml @@ -219,6 +219,25 @@ paths: application/json: schema: $ref: '#/components/schemas/AccesTokenRepresentationRecord' + /api/v1/users/forgotLegacyPassword: + post: + tags: + - Users Resource + summary: "The end user has forgotten the legacy password they used for VCell,\ + \ so they will be emailed it." + operationId: forgotLegacyPassword + parameters: + - name: userID + in: query + schema: + type: string + responses: + "200": + description: Legacy password sent in email + "401": + description: Need to login to Auth0 + "500": + description: Internal Error /api/v1/users/mapUser: post: tags: diff --git a/vcell-rest/src/main/java/org/vcell/restq/db/UserRestDB.java b/vcell-rest/src/main/java/org/vcell/restq/db/UserRestDB.java index 85468d8fd7..f7179bb30d 100644 --- a/vcell-rest/src/main/java/org/vcell/restq/db/UserRestDB.java +++ b/vcell-rest/src/main/java/org/vcell/restq/db/UserRestDB.java @@ -185,6 +185,10 @@ public ApiAccessToken generateApiAccessToken(KeyValue apiClientKey, User user) t } } + public void sendOldLegacyPassword(String userID) throws SQLException, DataAccessException { + adminDBTopLevel.sendLostPassword(userID, true); + } + public ApiClient getAPIClient() throws DataAccessException { try { return adminDBTopLevel.getApiClient(DEFAULT_CLIENTID, true); diff --git a/vcell-rest/src/main/java/org/vcell/restq/handlers/UsersResource.java b/vcell-rest/src/main/java/org/vcell/restq/handlers/UsersResource.java index 033f06ce23..b6ac1f84b0 100644 --- a/vcell-rest/src/main/java/org/vcell/restq/handlers/UsersResource.java +++ b/vcell-rest/src/main/java/org/vcell/restq/handlers/UsersResource.java @@ -20,6 +20,7 @@ import org.vcell.util.UseridIDExistsException; import java.math.BigDecimal; +import java.sql.SQLException; import java.util.List; @Path("/api/v1/users") @@ -138,6 +139,27 @@ public AccesTokenRepresentationRecord generateBearerToken() throws DataAccessExc return AccesTokenRepresentationRecord.getRecordFromAccessTokenRepresentation(apiAccessToken); } + @POST + @Path("/forgotLegacyPassword") + @Operation(operationId = "forgotLegacyPassword", summary = "The end user has forgotten the legacy password they used for VCell, so they will be emailed it.") + @Consumes(MediaType.APPLICATION_JSON) + @APIResponses({ + @APIResponse(responseCode = "200", description = "Legacy password sent in email"), + @APIResponse(responseCode = "401", description = "Need to login to Auth0"), + @APIResponse(responseCode = "500", description = "Internal Error") + }) + public void forgotLegacyPassword(@QueryParam("userID") String userID) throws DataAccessException { + if(securityIdentity.isAnonymous()){ + throw new WebApplicationException("securityIdentity is missing jwt", Response.Status.UNAUTHORIZED); + } + try { + userRestDB.sendOldLegacyPassword(userID); + } catch (SQLException e) { + throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR); + } + } + + public record AccesTokenRepresentationRecord( String token, long creationDateSeconds, diff --git a/vcell-restclient/README.md b/vcell-restclient/README.md index c60c2c86d6..6566265daa 100644 --- a/vcell-restclient/README.md +++ b/vcell-restclient/README.md @@ -125,6 +125,8 @@ Class | Method | HTTP request | Description *PublicationResourceApi* | [**getPublicationsWithHttpInfo**](docs/PublicationResourceApi.md#getPublicationsWithHttpInfo) | **GET** /api/v1/publications | Get all publications *PublicationResourceApi* | [**updatePublication**](docs/PublicationResourceApi.md#updatePublication) | **PUT** /api/v1/publications | Create publication *PublicationResourceApi* | [**updatePublicationWithHttpInfo**](docs/PublicationResourceApi.md#updatePublicationWithHttpInfo) | **PUT** /api/v1/publications | Create publication +*UsersResourceApi* | [**forgotLegacyPassword**](docs/UsersResourceApi.md#forgotLegacyPassword) | **POST** /api/v1/users/forgotLegacyPassword | The end user has forgotten the legacy password they used for VCell, so they will be emailed it. +*UsersResourceApi* | [**forgotLegacyPasswordWithHttpInfo**](docs/UsersResourceApi.md#forgotLegacyPasswordWithHttpInfo) | **POST** /api/v1/users/forgotLegacyPassword | The end user has forgotten the legacy password they used for VCell, so they will be emailed it. *UsersResourceApi* | [**getLegacyApiToken**](docs/UsersResourceApi.md#getLegacyApiToken) | **POST** /api/v1/users/bearerToken | Get token for legacy API *UsersResourceApi* | [**getLegacyApiTokenWithHttpInfo**](docs/UsersResourceApi.md#getLegacyApiTokenWithHttpInfo) | **POST** /api/v1/users/bearerToken | Get token for legacy API *UsersResourceApi* | [**getMappedUser**](docs/UsersResourceApi.md#getMappedUser) | **GET** /api/v1/users/mappedUser | Get mapped VCell identity diff --git a/vcell-restclient/api/openapi.yaml b/vcell-restclient/api/openapi.yaml index 17d3fc3cc6..97aa10c295 100644 --- a/vcell-restclient/api/openapi.yaml +++ b/vcell-restclient/api/openapi.yaml @@ -240,6 +240,29 @@ paths: tags: - Users Resource x-accepts: application/json + /api/v1/users/forgotLegacyPassword: + post: + operationId: forgotLegacyPassword + parameters: + - explode: true + in: query + name: userID + required: false + schema: + type: string + style: form + responses: + "200": + description: Legacy password sent in email + "401": + description: Need to login to Auth0 + "500": + description: Internal Error + summary: "The end user has forgotten the legacy password they used for VCell,\ + \ so they will be emailed it." + tags: + - Users Resource + x-accepts: application/json /api/v1/users/mapUser: post: operationId: mapUser diff --git a/vcell-restclient/docs/UsersResourceApi.md b/vcell-restclient/docs/UsersResourceApi.md index 2a97e2fea3..9c45e1f2a6 100644 --- a/vcell-restclient/docs/UsersResourceApi.md +++ b/vcell-restclient/docs/UsersResourceApi.md @@ -4,6 +4,8 @@ All URIs are relative to *https://vcellapi-test.cam.uchc.edu* | Method | HTTP request | Description | |------------- | ------------- | -------------| +| [**forgotLegacyPassword**](UsersResourceApi.md#forgotLegacyPassword) | **POST** /api/v1/users/forgotLegacyPassword | The end user has forgotten the legacy password they used for VCell, so they will be emailed it. | +| [**forgotLegacyPasswordWithHttpInfo**](UsersResourceApi.md#forgotLegacyPasswordWithHttpInfo) | **POST** /api/v1/users/forgotLegacyPassword | The end user has forgotten the legacy password they used for VCell, so they will be emailed it. | | [**getLegacyApiToken**](UsersResourceApi.md#getLegacyApiToken) | **POST** /api/v1/users/bearerToken | Get token for legacy API | | [**getLegacyApiTokenWithHttpInfo**](UsersResourceApi.md#getLegacyApiTokenWithHttpInfo) | **POST** /api/v1/users/bearerToken | Get token for legacy API | | [**getMappedUser**](UsersResourceApi.md#getMappedUser) | **GET** /api/v1/users/mappedUser | Get mapped VCell identity | @@ -19,6 +21,138 @@ All URIs are relative to *https://vcellapi-test.cam.uchc.edu* +## forgotLegacyPassword + +> void forgotLegacyPassword(userID) + +The end user has forgotten the legacy password they used for VCell, so they will be emailed it. + +### Example + +```java +// Import classes: +import org.vcell.restclient.ApiClient; +import org.vcell.restclient.ApiException; +import org.vcell.restclient.Configuration; +import org.vcell.restclient.models.*; +import org.vcell.restclient.api.UsersResourceApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://vcellapi-test.cam.uchc.edu"); + + UsersResourceApi apiInstance = new UsersResourceApi(defaultClient); + String userID = "userID_example"; // String | + try { + apiInstance.forgotLegacyPassword(userID); + } catch (ApiException e) { + System.err.println("Exception when calling UsersResourceApi#forgotLegacyPassword"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + + +| Name | Type | Description | Notes | +|------------- | ------------- | ------------- | -------------| +| **userID** | **String**| | [optional] | + +### Return type + + +null (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | Legacy password sent in email | - | +| **401** | Need to login to Auth0 | - | +| **500** | Internal Error | - | + +## forgotLegacyPasswordWithHttpInfo + +> ApiResponse forgotLegacyPassword forgotLegacyPasswordWithHttpInfo(userID) + +The end user has forgotten the legacy password they used for VCell, so they will be emailed it. + +### Example + +```java +// Import classes: +import org.vcell.restclient.ApiClient; +import org.vcell.restclient.ApiException; +import org.vcell.restclient.ApiResponse; +import org.vcell.restclient.Configuration; +import org.vcell.restclient.models.*; +import org.vcell.restclient.api.UsersResourceApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://vcellapi-test.cam.uchc.edu"); + + UsersResourceApi apiInstance = new UsersResourceApi(defaultClient); + String userID = "userID_example"; // String | + try { + ApiResponse response = apiInstance.forgotLegacyPasswordWithHttpInfo(userID); + System.out.println("Status code: " + response.getStatusCode()); + System.out.println("Response headers: " + response.getHeaders()); + } catch (ApiException e) { + System.err.println("Exception when calling UsersResourceApi#forgotLegacyPassword"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Response headers: " + e.getResponseHeaders()); + System.err.println("Reason: " + e.getResponseBody()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + + +| Name | Type | Description | Notes | +|------------- | ------------- | ------------- | -------------| +| **userID** | **String**| | [optional] | + +### Return type + + +ApiResponse + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | Legacy password sent in email | - | +| **401** | Need to login to Auth0 | - | +| **500** | Internal Error | - | + + ## getLegacyApiToken > AccesTokenRepresentationRecord getLegacyApiToken() diff --git a/vcell-restclient/src/main/java/org/vcell/restclient/api/UsersResourceApi.java b/vcell-restclient/src/main/java/org/vcell/restclient/api/UsersResourceApi.java index 70c7ef87a9..cbd6951510 100644 --- a/vcell-restclient/src/main/java/org/vcell/restclient/api/UsersResourceApi.java +++ b/vcell-restclient/src/main/java/org/vcell/restclient/api/UsersResourceApi.java @@ -85,6 +85,91 @@ private String formatExceptionMessage(String operationId, int statusCode, String return operationId + " call failed with: " + statusCode + " - " + body; } + /** + * The end user has forgotten the legacy password they used for VCell, so they will be emailed it. + * + * @param userID (optional) + * @throws ApiException if fails to make API call + */ + public void forgotLegacyPassword(String userID) throws ApiException { + forgotLegacyPasswordWithHttpInfo(userID); + } + + /** + * The end user has forgotten the legacy password they used for VCell, so they will be emailed it. + * + * @param userID (optional) + * @return ApiResponse<Void> + * @throws ApiException if fails to make API call + */ + public ApiResponse forgotLegacyPasswordWithHttpInfo(String userID) throws ApiException { + HttpRequest.Builder localVarRequestBuilder = forgotLegacyPasswordRequestBuilder(userID); + try { + HttpResponse localVarResponse = memberVarHttpClient.send( + localVarRequestBuilder.build(), + HttpResponse.BodyHandlers.ofInputStream()); + if (memberVarResponseInterceptor != null) { + memberVarResponseInterceptor.accept(localVarResponse); + } + try { + if (localVarResponse.statusCode()/ 100 != 2) { + throw getApiException("forgotLegacyPassword", localVarResponse); + } + return new ApiResponse( + localVarResponse.statusCode(), + localVarResponse.headers().map(), + null + ); + } finally { + // Drain the InputStream + while (localVarResponse.body().read() != -1) { + // Ignore + } + localVarResponse.body().close(); + } + } catch (IOException e) { + throw new ApiException(e); + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new ApiException(e); + } + } + + private HttpRequest.Builder forgotLegacyPasswordRequestBuilder(String userID) throws ApiException { + + HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder(); + + String localVarPath = "/api/v1/users/forgotLegacyPassword"; + + List localVarQueryParams = new ArrayList<>(); + StringJoiner localVarQueryStringJoiner = new StringJoiner("&"); + String localVarQueryParameterBaseName; + localVarQueryParameterBaseName = "userID"; + localVarQueryParams.addAll(ApiClient.parameterToPairs("userID", userID)); + + if (!localVarQueryParams.isEmpty() || localVarQueryStringJoiner.length() != 0) { + StringJoiner queryJoiner = new StringJoiner("&"); + localVarQueryParams.forEach(p -> queryJoiner.add(p.getName() + '=' + p.getValue())); + if (localVarQueryStringJoiner.length() != 0) { + queryJoiner.add(localVarQueryStringJoiner.toString()); + } + localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath + '?' + queryJoiner.toString())); + } else { + localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath)); + } + + localVarRequestBuilder.header("Accept", "application/json"); + + localVarRequestBuilder.method("POST", HttpRequest.BodyPublishers.noBody()); + if (memberVarReadTimeout != null) { + localVarRequestBuilder.timeout(memberVarReadTimeout); + } + if (memberVarInterceptor != null) { + memberVarInterceptor.accept(localVarRequestBuilder); + } + return localVarRequestBuilder; + } /** * Get token for legacy API * diff --git a/webapp-ng/src/app/core/modules/openapi/api/users-resource.service.ts b/webapp-ng/src/app/core/modules/openapi/api/users-resource.service.ts index b9bfeb542d..ca1f341579 100644 --- a/webapp-ng/src/app/core/modules/openapi/api/users-resource.service.ts +++ b/webapp-ng/src/app/core/modules/openapi/api/users-resource.service.ts @@ -102,6 +102,67 @@ export class UsersResourceService implements UsersResourceServiceInterface { return httpParams; } + /** + * The end user has forgotten the legacy password they used for VCell, so they will be emailed it. + * @param userID + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public forgotLegacyPassword(userID?: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext}): Observable; + public forgotLegacyPassword(userID?: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext}): Observable>; + public forgotLegacyPassword(userID?: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext}): Observable>; + public forgotLegacyPassword(userID?: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: undefined, context?: HttpContext}): Observable { + + let localVarQueryParameters = new HttpParams({encoder: this.encoder}); + if (userID !== undefined && userID !== null) { + localVarQueryParameters = this.addToHttpParams(localVarQueryParameters, + userID, 'userID'); + } + + let localVarHeaders = this.defaultHeaders; + + let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (localVarHttpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + ]; + localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + let localVarHttpContext: HttpContext | undefined = options && options.context; + if (localVarHttpContext === undefined) { + localVarHttpContext = new HttpContext(); + } + + + let responseType_: 'text' | 'json' | 'blob' = 'json'; + if (localVarHttpHeaderAcceptSelected) { + if (localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) { + responseType_ = 'json'; + } else { + responseType_ = 'blob'; + } + } + + let localVarPath = `/api/v1/users/forgotLegacyPassword`; + return this.httpClient.request('post', `${this.configuration.basePath}${localVarPath}`, + { + context: localVarHttpContext, + params: localVarQueryParameters, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + reportProgress: reportProgress + } + ); + } + /** * Get token for legacy API * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. diff --git a/webapp-ng/src/app/core/modules/openapi/api/users-resource.serviceInterface.ts b/webapp-ng/src/app/core/modules/openapi/api/users-resource.serviceInterface.ts index e56d94c415..5fe009e5c2 100644 --- a/webapp-ng/src/app/core/modules/openapi/api/users-resource.serviceInterface.ts +++ b/webapp-ng/src/app/core/modules/openapi/api/users-resource.serviceInterface.ts @@ -28,6 +28,13 @@ export interface UsersResourceServiceInterface { defaultHeaders: HttpHeaders; configuration: Configuration; + /** + * The end user has forgotten the legacy password they used for VCell, so they will be emailed it. + * + * @param userID + */ + forgotLegacyPassword(userID?: string, extraHttpRequestParams?: any): Observable<{}>; + /** * Get token for legacy API * From 2de3d0ab1f283dad6ac4210bcd8b6da3287f3b49 Mon Sep 17 00:00:00 2001 From: Ezequiel Valencia Date: Wed, 15 May 2024 08:09:00 -0400 Subject: [PATCH 02/10] Allow HTTP Connection Allow a HTTP connection when the "isHTTP" property is set to true. --- .../bootstrap/client/RemoteProxyVCellConnectionFactory.java | 6 ++++-- .../src/main/java/cbit/vcell/resource/PropertyLoader.java | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/vcell-core/src/main/java/cbit/vcell/message/server/bootstrap/client/RemoteProxyVCellConnectionFactory.java b/vcell-core/src/main/java/cbit/vcell/message/server/bootstrap/client/RemoteProxyVCellConnectionFactory.java index 620e519acf..f4701c7497 100644 --- a/vcell-core/src/main/java/cbit/vcell/message/server/bootstrap/client/RemoteProxyVCellConnectionFactory.java +++ b/vcell-core/src/main/java/cbit/vcell/message/server/bootstrap/client/RemoteProxyVCellConnectionFactory.java @@ -176,8 +176,9 @@ public RemoteProxyVCellConnectionFactory( this.pathPrefix_v0 = pathPrefix_v0; boolean bIgnoreCertProblems = PropertyLoader.getBooleanProperty(PropertyLoader.sslIgnoreCertProblems,false); boolean bIgnoreHostMismatch = PropertyLoader.getBooleanProperty(PropertyLoader.sslIgnoreHostMismatch,false);; + boolean isHTTP = PropertyLoader.getBooleanProperty(PropertyLoader.isHTTP,false); try { - this.vcellApiClient = new VCellApiClient(this.apihost, this.apiport, this.pathPrefix_v0, bIgnoreCertProblems, bIgnoreHostMismatch); + this.vcellApiClient = new VCellApiClient(this.apihost, this.apiport, this.pathPrefix_v0, isHTTP, bIgnoreCertProblems, bIgnoreHostMismatch); } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { throw new RuntimeException("VCellApiClient configuration exception: "+e.getMessage(),e); } @@ -244,8 +245,9 @@ public String getAuth0MappedUser() { public static String getVCellSoftwareVersion(String apihost, Integer apiport, String pathPrefix_v0) { boolean bIgnoreCertProblems = PropertyLoader.getBooleanProperty(PropertyLoader.sslIgnoreCertProblems,false); boolean bIgnoreHostMismatch = PropertyLoader.getBooleanProperty(PropertyLoader.sslIgnoreHostMismatch,false);; + boolean isHTTP = PropertyLoader.getBooleanProperty(PropertyLoader.isHTTP,false); try { - VCellApiClient tempApiClient = new VCellApiClient(apihost, apiport, pathPrefix_v0, bIgnoreCertProblems, bIgnoreHostMismatch); + VCellApiClient tempApiClient = new VCellApiClient(apihost, apiport, pathPrefix_v0, isHTTP, bIgnoreCertProblems, bIgnoreHostMismatch); String serverSoftwareVersion = tempApiClient.getServerSoftwareVersion(); return serverSoftwareVersion; } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { diff --git a/vcell-core/src/main/java/cbit/vcell/resource/PropertyLoader.java b/vcell-core/src/main/java/cbit/vcell/resource/PropertyLoader.java index b46712bab5..743adcd3d3 100644 --- a/vcell-core/src/main/java/cbit/vcell/resource/PropertyLoader.java +++ b/vcell-core/src/main/java/cbit/vcell/resource/PropertyLoader.java @@ -124,6 +124,7 @@ public static void setConfigProvider(VCellConfigProvider configProvider) { public static final String vcellServerPrefixV0 = record("vcell.serverPrefix.v0",ValueType.GEN); public static final String sslIgnoreHostMismatch = record("vcell.ssl.ignoreHostMismatch",ValueType.BOOL); public static final String sslIgnoreCertProblems = record("vcell.ssl.ignoreCertProblems",ValueType.BOOL); + public static final String isHTTP = record("vcell.ssl.isHTTP",ValueType.BOOL); //Python properties public static final String pythonExe = record("vcell.python.executable",ValueType.EXE); From 037950e62e827551cbe33c76a97fd5af701cf572 Mon Sep 17 00:00:00 2001 From: Ezequiel Valencia Date: Thu, 16 May 2024 07:28:25 -0400 Subject: [PATCH 03/10] Granular Control of Quarkus URL Added the ability to control the URL utilized by the client created to interact with quarkus. Before it assumed that Quarkus and the legacy API where on the same host, but now that doesn't have to be the case. It still makes that assumption if other options aren't provided. --- .../org/vcell/api/client/VCellApiClient.java | 23 +++++++++++++++---- .../cbit/vcell/client/VCellClientMain.java | 15 +++++++++++- .../dependency/client/VCellClientModule.java | 13 ++++++++++- .../RemoteProxyVCellConnectionFactory.java | 17 ++++++++++++-- .../java/org/vcell/DependencyConstants.java | 3 +++ 5 files changed, 63 insertions(+), 8 deletions(-) diff --git a/vcell-apiclient/src/main/java/org/vcell/api/client/VCellApiClient.java b/vcell-apiclient/src/main/java/org/vcell/api/client/VCellApiClient.java index c49f056817..0d775757ce 100644 --- a/vcell-apiclient/src/main/java/org/vcell/api/client/VCellApiClient.java +++ b/vcell-apiclient/src/main/java/org/vcell/api/client/VCellApiClient.java @@ -37,8 +37,10 @@ import org.vcell.restclient.model.UserLoginInfoForMapping; import java.io.*; +import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; @@ -65,6 +67,7 @@ public class VCellApiClient implements AutoCloseable { boolean bIgnoreHostMismatch = true; boolean bSkipSSL = true; private final static String DEFAULT_CLIENTID = "85133f8d-26f7-4247-8356-d175399fc2e6"; + private final URL quarkusURL; private AuthApiClient apiClient = null; @@ -119,14 +122,26 @@ public VCellApiClient(String host, int port, String pathPrefix_v0, boolean bIgno this(host, port, pathPrefix_v0, false, bIgnoreCertProblems, bIgnoreHostMismatch); } - public VCellApiClient(String host, int port, String pathPrefix_v0, boolean bSkipSSL, boolean bIgnoreCertProblems, boolean bIgnoreHostMismatch) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException{ + public VCellApiClient(String host, int port, String pathPrefix_v0, boolean bSkipSSL, boolean bIgnoreCertProblems, boolean bIgnoreHostMismatch) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { + this(host, port, pathPrefix_v0, host, port, "/api/v1", + bSkipSSL, bIgnoreCertProblems, bIgnoreHostMismatch); + } + + public VCellApiClient(String host, int port, String pathPrefix_v0, + String quarkusHost, int quarkusPort, String quarkusPathPrefix, + boolean bSkipSSL, boolean bIgnoreCertProblems, boolean bIgnoreHostMismatch) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException{ this.httpHost = new HttpHost(host,port,(bSkipSSL?"http":"https")); this.pathPrefix_v0 = pathPrefix_v0; this.clientID = DEFAULT_CLIENTID; this.bIgnoreCertProblems = bIgnoreCertProblems; this.bIgnoreHostMismatch = bIgnoreHostMismatch; this.bSkipSSL = bSkipSSL; - initClient(); + try { + this.quarkusURL = new URL((bSkipSSL?"http":"https"), quarkusHost, quarkusPort, quarkusPathPrefix); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + initClient(); } private void initClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { @@ -490,8 +505,8 @@ public AccessTokenRepresentation authenticate(String userid, String password, bo public void authenticateWithAuth0() throws URISyntaxException, IOException, ParseException, ApiException { apiClient = InteractiveLogin.login("cjoWhd7W8A8znf7Z7vizyvKJCiqTgRtf", new URI("https://dev-dzhx7i2db3x3kkvq.us.auth0.com/authorize"), - new URI(this.httpHost.getSchemeName()+"://"+this.httpHost.getHostName()+":"+this.httpHost.getPort())); - apiClient.setScheme(this.httpHost.getSchemeName()); + this.quarkusURL.toURI()); + apiClient.setScheme(this.quarkusURL.getProtocol()); } public String getVCellUserNameFromAuth0Mapping() throws ApiException { diff --git a/vcell-client/src/main/java/cbit/vcell/client/VCellClientMain.java b/vcell-client/src/main/java/cbit/vcell/client/VCellClientMain.java index d3d0c55c61..612656910b 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/VCellClientMain.java +++ b/vcell-client/src/main/java/cbit/vcell/client/VCellClientMain.java @@ -68,6 +68,11 @@ public class VCellClientMain implements Callable { @Option(names = {"-console"}, type = Boolean.class, description = "Install4J parameter, ignored") private boolean _console = false; private VCellClient vcellClient; + @Option(names = {"--quarkus-api-host"}, hidden = true, description = "Quarkus API server host[:port]") + private String quarkusAPIHost = null; + @Option(names = {"--api-prefix-v1"}, description = "VCell api server path prefix for api version 1, " + + "defaults to /api/v1 if not specified") + private String pathPrefixV1 = "/api/v1"; private VCellClientMain() { @@ -115,8 +120,16 @@ public Integer call() throws Exception { apiport = Integer.parseInt(hostParts[1]); } + String[] quarkusHostParts = this.quarkusAPIHost.split(":"); + String quarkusApiHost = quarkusHostParts[0]; + int quarkusApiPort = 443; + if (quarkusHostParts.length == 2) { + quarkusApiPort = Integer.parseInt(quarkusHostParts[1]); + } + PropertyLoader.loadProperties(REQUIRED_CLIENT_PROPERTIES); - Injector injector = Guice.createInjector(new VCellClientModule(apihost, apiport, pathPrefixV0)); + Injector injector = Guice.createInjector(new VCellClientModule(apihost, apiport, pathPrefixV0, + quarkusApiHost, quarkusApiPort, pathPrefixV1)); this.vcellClient = injector.getInstance(VCellClient.class); // see static-files-config ConfigMap for definitions of dynamic properties as deployed diff --git a/vcell-client/src/main/java/org/vcell/dependency/client/VCellClientModule.java b/vcell-client/src/main/java/org/vcell/dependency/client/VCellClientModule.java index 84c7d2c0ad..44fbf56098 100644 --- a/vcell-client/src/main/java/org/vcell/dependency/client/VCellClientModule.java +++ b/vcell-client/src/main/java/org/vcell/dependency/client/VCellClientModule.java @@ -19,11 +19,18 @@ public class VCellClientModule extends AbstractModule { private final String apiHost; private final int apiPort; private final String apiPathPrefixV0; + private final String apiPathPrefixV1; + private final String quarkusAPIHost; + private final int quarkusAPIPort; - public VCellClientModule(String apiHost, int apiPort, String apiPathPrefixV0) { + public VCellClientModule(String apiHost, int apiPort, String apiPathPrefixV0, + String quarkusAPIHost, int quarkusAPIPort, String quarkusAPIPathPrefixV1) { this.apiHost = apiHost; this.apiPort = apiPort; this.apiPathPrefixV0 = apiPathPrefixV0; + this.quarkusAPIHost = quarkusAPIHost; + this.quarkusAPIPort = quarkusAPIPort; + this.apiPathPrefixV1 = quarkusAPIPathPrefixV1; } public interface UnimplementedService { @@ -50,5 +57,9 @@ protected void configure() { bind(String.class).annotatedWith(Names.named(DependencyConstants.VCELL_API_HOST)).toInstance(apiHost); bind(Integer.class).annotatedWith(Names.named(DependencyConstants.VCELL_API_PORT)).toInstance(apiPort); bind(String.class).annotatedWith(Names.named(DependencyConstants.VCELL_API_PATH_PREFIX_V0)).toInstance(apiPathPrefixV0); + + bind(String.class).annotatedWith(Names.named(DependencyConstants.VCELL_API_PATH_PREFIX_V1)).toInstance(apiPathPrefixV1); + bind(String.class).annotatedWith(Names.named(DependencyConstants.VCELL_QUARKUS_API_HOST)).toInstance(quarkusAPIHost); + bind(Integer.class).annotatedWith(Names.named(DependencyConstants.VCELL_QUARKUS_API_PORT)).toInstance(quarkusAPIPort); } } diff --git a/vcell-core/src/main/java/cbit/vcell/message/server/bootstrap/client/RemoteProxyVCellConnectionFactory.java b/vcell-core/src/main/java/cbit/vcell/message/server/bootstrap/client/RemoteProxyVCellConnectionFactory.java index f4701c7497..ee96f8cf33 100644 --- a/vcell-core/src/main/java/cbit/vcell/message/server/bootstrap/client/RemoteProxyVCellConnectionFactory.java +++ b/vcell-core/src/main/java/cbit/vcell/message/server/bootstrap/client/RemoteProxyVCellConnectionFactory.java @@ -166,11 +166,22 @@ public RemoteProxyException(String message, Exception e) { } + @Inject + public RemoteProxyVCellConnectionFactory( + @Named(DependencyConstants.VCELL_API_HOST) String apihost, + @Named(DependencyConstants.VCELL_API_PORT) Integer apiport, + @Named(DependencyConstants.VCELL_API_PATH_PREFIX_V0) String pathPrefix_v0) { + this(apihost, apiport, pathPrefix_v0, apihost, apiport, "/api/v1"); + } + @Inject public RemoteProxyVCellConnectionFactory( @Named(DependencyConstants.VCELL_API_HOST) String apihost, @Named(DependencyConstants.VCELL_API_PORT) Integer apiport, - @Named(DependencyConstants.VCELL_API_PATH_PREFIX_V0) String pathPrefix_v0) { + @Named(DependencyConstants.VCELL_API_PATH_PREFIX_V0) String pathPrefix_v0, + @Named(DependencyConstants.VCELL_QUARKUS_API_HOST) String quarkusApiHost, + @Named(DependencyConstants.VCELL_QUARKUS_API_PORT) Integer quarkusApiPort, + @Named(DependencyConstants.VCELL_API_PATH_PREFIX_V1) String pathPrefix_v1) { this.apihost = apihost; this.apiport = apiport; this.pathPrefix_v0 = pathPrefix_v0; @@ -178,7 +189,9 @@ public RemoteProxyVCellConnectionFactory( boolean bIgnoreHostMismatch = PropertyLoader.getBooleanProperty(PropertyLoader.sslIgnoreHostMismatch,false);; boolean isHTTP = PropertyLoader.getBooleanProperty(PropertyLoader.isHTTP,false); try { - this.vcellApiClient = new VCellApiClient(this.apihost, this.apiport, this.pathPrefix_v0, isHTTP, bIgnoreCertProblems, bIgnoreHostMismatch); + this.vcellApiClient = new VCellApiClient(this.apihost, this.apiport, this.pathPrefix_v0, + quarkusApiHost, quarkusApiPort, pathPrefix_v1, + isHTTP, bIgnoreCertProblems, bIgnoreHostMismatch); } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { throw new RuntimeException("VCellApiClient configuration exception: "+e.getMessage(),e); } diff --git a/vcell-core/src/main/java/org/vcell/DependencyConstants.java b/vcell-core/src/main/java/org/vcell/DependencyConstants.java index 3f3d716d61..48de785c0f 100644 --- a/vcell-core/src/main/java/org/vcell/DependencyConstants.java +++ b/vcell-core/src/main/java/org/vcell/DependencyConstants.java @@ -4,4 +4,7 @@ public class DependencyConstants { public static final String VCELL_API_PORT = "VCELL_API_PORT"; public static final String VCELL_API_HOST = "VCELL_API_HOST"; public static final String VCELL_API_PATH_PREFIX_V0 = "VCELL_API_PATH_PREFIX_V0"; + public static final String VCELL_API_PATH_PREFIX_V1 = "VCELL_API_PATH_PREFIX_V1"; + public static final String VCELL_QUARKUS_API_PORT = "VCELL_QUARKUS_API_PORT"; + public static final String VCELL_QUARKUS_API_HOST = "VCELL_QUARKUS_API_HOST"; } From 1c4c12eda9a79333b2422f4696635a830242ea6b Mon Sep 17 00:00:00 2001 From: Ezequiel Valencia Date: Thu, 16 May 2024 14:07:16 -0400 Subject: [PATCH 04/10] Revert API V1 Dynamic Path Changes Attempted to incorporate a dynamic path prefix to the quarkus API, but it was unnecessary since no matter if there's an ingress or not the path will always have the prefix /api/v1. --- .../org/vcell/api/client/VCellApiClient.java | 6 +++--- .../cbit/vcell/client/VCellClientMain.java | 20 +++++++++---------- .../dependency/client/VCellClientModule.java | 5 +---- .../RemoteProxyVCellConnectionFactory.java | 15 +++++++------- .../java/org/vcell/DependencyConstants.java | 1 - 5 files changed, 21 insertions(+), 26 deletions(-) diff --git a/vcell-apiclient/src/main/java/org/vcell/api/client/VCellApiClient.java b/vcell-apiclient/src/main/java/org/vcell/api/client/VCellApiClient.java index 0d775757ce..46c098de4c 100644 --- a/vcell-apiclient/src/main/java/org/vcell/api/client/VCellApiClient.java +++ b/vcell-apiclient/src/main/java/org/vcell/api/client/VCellApiClient.java @@ -123,12 +123,12 @@ public VCellApiClient(String host, int port, String pathPrefix_v0, boolean bIgno } public VCellApiClient(String host, int port, String pathPrefix_v0, boolean bSkipSSL, boolean bIgnoreCertProblems, boolean bIgnoreHostMismatch) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { - this(host, port, pathPrefix_v0, host, port, "/api/v1", + this(host, port, pathPrefix_v0, host, port, bSkipSSL, bIgnoreCertProblems, bIgnoreHostMismatch); } public VCellApiClient(String host, int port, String pathPrefix_v0, - String quarkusHost, int quarkusPort, String quarkusPathPrefix, + String quarkusHost, int quarkusPort, boolean bSkipSSL, boolean bIgnoreCertProblems, boolean bIgnoreHostMismatch) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException{ this.httpHost = new HttpHost(host,port,(bSkipSSL?"http":"https")); this.pathPrefix_v0 = pathPrefix_v0; @@ -137,7 +137,7 @@ public VCellApiClient(String host, int port, String pathPrefix_v0, this.bIgnoreHostMismatch = bIgnoreHostMismatch; this.bSkipSSL = bSkipSSL; try { - this.quarkusURL = new URL((bSkipSSL?"http":"https"), quarkusHost, quarkusPort, quarkusPathPrefix); + this.quarkusURL = new URL((bSkipSSL?"http":"https"), quarkusHost, quarkusPort, ""); } catch (MalformedURLException e) { throw new RuntimeException(e); } diff --git a/vcell-client/src/main/java/cbit/vcell/client/VCellClientMain.java b/vcell-client/src/main/java/cbit/vcell/client/VCellClientMain.java index 612656910b..8063fe713a 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/VCellClientMain.java +++ b/vcell-client/src/main/java/cbit/vcell/client/VCellClientMain.java @@ -70,10 +70,6 @@ public class VCellClientMain implements Callable { private VCellClient vcellClient; @Option(names = {"--quarkus-api-host"}, hidden = true, description = "Quarkus API server host[:port]") private String quarkusAPIHost = null; - @Option(names = {"--api-prefix-v1"}, description = "VCell api server path prefix for api version 1, " + - "defaults to /api/v1 if not specified") - private String pathPrefixV1 = "/api/v1"; - private VCellClientMain() { } @@ -120,16 +116,20 @@ public Integer call() throws Exception { apiport = Integer.parseInt(hostParts[1]); } - String[] quarkusHostParts = this.quarkusAPIHost.split(":"); - String quarkusApiHost = quarkusHostParts[0]; - int quarkusApiPort = 443; - if (quarkusHostParts.length == 2) { - quarkusApiPort = Integer.parseInt(quarkusHostParts[1]); + String quarkusApiHost = apihost; + int quarkusApiPort = apiport; + if (quarkusAPIHost != null) { + String[] quarkusHostParts = this.quarkusAPIHost.split(":"); + quarkusApiHost = quarkusHostParts[0]; + quarkusApiPort = 443; + if (quarkusHostParts.length == 2) { + quarkusApiPort = Integer.parseInt(quarkusHostParts[1]); + } } PropertyLoader.loadProperties(REQUIRED_CLIENT_PROPERTIES); Injector injector = Guice.createInjector(new VCellClientModule(apihost, apiport, pathPrefixV0, - quarkusApiHost, quarkusApiPort, pathPrefixV1)); + quarkusApiHost, quarkusApiPort)); this.vcellClient = injector.getInstance(VCellClient.class); // see static-files-config ConfigMap for definitions of dynamic properties as deployed diff --git a/vcell-client/src/main/java/org/vcell/dependency/client/VCellClientModule.java b/vcell-client/src/main/java/org/vcell/dependency/client/VCellClientModule.java index 44fbf56098..aff5375858 100644 --- a/vcell-client/src/main/java/org/vcell/dependency/client/VCellClientModule.java +++ b/vcell-client/src/main/java/org/vcell/dependency/client/VCellClientModule.java @@ -19,18 +19,16 @@ public class VCellClientModule extends AbstractModule { private final String apiHost; private final int apiPort; private final String apiPathPrefixV0; - private final String apiPathPrefixV1; private final String quarkusAPIHost; private final int quarkusAPIPort; public VCellClientModule(String apiHost, int apiPort, String apiPathPrefixV0, - String quarkusAPIHost, int quarkusAPIPort, String quarkusAPIPathPrefixV1) { + String quarkusAPIHost, int quarkusAPIPort) { this.apiHost = apiHost; this.apiPort = apiPort; this.apiPathPrefixV0 = apiPathPrefixV0; this.quarkusAPIHost = quarkusAPIHost; this.quarkusAPIPort = quarkusAPIPort; - this.apiPathPrefixV1 = quarkusAPIPathPrefixV1; } public interface UnimplementedService { @@ -58,7 +56,6 @@ protected void configure() { bind(Integer.class).annotatedWith(Names.named(DependencyConstants.VCELL_API_PORT)).toInstance(apiPort); bind(String.class).annotatedWith(Names.named(DependencyConstants.VCELL_API_PATH_PREFIX_V0)).toInstance(apiPathPrefixV0); - bind(String.class).annotatedWith(Names.named(DependencyConstants.VCELL_API_PATH_PREFIX_V1)).toInstance(apiPathPrefixV1); bind(String.class).annotatedWith(Names.named(DependencyConstants.VCELL_QUARKUS_API_HOST)).toInstance(quarkusAPIHost); bind(Integer.class).annotatedWith(Names.named(DependencyConstants.VCELL_QUARKUS_API_PORT)).toInstance(quarkusAPIPort); } diff --git a/vcell-core/src/main/java/cbit/vcell/message/server/bootstrap/client/RemoteProxyVCellConnectionFactory.java b/vcell-core/src/main/java/cbit/vcell/message/server/bootstrap/client/RemoteProxyVCellConnectionFactory.java index ee96f8cf33..369e78c866 100644 --- a/vcell-core/src/main/java/cbit/vcell/message/server/bootstrap/client/RemoteProxyVCellConnectionFactory.java +++ b/vcell-core/src/main/java/cbit/vcell/message/server/bootstrap/client/RemoteProxyVCellConnectionFactory.java @@ -166,12 +166,12 @@ public RemoteProxyException(String message, Exception e) { } - @Inject public RemoteProxyVCellConnectionFactory( - @Named(DependencyConstants.VCELL_API_HOST) String apihost, - @Named(DependencyConstants.VCELL_API_PORT) Integer apiport, - @Named(DependencyConstants.VCELL_API_PATH_PREFIX_V0) String pathPrefix_v0) { - this(apihost, apiport, pathPrefix_v0, apihost, apiport, "/api/v1"); + String apihost, + Integer apiport, + String pathPrefix_v0) { + this(apihost, apiport, pathPrefix_v0, + apihost, apiport); } @Inject @@ -180,8 +180,7 @@ public RemoteProxyVCellConnectionFactory( @Named(DependencyConstants.VCELL_API_PORT) Integer apiport, @Named(DependencyConstants.VCELL_API_PATH_PREFIX_V0) String pathPrefix_v0, @Named(DependencyConstants.VCELL_QUARKUS_API_HOST) String quarkusApiHost, - @Named(DependencyConstants.VCELL_QUARKUS_API_PORT) Integer quarkusApiPort, - @Named(DependencyConstants.VCELL_API_PATH_PREFIX_V1) String pathPrefix_v1) { + @Named(DependencyConstants.VCELL_QUARKUS_API_PORT) Integer quarkusApiPort) { this.apihost = apihost; this.apiport = apiport; this.pathPrefix_v0 = pathPrefix_v0; @@ -190,7 +189,7 @@ public RemoteProxyVCellConnectionFactory( boolean isHTTP = PropertyLoader.getBooleanProperty(PropertyLoader.isHTTP,false); try { this.vcellApiClient = new VCellApiClient(this.apihost, this.apiport, this.pathPrefix_v0, - quarkusApiHost, quarkusApiPort, pathPrefix_v1, + quarkusApiHost, quarkusApiPort, isHTTP, bIgnoreCertProblems, bIgnoreHostMismatch); } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { throw new RuntimeException("VCellApiClient configuration exception: "+e.getMessage(),e); diff --git a/vcell-core/src/main/java/org/vcell/DependencyConstants.java b/vcell-core/src/main/java/org/vcell/DependencyConstants.java index 48de785c0f..86ff3369ea 100644 --- a/vcell-core/src/main/java/org/vcell/DependencyConstants.java +++ b/vcell-core/src/main/java/org/vcell/DependencyConstants.java @@ -4,7 +4,6 @@ public class DependencyConstants { public static final String VCELL_API_PORT = "VCELL_API_PORT"; public static final String VCELL_API_HOST = "VCELL_API_HOST"; public static final String VCELL_API_PATH_PREFIX_V0 = "VCELL_API_PATH_PREFIX_V0"; - public static final String VCELL_API_PATH_PREFIX_V1 = "VCELL_API_PATH_PREFIX_V1"; public static final String VCELL_QUARKUS_API_PORT = "VCELL_QUARKUS_API_PORT"; public static final String VCELL_QUARKUS_API_HOST = "VCELL_QUARKUS_API_HOST"; } From 47e3f2eaad80c079bacaf0db3e8f3c446a081eea Mon Sep 17 00:00:00 2001 From: Ezequiel Valencia Date: Fri, 17 May 2024 13:48:06 -0400 Subject: [PATCH 05/10] Authenticated Swagger UI --- vcell-rest/src/main/resources/application.properties | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vcell-rest/src/main/resources/application.properties b/vcell-rest/src/main/resources/application.properties index f30a0b980b..1120e71e76 100644 --- a/vcell-rest/src/main/resources/application.properties +++ b/vcell-rest/src/main/resources/application.properties @@ -110,10 +110,11 @@ quarkus.swagger-ui.always-include=true %dev.quarkus.swagger-ui.oauth-client-secret= %test.quarkus.swagger-ui.oauth-client-id=backend-service %test.quarkus.swagger-ui.oauth-client-secret=secret -##quarkus.swagger-ui.oauth-redirect-uri=http://localhost:9000/q/swagger-ui/oauth2-redirect.html -%dev.quarkus.swagger-ui.oauth-authorization-uri=${quarkus.oidc.auth-server-url}/protocol/openid-connect/auth -%dev.quarkus.swagger-ui.oauth-token-uri=${quarkus.oidc.auth-server-url}/protocol/openid-connect/token -%dev.quarkus.swagger-ui.oauth-scopes=openid,profile,email +# %dev.quarkus.swagger-ui.oauth-redirect-uri=http://localhost:9000/q/swagger-ui/oauth2-redirect.html +%dev.quarkus.swagger-ui.oauth-authorization-uri=${quarkus.oidc.auth-server-url}/authorize +%dev.quarkus.swagger-ui.oauth-token-uri=${quarkus.oidc.auth-server-url}/oauth/token +%dev.quarkus.swagger-ui.oauth-additional-query-string-params={"audience": "http://localhost:9000"} +%dev.quarkus.swagger-ui.oauth-scopes=openid,profile,email,name %dev.quarkus.swagger-ui.oauth-use-pkce-with-authorization-code-grant=true %test.quarkus.swagger-ui.oauth-use-pkce-with-authorization-code-grant=false From 87531ac6b58336ab8a44bb2ef7f505ec779fb7fe Mon Sep 17 00:00:00 2001 From: Ezequiel Valencia Date: Tue, 21 May 2024 08:43:20 -0400 Subject: [PATCH 06/10] Changed JWT Audience Change the audience for the JWT token to vcellapi because although the domain may be incorrect, the protocol used for that domain is the same for local development, thus one less thing to take care of. --- vcell-rest/src/main/resources/application.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vcell-rest/src/main/resources/application.properties b/vcell-rest/src/main/resources/application.properties index 1120e71e76..9b0134a3d9 100644 --- a/vcell-rest/src/main/resources/application.properties +++ b/vcell-rest/src/main/resources/application.properties @@ -90,6 +90,7 @@ quarkus.smallrye-openapi.auto-add-security-requirement=true quarkus.smallrye-openapi.auto-add-security=true quarkus.smallrye-openapi.security-scheme-name=openId quarkus.smallrye-openapi.servers=https://vcellapi-test.cam.uchc.edu +%dev.quarkus.smallrye-openapi.servers=http://localhost:9000 #quarkus.smallrye-openapi.security-scheme=oidc @@ -113,7 +114,7 @@ quarkus.swagger-ui.always-include=true # %dev.quarkus.swagger-ui.oauth-redirect-uri=http://localhost:9000/q/swagger-ui/oauth2-redirect.html %dev.quarkus.swagger-ui.oauth-authorization-uri=${quarkus.oidc.auth-server-url}/authorize %dev.quarkus.swagger-ui.oauth-token-uri=${quarkus.oidc.auth-server-url}/oauth/token -%dev.quarkus.swagger-ui.oauth-additional-query-string-params={"audience": "http://localhost:9000"} +%dev.quarkus.swagger-ui.oauth-additional-query-string-params={"audience": "https://vcellapi.cam.uchc.edu"} %dev.quarkus.swagger-ui.oauth-scopes=openid,profile,email,name %dev.quarkus.swagger-ui.oauth-use-pkce-with-authorization-code-grant=true %test.quarkus.swagger-ui.oauth-use-pkce-with-authorization-code-grant=false From 3b2fce5aa698cf46613ebbaa427a59c859167707 Mon Sep 17 00:00:00 2001 From: Ezequiel Valencia Date: Tue, 21 May 2024 08:46:30 -0400 Subject: [PATCH 07/10] Dynamic Java Popup for Redirect Browser Popup a GUI telling the enduser about the redirection of the browser. Initial creation still needs refinement. --- .../java/cbit/vcell/client/VCellClient.java | 88 +++++++++---------- 1 file changed, 42 insertions(+), 46 deletions(-) diff --git a/vcell-client/src/main/java/cbit/vcell/client/VCellClient.java b/vcell-client/src/main/java/cbit/vcell/client/VCellClient.java index d940674401..f47465d290 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/VCellClient.java +++ b/vcell-client/src/main/java/cbit/vcell/client/VCellClient.java @@ -10,35 +10,6 @@ package cbit.vcell.client; -import java.awt.Window; -import java.util.Hashtable; -import java.util.Map; - -import javax.swing.JComponent; -import javax.swing.JPanel; -import javax.swing.RepaintManager; -import javax.swing.SwingUtilities; - -import cbit.vcell.server.VCellConnectionFactory; -import com.google.inject.Inject; -import org.apache.http.client.protocol.HttpClientContext; -import org.vcell.api.client.VCellApiClient; -import org.vcell.api.common.AccessTokenRepresentation; -import org.vcell.restclient.api.UsersResourceApi; -import org.vcell.restclient.auth.AuthApiClient; -import org.vcell.restclient.model.AccesTokenRepresentationRecord; -import org.vcell.restclient.model.MapUser; -import org.vcell.util.UserCancelException; -import org.vcell.util.document.UserLoginInfo; -import org.vcell.util.document.UserLoginInfo.DigestedPassword; -import org.vcell.util.document.VCDocument; -import org.vcell.util.document.VCDocument.VCDocumentType; -import org.vcell.util.gui.DialogUtils; - -import com.install4j.api.launcher.ApplicationLauncher; -import com.install4j.api.launcher.ApplicationLauncher.Callback; -import com.install4j.api.launcher.Variables; - import cbit.vcell.biomodel.BioModel; import cbit.vcell.client.desktop.DocumentWindowAboutBox; import cbit.vcell.client.server.ClientServerInfo; @@ -52,6 +23,20 @@ import cbit.vcell.geometry.Geometry; import cbit.vcell.mathmodel.MathModel; import cbit.vcell.resource.ErrorUtils; +import cbit.vcell.resource.ResourceUtil; +import cbit.vcell.server.VCellConnectionFactory; +import com.google.inject.Inject; +import com.install4j.api.launcher.ApplicationLauncher; +import org.vcell.util.UserCancelException; +import org.vcell.util.document.UserLoginInfo; +import org.vcell.util.document.UserLoginInfo.DigestedPassword; +import org.vcell.util.document.VCDocument; +import org.vcell.util.document.VCDocument.VCDocumentType; + +import javax.swing.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Hashtable; /** * Insert the type's description here. * Creation date: (5/5/2004 1:24:03 PM) @@ -265,27 +250,38 @@ public void run(Hashtable hashTable) throws Exception { AsynchClientTask task2b = new AsynchClientTask("Login With Auth0...", AsynchClientTask.TASKTYPE_NONSWING_BLOCKING) { @Override public void run(Hashtable hashTable) throws Exception { + Path appState = Path.of(ResourceUtil.getVcellHome().getAbsolutePath(), "/state.json"); + boolean showPopupMenu = true; + try{ + if (Files.exists(appState)) { + String json = Files.readString(appState); + showPopupMenu = false; + } + } + catch(Exception e){ + throw new Exception("Failed to read state file"); + } + if(showPopupMenu){ + int accept = JOptionPane.showConfirmDialog(null, + "VCell is going to redirect you to your browser to login. Do you wish to proceed?"); + if(accept==JOptionPane.NO_OPTION || accept==JOptionPane.CLOSED_OPTION || accept==JOptionPane.CANCEL_OPTION){ + return; + } + } vcellConnectionFactory.auth0SignIn(); DocumentWindowManager currWindowManager = (DocumentWindowManager)hashTable.get("currWindowManager"); - if (!vcellConnectionFactory.isVCellIdentityMappedToAuth0Identity()){ - // pop up with ability to input login info - loginAuth0(getRequestManager(), clientServerInfo, currWindowManager); - } - else{ - ClientServerInfo newClientServerInfo = createClientServerInfo(clientServerInfo, vcellConnectionFactory.getAuth0MappedUser(), null); - getRequestManager().connectToServer(currWindowManager, newClientServerInfo); + int numberOfPolls = 0; + while(!vcellConnectionFactory.isVCellIdentityMappedToAuth0Identity()){ + if (numberOfPolls==20) { + return; + } + numberOfPolls++; + Thread.sleep(5000); // Poll every 5 seconds } - } - }; - - AsynchClientTask task2c = new AsynchClientTask("Login...", AsynchClientTask.TASKTYPE_SWING_BLOCKING) { - @Override - public void run(Hashtable hashTable) throws Exception { - if (clientServerInfo.getUsername() == null) { - // we were not supplied login credentials; pop-up dialog - VCellClient.this.login(VCellClient.this.getRequestManager(), clientServerInfo, ((DocumentWindowManager)hashTable.get("currWindowManager"))); - } + ClientServerInfo newClientServerInfo = createClientServerInfo(clientServerInfo, vcellConnectionFactory.getAuth0MappedUser(), null); + getRequestManager().connectToServer(currWindowManager, newClientServerInfo); + Files.createFile(appState); } }; From b6a9f1e508216472e9faf169712f02c64137b16b Mon Sep 17 00:00:00 2001 From: Ezequiel Valencia Date: Tue, 21 May 2024 08:47:37 -0400 Subject: [PATCH 08/10] Forgot Password Only Available to Auth0 Users --- .../src/main/java/org/vcell/restq/handlers/UsersResource.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vcell-rest/src/main/java/org/vcell/restq/handlers/UsersResource.java b/vcell-rest/src/main/java/org/vcell/restq/handlers/UsersResource.java index b6ac1f84b0..68221d2faa 100644 --- a/vcell-rest/src/main/java/org/vcell/restq/handlers/UsersResource.java +++ b/vcell-rest/src/main/java/org/vcell/restq/handlers/UsersResource.java @@ -122,7 +122,7 @@ public UserIdentityJSONSafe getIdentity() throws DataAccessException { @POST @Path("/bearerToken") -// @RolesAllowed("user") + @RolesAllowed("user") @Operation(operationId = "getLegacyApiToken", summary = "Get token for legacy API") @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Produces(MediaType.APPLICATION_JSON) @@ -141,6 +141,7 @@ public AccesTokenRepresentationRecord generateBearerToken() throws DataAccessExc @POST @Path("/forgotLegacyPassword") + @RolesAllowed("user") @Operation(operationId = "forgotLegacyPassword", summary = "The end user has forgotten the legacy password they used for VCell, so they will be emailed it.") @Consumes(MediaType.APPLICATION_JSON) @APIResponses({ From 764bcb04e69d9e3ab95c156a97d02061e2cedfa7 Mon Sep 17 00:00:00 2001 From: Ezequiel Valencia Date: Tue, 21 May 2024 08:59:39 -0400 Subject: [PATCH 09/10] Make Lost Password Public The function already contained within AdminDB existed, but was not an available function to classes external to its module, so I've relaxed the restrictions and made it public. --- .../src/main/java/cbit/vcell/modeldb/AdminDBTopLevel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcell-server/src/main/java/cbit/vcell/modeldb/AdminDBTopLevel.java b/vcell-server/src/main/java/cbit/vcell/modeldb/AdminDBTopLevel.java index fb922d5966..85bbbefbfe 100644 --- a/vcell-server/src/main/java/cbit/vcell/modeldb/AdminDBTopLevel.java +++ b/vcell-server/src/main/java/cbit/vcell/modeldb/AdminDBTopLevel.java @@ -992,7 +992,7 @@ UserInfo getUserInfo(KeyValue key, boolean bEnableRetry) } } - void sendLostPassword(String userid, boolean bEnableRetry) throws DataAccessException, java.sql.SQLException, ObjectNotFoundException{ + public void sendLostPassword(String userid, boolean bEnableRetry) throws DataAccessException, java.sql.SQLException, ObjectNotFoundException{ Object lock = new Object(); Connection con = conFactory.getConnection(lock); try { From 45d63a132981cfc9e2569d4c258061a02ade5c62 Mon Sep 17 00:00:00 2001 From: Ezequiel Valencia Date: Tue, 21 May 2024 14:20:28 -0400 Subject: [PATCH 10/10] Revert Python Dependencies --- python-restclient/pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python-restclient/pyproject.toml b/python-restclient/pyproject.toml index ec8675c605..6d29117bf4 100644 --- a/python-restclient/pyproject.toml +++ b/python-restclient/pyproject.toml @@ -10,12 +10,14 @@ keywords = ["OpenAPI", "OpenAPI-Generator", "VCell API"] include = ["vcell_client/py.typed"] [tool.poetry.dependencies] -python = "^3.7" +python = "^3.9" urllib3 = ">= 1.25.3" python-dateutil = ">=2.8.2" pydantic = ">=2" typing-extensions = ">=4.7.1" +requests-oauth2client = "^1.5.1" +jupyter = "^1.0.0" [tool.poetry.dev-dependencies] pytest = ">=7.2.1"