mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-01 02:17:43 +09:00 
			
		
		
		
	feat: lock auth session (#18322)
This commit is contained in:
		| @@ -1142,6 +1142,7 @@ | |||||||
|   "location_picker_latitude_hint": "Enter your latitude here", |   "location_picker_latitude_hint": "Enter your latitude here", | ||||||
|   "location_picker_longitude_error": "Enter a valid longitude", |   "location_picker_longitude_error": "Enter a valid longitude", | ||||||
|   "location_picker_longitude_hint": "Enter your longitude here", |   "location_picker_longitude_hint": "Enter your longitude here", | ||||||
|  |   "lock": "Lock", | ||||||
|   "locked_folder": "Locked Folder", |   "locked_folder": "Locked Folder", | ||||||
|   "log_out": "Log out", |   "log_out": "Log out", | ||||||
|   "log_out_all_devices": "Log Out All Devices", |   "log_out_all_devices": "Log Out All Devices", | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							| @@ -111,13 +111,14 @@ Class | Method | HTTP request | Description | |||||||
| *AuthenticationApi* | [**changePassword**](doc//AuthenticationApi.md#changepassword) | **POST** /auth/change-password |  | *AuthenticationApi* | [**changePassword**](doc//AuthenticationApi.md#changepassword) | **POST** /auth/change-password |  | ||||||
| *AuthenticationApi* | [**changePinCode**](doc//AuthenticationApi.md#changepincode) | **PUT** /auth/pin-code |  | *AuthenticationApi* | [**changePinCode**](doc//AuthenticationApi.md#changepincode) | **PUT** /auth/pin-code |  | ||||||
| *AuthenticationApi* | [**getAuthStatus**](doc//AuthenticationApi.md#getauthstatus) | **GET** /auth/status |  | *AuthenticationApi* | [**getAuthStatus**](doc//AuthenticationApi.md#getauthstatus) | **GET** /auth/status |  | ||||||
|  | *AuthenticationApi* | [**lockAuthSession**](doc//AuthenticationApi.md#lockauthsession) | **POST** /auth/session/lock |  | ||||||
| *AuthenticationApi* | [**login**](doc//AuthenticationApi.md#login) | **POST** /auth/login |  | *AuthenticationApi* | [**login**](doc//AuthenticationApi.md#login) | **POST** /auth/login |  | ||||||
| *AuthenticationApi* | [**logout**](doc//AuthenticationApi.md#logout) | **POST** /auth/logout |  | *AuthenticationApi* | [**logout**](doc//AuthenticationApi.md#logout) | **POST** /auth/logout |  | ||||||
| *AuthenticationApi* | [**resetPinCode**](doc//AuthenticationApi.md#resetpincode) | **DELETE** /auth/pin-code |  | *AuthenticationApi* | [**resetPinCode**](doc//AuthenticationApi.md#resetpincode) | **DELETE** /auth/pin-code |  | ||||||
| *AuthenticationApi* | [**setupPinCode**](doc//AuthenticationApi.md#setuppincode) | **POST** /auth/pin-code |  | *AuthenticationApi* | [**setupPinCode**](doc//AuthenticationApi.md#setuppincode) | **POST** /auth/pin-code |  | ||||||
| *AuthenticationApi* | [**signUpAdmin**](doc//AuthenticationApi.md#signupadmin) | **POST** /auth/admin-sign-up |  | *AuthenticationApi* | [**signUpAdmin**](doc//AuthenticationApi.md#signupadmin) | **POST** /auth/admin-sign-up |  | ||||||
|  | *AuthenticationApi* | [**unlockAuthSession**](doc//AuthenticationApi.md#unlockauthsession) | **POST** /auth/session/unlock |  | ||||||
| *AuthenticationApi* | [**validateAccessToken**](doc//AuthenticationApi.md#validateaccesstoken) | **POST** /auth/validateToken |  | *AuthenticationApi* | [**validateAccessToken**](doc//AuthenticationApi.md#validateaccesstoken) | **POST** /auth/validateToken |  | ||||||
| *AuthenticationApi* | [**verifyPinCode**](doc//AuthenticationApi.md#verifypincode) | **POST** /auth/pin-code/verify |  |  | ||||||
| *DeprecatedApi* | [**getRandom**](doc//DeprecatedApi.md#getrandom) | **GET** /assets/random |  | *DeprecatedApi* | [**getRandom**](doc//DeprecatedApi.md#getrandom) | **GET** /assets/random |  | ||||||
| *DownloadApi* | [**downloadArchive**](doc//DownloadApi.md#downloadarchive) | **POST** /download/archive |  | *DownloadApi* | [**downloadArchive**](doc//DownloadApi.md#downloadarchive) | **POST** /download/archive |  | ||||||
| *DownloadApi* | [**getDownloadInfo**](doc//DownloadApi.md#getdownloadinfo) | **POST** /download/info |  | *DownloadApi* | [**getDownloadInfo**](doc//DownloadApi.md#getdownloadinfo) | **POST** /download/info |  | ||||||
| @@ -198,6 +199,7 @@ Class | Method | HTTP request | Description | |||||||
| *SessionsApi* | [**deleteAllSessions**](doc//SessionsApi.md#deleteallsessions) | **DELETE** /sessions |  | *SessionsApi* | [**deleteAllSessions**](doc//SessionsApi.md#deleteallsessions) | **DELETE** /sessions |  | ||||||
| *SessionsApi* | [**deleteSession**](doc//SessionsApi.md#deletesession) | **DELETE** /sessions/{id} |  | *SessionsApi* | [**deleteSession**](doc//SessionsApi.md#deletesession) | **DELETE** /sessions/{id} |  | ||||||
| *SessionsApi* | [**getSessions**](doc//SessionsApi.md#getsessions) | **GET** /sessions |  | *SessionsApi* | [**getSessions**](doc//SessionsApi.md#getsessions) | **GET** /sessions |  | ||||||
|  | *SessionsApi* | [**lockSession**](doc//SessionsApi.md#locksession) | **POST** /sessions/{id}/lock |  | ||||||
| *SharedLinksApi* | [**addSharedLinkAssets**](doc//SharedLinksApi.md#addsharedlinkassets) | **PUT** /shared-links/{id}/assets |  | *SharedLinksApi* | [**addSharedLinkAssets**](doc//SharedLinksApi.md#addsharedlinkassets) | **PUT** /shared-links/{id}/assets |  | ||||||
| *SharedLinksApi* | [**createSharedLink**](doc//SharedLinksApi.md#createsharedlink) | **POST** /shared-links |  | *SharedLinksApi* | [**createSharedLink**](doc//SharedLinksApi.md#createsharedlink) | **POST** /shared-links |  | ||||||
| *SharedLinksApi* | [**getAllSharedLinks**](doc//SharedLinksApi.md#getallsharedlinks) | **GET** /shared-links |  | *SharedLinksApi* | [**getAllSharedLinks**](doc//SharedLinksApi.md#getallsharedlinks) | **GET** /shared-links |  | ||||||
| @@ -392,6 +394,7 @@ Class | Method | HTTP request | Description | |||||||
|  - [PersonUpdateDto](doc//PersonUpdateDto.md) |  - [PersonUpdateDto](doc//PersonUpdateDto.md) | ||||||
|  - [PersonWithFacesResponseDto](doc//PersonWithFacesResponseDto.md) |  - [PersonWithFacesResponseDto](doc//PersonWithFacesResponseDto.md) | ||||||
|  - [PinCodeChangeDto](doc//PinCodeChangeDto.md) |  - [PinCodeChangeDto](doc//PinCodeChangeDto.md) | ||||||
|  |  - [PinCodeResetDto](doc//PinCodeResetDto.md) | ||||||
|  - [PinCodeSetupDto](doc//PinCodeSetupDto.md) |  - [PinCodeSetupDto](doc//PinCodeSetupDto.md) | ||||||
|  - [PlacesResponseDto](doc//PlacesResponseDto.md) |  - [PlacesResponseDto](doc//PlacesResponseDto.md) | ||||||
|  - [PurchaseResponse](doc//PurchaseResponse.md) |  - [PurchaseResponse](doc//PurchaseResponse.md) | ||||||
| @@ -424,6 +427,7 @@ Class | Method | HTTP request | Description | |||||||
|  - [SessionCreateDto](doc//SessionCreateDto.md) |  - [SessionCreateDto](doc//SessionCreateDto.md) | ||||||
|  - [SessionCreateResponseDto](doc//SessionCreateResponseDto.md) |  - [SessionCreateResponseDto](doc//SessionCreateResponseDto.md) | ||||||
|  - [SessionResponseDto](doc//SessionResponseDto.md) |  - [SessionResponseDto](doc//SessionResponseDto.md) | ||||||
|  |  - [SessionUnlockDto](doc//SessionUnlockDto.md) | ||||||
|  - [SharedLinkCreateDto](doc//SharedLinkCreateDto.md) |  - [SharedLinkCreateDto](doc//SharedLinkCreateDto.md) | ||||||
|  - [SharedLinkEditDto](doc//SharedLinkEditDto.md) |  - [SharedLinkEditDto](doc//SharedLinkEditDto.md) | ||||||
|  - [SharedLinkResponseDto](doc//SharedLinkResponseDto.md) |  - [SharedLinkResponseDto](doc//SharedLinkResponseDto.md) | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								mobile/openapi/lib/api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/lib/api.dart
									
									
									
										generated
									
									
									
								
							| @@ -189,6 +189,7 @@ part 'model/person_statistics_response_dto.dart'; | |||||||
| part 'model/person_update_dto.dart'; | part 'model/person_update_dto.dart'; | ||||||
| part 'model/person_with_faces_response_dto.dart'; | part 'model/person_with_faces_response_dto.dart'; | ||||||
| part 'model/pin_code_change_dto.dart'; | part 'model/pin_code_change_dto.dart'; | ||||||
|  | part 'model/pin_code_reset_dto.dart'; | ||||||
| part 'model/pin_code_setup_dto.dart'; | part 'model/pin_code_setup_dto.dart'; | ||||||
| part 'model/places_response_dto.dart'; | part 'model/places_response_dto.dart'; | ||||||
| part 'model/purchase_response.dart'; | part 'model/purchase_response.dart'; | ||||||
| @@ -221,6 +222,7 @@ part 'model/server_version_response_dto.dart'; | |||||||
| part 'model/session_create_dto.dart'; | part 'model/session_create_dto.dart'; | ||||||
| part 'model/session_create_response_dto.dart'; | part 'model/session_create_response_dto.dart'; | ||||||
| part 'model/session_response_dto.dart'; | part 'model/session_response_dto.dart'; | ||||||
|  | part 'model/session_unlock_dto.dart'; | ||||||
| part 'model/shared_link_create_dto.dart'; | part 'model/shared_link_create_dto.dart'; | ||||||
| part 'model/shared_link_edit_dto.dart'; | part 'model/shared_link_edit_dto.dart'; | ||||||
| part 'model/shared_link_response_dto.dart'; | part 'model/shared_link_response_dto.dart'; | ||||||
|   | |||||||
							
								
								
									
										123
									
								
								mobile/openapi/lib/api/authentication_api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										123
									
								
								mobile/openapi/lib/api/authentication_api.dart
									
									
									
										generated
									
									
									
								
							| @@ -143,6 +143,39 @@ class AuthenticationApi { | |||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /// Performs an HTTP 'POST /auth/session/lock' operation and returns the [Response]. | ||||||
|  |   Future<Response> lockAuthSessionWithHttpInfo() async { | ||||||
|  |     // ignore: prefer_const_declarations | ||||||
|  |     final apiPath = r'/auth/session/lock'; | ||||||
|  | 
 | ||||||
|  |     // ignore: prefer_final_locals | ||||||
|  |     Object? postBody; | ||||||
|  | 
 | ||||||
|  |     final queryParams = <QueryParam>[]; | ||||||
|  |     final headerParams = <String, String>{}; | ||||||
|  |     final formParams = <String, String>{}; | ||||||
|  | 
 | ||||||
|  |     const contentTypes = <String>[]; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     return apiClient.invokeAPI( | ||||||
|  |       apiPath, | ||||||
|  |       'POST', | ||||||
|  |       queryParams, | ||||||
|  |       postBody, | ||||||
|  |       headerParams, | ||||||
|  |       formParams, | ||||||
|  |       contentTypes.isEmpty ? null : contentTypes.first, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> lockAuthSession() async { | ||||||
|  |     final response = await lockAuthSessionWithHttpInfo(); | ||||||
|  |     if (response.statusCode >= HttpStatus.badRequest) { | ||||||
|  |       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   /// Performs an HTTP 'POST /auth/login' operation and returns the [Response]. |   /// Performs an HTTP 'POST /auth/login' operation and returns the [Response]. | ||||||
|   /// Parameters: |   /// Parameters: | ||||||
|   /// |   /// | ||||||
| @@ -234,13 +267,13 @@ class AuthenticationApi { | |||||||
|   /// Performs an HTTP 'DELETE /auth/pin-code' operation and returns the [Response]. |   /// Performs an HTTP 'DELETE /auth/pin-code' operation and returns the [Response]. | ||||||
|   /// Parameters: |   /// Parameters: | ||||||
|   /// |   /// | ||||||
|   /// * [PinCodeChangeDto] pinCodeChangeDto (required): |   /// * [PinCodeResetDto] pinCodeResetDto (required): | ||||||
|   Future<Response> resetPinCodeWithHttpInfo(PinCodeChangeDto pinCodeChangeDto,) async { |   Future<Response> resetPinCodeWithHttpInfo(PinCodeResetDto pinCodeResetDto,) async { | ||||||
|     // ignore: prefer_const_declarations |     // ignore: prefer_const_declarations | ||||||
|     final apiPath = r'/auth/pin-code'; |     final apiPath = r'/auth/pin-code'; | ||||||
| 
 | 
 | ||||||
|     // ignore: prefer_final_locals |     // ignore: prefer_final_locals | ||||||
|     Object? postBody = pinCodeChangeDto; |     Object? postBody = pinCodeResetDto; | ||||||
| 
 | 
 | ||||||
|     final queryParams = <QueryParam>[]; |     final queryParams = <QueryParam>[]; | ||||||
|     final headerParams = <String, String>{}; |     final headerParams = <String, String>{}; | ||||||
| @@ -262,9 +295,9 @@ class AuthenticationApi { | |||||||
| 
 | 
 | ||||||
|   /// Parameters: |   /// Parameters: | ||||||
|   /// |   /// | ||||||
|   /// * [PinCodeChangeDto] pinCodeChangeDto (required): |   /// * [PinCodeResetDto] pinCodeResetDto (required): | ||||||
|   Future<void> resetPinCode(PinCodeChangeDto pinCodeChangeDto,) async { |   Future<void> resetPinCode(PinCodeResetDto pinCodeResetDto,) async { | ||||||
|     final response = await resetPinCodeWithHttpInfo(pinCodeChangeDto,); |     final response = await resetPinCodeWithHttpInfo(pinCodeResetDto,); | ||||||
|     if (response.statusCode >= HttpStatus.badRequest) { |     if (response.statusCode >= HttpStatus.badRequest) { | ||||||
|       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); |       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||||
|     } |     } | ||||||
| @@ -356,6 +389,45 @@ class AuthenticationApi { | |||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /// Performs an HTTP 'POST /auth/session/unlock' operation and returns the [Response]. | ||||||
|  |   /// Parameters: | ||||||
|  |   /// | ||||||
|  |   /// * [SessionUnlockDto] sessionUnlockDto (required): | ||||||
|  |   Future<Response> unlockAuthSessionWithHttpInfo(SessionUnlockDto sessionUnlockDto,) async { | ||||||
|  |     // ignore: prefer_const_declarations | ||||||
|  |     final apiPath = r'/auth/session/unlock'; | ||||||
|  | 
 | ||||||
|  |     // ignore: prefer_final_locals | ||||||
|  |     Object? postBody = sessionUnlockDto; | ||||||
|  | 
 | ||||||
|  |     final queryParams = <QueryParam>[]; | ||||||
|  |     final headerParams = <String, String>{}; | ||||||
|  |     final formParams = <String, String>{}; | ||||||
|  | 
 | ||||||
|  |     const contentTypes = <String>['application/json']; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     return apiClient.invokeAPI( | ||||||
|  |       apiPath, | ||||||
|  |       'POST', | ||||||
|  |       queryParams, | ||||||
|  |       postBody, | ||||||
|  |       headerParams, | ||||||
|  |       formParams, | ||||||
|  |       contentTypes.isEmpty ? null : contentTypes.first, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Parameters: | ||||||
|  |   /// | ||||||
|  |   /// * [SessionUnlockDto] sessionUnlockDto (required): | ||||||
|  |   Future<void> unlockAuthSession(SessionUnlockDto sessionUnlockDto,) async { | ||||||
|  |     final response = await unlockAuthSessionWithHttpInfo(sessionUnlockDto,); | ||||||
|  |     if (response.statusCode >= HttpStatus.badRequest) { | ||||||
|  |       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   /// Performs an HTTP 'POST /auth/validateToken' operation and returns the [Response]. |   /// Performs an HTTP 'POST /auth/validateToken' operation and returns the [Response]. | ||||||
|   Future<Response> validateAccessTokenWithHttpInfo() async { |   Future<Response> validateAccessTokenWithHttpInfo() async { | ||||||
|     // ignore: prefer_const_declarations |     // ignore: prefer_const_declarations | ||||||
| @@ -396,43 +468,4 @@ class AuthenticationApi { | |||||||
|     } |     } | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| 
 |  | ||||||
|   /// Performs an HTTP 'POST /auth/pin-code/verify' operation and returns the [Response]. |  | ||||||
|   /// Parameters: |  | ||||||
|   /// |  | ||||||
|   /// * [PinCodeSetupDto] pinCodeSetupDto (required): |  | ||||||
|   Future<Response> verifyPinCodeWithHttpInfo(PinCodeSetupDto pinCodeSetupDto,) async { |  | ||||||
|     // ignore: prefer_const_declarations |  | ||||||
|     final apiPath = r'/auth/pin-code/verify'; |  | ||||||
| 
 |  | ||||||
|     // ignore: prefer_final_locals |  | ||||||
|     Object? postBody = pinCodeSetupDto; |  | ||||||
| 
 |  | ||||||
|     final queryParams = <QueryParam>[]; |  | ||||||
|     final headerParams = <String, String>{}; |  | ||||||
|     final formParams = <String, String>{}; |  | ||||||
| 
 |  | ||||||
|     const contentTypes = <String>['application/json']; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     return apiClient.invokeAPI( |  | ||||||
|       apiPath, |  | ||||||
|       'POST', |  | ||||||
|       queryParams, |  | ||||||
|       postBody, |  | ||||||
|       headerParams, |  | ||||||
|       formParams, |  | ||||||
|       contentTypes.isEmpty ? null : contentTypes.first, |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /// Parameters: |  | ||||||
|   /// |  | ||||||
|   /// * [PinCodeSetupDto] pinCodeSetupDto (required): |  | ||||||
|   Future<void> verifyPinCode(PinCodeSetupDto pinCodeSetupDto,) async { |  | ||||||
|     final response = await verifyPinCodeWithHttpInfo(pinCodeSetupDto,); |  | ||||||
|     if (response.statusCode >= HttpStatus.badRequest) { |  | ||||||
|       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										40
									
								
								mobile/openapi/lib/api/sessions_api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										40
									
								
								mobile/openapi/lib/api/sessions_api.dart
									
									
									
										generated
									
									
									
								
							| @@ -179,4 +179,44 @@ class SessionsApi { | |||||||
|     } |     } | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   /// Performs an HTTP 'POST /sessions/{id}/lock' operation and returns the [Response]. | ||||||
|  |   /// Parameters: | ||||||
|  |   /// | ||||||
|  |   /// * [String] id (required): | ||||||
|  |   Future<Response> lockSessionWithHttpInfo(String id,) async { | ||||||
|  |     // ignore: prefer_const_declarations | ||||||
|  |     final apiPath = r'/sessions/{id}/lock' | ||||||
|  |       .replaceAll('{id}', id); | ||||||
|  | 
 | ||||||
|  |     // ignore: prefer_final_locals | ||||||
|  |     Object? postBody; | ||||||
|  | 
 | ||||||
|  |     final queryParams = <QueryParam>[]; | ||||||
|  |     final headerParams = <String, String>{}; | ||||||
|  |     final formParams = <String, String>{}; | ||||||
|  | 
 | ||||||
|  |     const contentTypes = <String>[]; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     return apiClient.invokeAPI( | ||||||
|  |       apiPath, | ||||||
|  |       'POST', | ||||||
|  |       queryParams, | ||||||
|  |       postBody, | ||||||
|  |       headerParams, | ||||||
|  |       formParams, | ||||||
|  |       contentTypes.isEmpty ? null : contentTypes.first, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Parameters: | ||||||
|  |   /// | ||||||
|  |   /// * [String] id (required): | ||||||
|  |   Future<void> lockSession(String id,) async { | ||||||
|  |     final response = await lockSessionWithHttpInfo(id,); | ||||||
|  |     if (response.statusCode >= HttpStatus.badRequest) { | ||||||
|  |       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								mobile/openapi/lib/api_client.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								mobile/openapi/lib/api_client.dart
									
									
									
										generated
									
									
									
								
							| @@ -434,6 +434,8 @@ class ApiClient { | |||||||
|           return PersonWithFacesResponseDto.fromJson(value); |           return PersonWithFacesResponseDto.fromJson(value); | ||||||
|         case 'PinCodeChangeDto': |         case 'PinCodeChangeDto': | ||||||
|           return PinCodeChangeDto.fromJson(value); |           return PinCodeChangeDto.fromJson(value); | ||||||
|  |         case 'PinCodeResetDto': | ||||||
|  |           return PinCodeResetDto.fromJson(value); | ||||||
|         case 'PinCodeSetupDto': |         case 'PinCodeSetupDto': | ||||||
|           return PinCodeSetupDto.fromJson(value); |           return PinCodeSetupDto.fromJson(value); | ||||||
|         case 'PlacesResponseDto': |         case 'PlacesResponseDto': | ||||||
| @@ -498,6 +500,8 @@ class ApiClient { | |||||||
|           return SessionCreateResponseDto.fromJson(value); |           return SessionCreateResponseDto.fromJson(value); | ||||||
|         case 'SessionResponseDto': |         case 'SessionResponseDto': | ||||||
|           return SessionResponseDto.fromJson(value); |           return SessionResponseDto.fromJson(value); | ||||||
|  |         case 'SessionUnlockDto': | ||||||
|  |           return SessionUnlockDto.fromJson(value); | ||||||
|         case 'SharedLinkCreateDto': |         case 'SharedLinkCreateDto': | ||||||
|           return SharedLinkCreateDto.fromJson(value); |           return SharedLinkCreateDto.fromJson(value); | ||||||
|         case 'SharedLinkEditDto': |         case 'SharedLinkEditDto': | ||||||
|   | |||||||
| @@ -13,38 +13,70 @@ part of openapi.api; | |||||||
| class AuthStatusResponseDto { | class AuthStatusResponseDto { | ||||||
|   /// Returns a new [AuthStatusResponseDto] instance. |   /// Returns a new [AuthStatusResponseDto] instance. | ||||||
|   AuthStatusResponseDto({ |   AuthStatusResponseDto({ | ||||||
|  |     this.expiresAt, | ||||||
|     required this.isElevated, |     required this.isElevated, | ||||||
|     required this.password, |     required this.password, | ||||||
|     required this.pinCode, |     required this.pinCode, | ||||||
|  |     this.pinExpiresAt, | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  |   /// | ||||||
|  |   /// Please note: This property should have been non-nullable! Since the specification file | ||||||
|  |   /// does not include a default value (using the "default:" property), however, the generated | ||||||
|  |   /// source code must fall back to having a nullable type. | ||||||
|  |   /// Consider adding a "default:" property in the specification file to hide this note. | ||||||
|  |   /// | ||||||
|  |   String? expiresAt; | ||||||
|  | 
 | ||||||
|   bool isElevated; |   bool isElevated; | ||||||
| 
 | 
 | ||||||
|   bool password; |   bool password; | ||||||
| 
 | 
 | ||||||
|   bool pinCode; |   bool pinCode; | ||||||
| 
 | 
 | ||||||
|  |   /// | ||||||
|  |   /// Please note: This property should have been non-nullable! Since the specification file | ||||||
|  |   /// does not include a default value (using the "default:" property), however, the generated | ||||||
|  |   /// source code must fall back to having a nullable type. | ||||||
|  |   /// Consider adding a "default:" property in the specification file to hide this note. | ||||||
|  |   /// | ||||||
|  |   String? pinExpiresAt; | ||||||
|  | 
 | ||||||
|   @override |   @override | ||||||
|   bool operator ==(Object other) => identical(this, other) || other is AuthStatusResponseDto && |   bool operator ==(Object other) => identical(this, other) || other is AuthStatusResponseDto && | ||||||
|  |     other.expiresAt == expiresAt && | ||||||
|     other.isElevated == isElevated && |     other.isElevated == isElevated && | ||||||
|     other.password == password && |     other.password == password && | ||||||
|     other.pinCode == pinCode; |     other.pinCode == pinCode && | ||||||
|  |     other.pinExpiresAt == pinExpiresAt; | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   int get hashCode => |   int get hashCode => | ||||||
|     // ignore: unnecessary_parenthesis |     // ignore: unnecessary_parenthesis | ||||||
|  |     (expiresAt == null ? 0 : expiresAt!.hashCode) + | ||||||
|     (isElevated.hashCode) + |     (isElevated.hashCode) + | ||||||
|     (password.hashCode) + |     (password.hashCode) + | ||||||
|     (pinCode.hashCode); |     (pinCode.hashCode) + | ||||||
|  |     (pinExpiresAt == null ? 0 : pinExpiresAt!.hashCode); | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String toString() => 'AuthStatusResponseDto[isElevated=$isElevated, password=$password, pinCode=$pinCode]'; |   String toString() => 'AuthStatusResponseDto[expiresAt=$expiresAt, isElevated=$isElevated, password=$password, pinCode=$pinCode, pinExpiresAt=$pinExpiresAt]'; | ||||||
| 
 | 
 | ||||||
|   Map<String, dynamic> toJson() { |   Map<String, dynamic> toJson() { | ||||||
|     final json = <String, dynamic>{}; |     final json = <String, dynamic>{}; | ||||||
|  |     if (this.expiresAt != null) { | ||||||
|  |       json[r'expiresAt'] = this.expiresAt; | ||||||
|  |     } else { | ||||||
|  |     //  json[r'expiresAt'] = null; | ||||||
|  |     } | ||||||
|       json[r'isElevated'] = this.isElevated; |       json[r'isElevated'] = this.isElevated; | ||||||
|       json[r'password'] = this.password; |       json[r'password'] = this.password; | ||||||
|       json[r'pinCode'] = this.pinCode; |       json[r'pinCode'] = this.pinCode; | ||||||
|  |     if (this.pinExpiresAt != null) { | ||||||
|  |       json[r'pinExpiresAt'] = this.pinExpiresAt; | ||||||
|  |     } else { | ||||||
|  |     //  json[r'pinExpiresAt'] = null; | ||||||
|  |     } | ||||||
|     return json; |     return json; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @@ -57,9 +89,11 @@ class AuthStatusResponseDto { | |||||||
|       final json = value.cast<String, dynamic>(); |       final json = value.cast<String, dynamic>(); | ||||||
| 
 | 
 | ||||||
|       return AuthStatusResponseDto( |       return AuthStatusResponseDto( | ||||||
|  |         expiresAt: mapValueOfType<String>(json, r'expiresAt'), | ||||||
|         isElevated: mapValueOfType<bool>(json, r'isElevated')!, |         isElevated: mapValueOfType<bool>(json, r'isElevated')!, | ||||||
|         password: mapValueOfType<bool>(json, r'password')!, |         password: mapValueOfType<bool>(json, r'password')!, | ||||||
|         pinCode: mapValueOfType<bool>(json, r'pinCode')!, |         pinCode: mapValueOfType<bool>(json, r'pinCode')!, | ||||||
|  |         pinExpiresAt: mapValueOfType<String>(json, r'pinExpiresAt'), | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|     return null; |     return null; | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								mobile/openapi/lib/model/permission.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3
									
								
								mobile/openapi/lib/model/permission.dart
									
									
									
										generated
									
									
									
								
							| @@ -85,6 +85,7 @@ class Permission { | |||||||
|   static const sessionPeriodRead = Permission._(r'session.read'); |   static const sessionPeriodRead = Permission._(r'session.read'); | ||||||
|   static const sessionPeriodUpdate = Permission._(r'session.update'); |   static const sessionPeriodUpdate = Permission._(r'session.update'); | ||||||
|   static const sessionPeriodDelete = Permission._(r'session.delete'); |   static const sessionPeriodDelete = Permission._(r'session.delete'); | ||||||
|  |   static const sessionPeriodLock = Permission._(r'session.lock'); | ||||||
|   static const sharedLinkPeriodCreate = Permission._(r'sharedLink.create'); |   static const sharedLinkPeriodCreate = Permission._(r'sharedLink.create'); | ||||||
|   static const sharedLinkPeriodRead = Permission._(r'sharedLink.read'); |   static const sharedLinkPeriodRead = Permission._(r'sharedLink.read'); | ||||||
|   static const sharedLinkPeriodUpdate = Permission._(r'sharedLink.update'); |   static const sharedLinkPeriodUpdate = Permission._(r'sharedLink.update'); | ||||||
| @@ -171,6 +172,7 @@ class Permission { | |||||||
|     sessionPeriodRead, |     sessionPeriodRead, | ||||||
|     sessionPeriodUpdate, |     sessionPeriodUpdate, | ||||||
|     sessionPeriodDelete, |     sessionPeriodDelete, | ||||||
|  |     sessionPeriodLock, | ||||||
|     sharedLinkPeriodCreate, |     sharedLinkPeriodCreate, | ||||||
|     sharedLinkPeriodRead, |     sharedLinkPeriodRead, | ||||||
|     sharedLinkPeriodUpdate, |     sharedLinkPeriodUpdate, | ||||||
| @@ -292,6 +294,7 @@ class PermissionTypeTransformer { | |||||||
|         case r'session.read': return Permission.sessionPeriodRead; |         case r'session.read': return Permission.sessionPeriodRead; | ||||||
|         case r'session.update': return Permission.sessionPeriodUpdate; |         case r'session.update': return Permission.sessionPeriodUpdate; | ||||||
|         case r'session.delete': return Permission.sessionPeriodDelete; |         case r'session.delete': return Permission.sessionPeriodDelete; | ||||||
|  |         case r'session.lock': return Permission.sessionPeriodLock; | ||||||
|         case r'sharedLink.create': return Permission.sharedLinkPeriodCreate; |         case r'sharedLink.create': return Permission.sharedLinkPeriodCreate; | ||||||
|         case r'sharedLink.read': return Permission.sharedLinkPeriodRead; |         case r'sharedLink.read': return Permission.sharedLinkPeriodRead; | ||||||
|         case r'sharedLink.update': return Permission.sharedLinkPeriodUpdate; |         case r'sharedLink.update': return Permission.sharedLinkPeriodUpdate; | ||||||
|   | |||||||
							
								
								
									
										125
									
								
								mobile/openapi/lib/model/pin_code_reset_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								mobile/openapi/lib/model/pin_code_reset_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | |||||||
|  | // | ||||||
|  | // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||||
|  | // | ||||||
|  | // @dart=2.18 | ||||||
|  | 
 | ||||||
|  | // ignore_for_file: unused_element, unused_import | ||||||
|  | // ignore_for_file: always_put_required_named_parameters_first | ||||||
|  | // ignore_for_file: constant_identifier_names | ||||||
|  | // ignore_for_file: lines_longer_than_80_chars | ||||||
|  | 
 | ||||||
|  | part of openapi.api; | ||||||
|  | 
 | ||||||
|  | class PinCodeResetDto { | ||||||
|  |   /// Returns a new [PinCodeResetDto] instance. | ||||||
|  |   PinCodeResetDto({ | ||||||
|  |     this.password, | ||||||
|  |     this.pinCode, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   /// | ||||||
|  |   /// Please note: This property should have been non-nullable! Since the specification file | ||||||
|  |   /// does not include a default value (using the "default:" property), however, the generated | ||||||
|  |   /// source code must fall back to having a nullable type. | ||||||
|  |   /// Consider adding a "default:" property in the specification file to hide this note. | ||||||
|  |   /// | ||||||
|  |   String? password; | ||||||
|  | 
 | ||||||
|  |   /// | ||||||
|  |   /// Please note: This property should have been non-nullable! Since the specification file | ||||||
|  |   /// does not include a default value (using the "default:" property), however, the generated | ||||||
|  |   /// source code must fall back to having a nullable type. | ||||||
|  |   /// Consider adding a "default:" property in the specification file to hide this note. | ||||||
|  |   /// | ||||||
|  |   String? pinCode; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   bool operator ==(Object other) => identical(this, other) || other is PinCodeResetDto && | ||||||
|  |     other.password == password && | ||||||
|  |     other.pinCode == pinCode; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   int get hashCode => | ||||||
|  |     // ignore: unnecessary_parenthesis | ||||||
|  |     (password == null ? 0 : password!.hashCode) + | ||||||
|  |     (pinCode == null ? 0 : pinCode!.hashCode); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => 'PinCodeResetDto[password=$password, pinCode=$pinCode]'; | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toJson() { | ||||||
|  |     final json = <String, dynamic>{}; | ||||||
|  |     if (this.password != null) { | ||||||
|  |       json[r'password'] = this.password; | ||||||
|  |     } else { | ||||||
|  |     //  json[r'password'] = null; | ||||||
|  |     } | ||||||
|  |     if (this.pinCode != null) { | ||||||
|  |       json[r'pinCode'] = this.pinCode; | ||||||
|  |     } else { | ||||||
|  |     //  json[r'pinCode'] = null; | ||||||
|  |     } | ||||||
|  |     return json; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Returns a new [PinCodeResetDto] instance and imports its values from | ||||||
|  |   /// [value] if it's a [Map], null otherwise. | ||||||
|  |   // ignore: prefer_constructors_over_static_methods | ||||||
|  |   static PinCodeResetDto? fromJson(dynamic value) { | ||||||
|  |     upgradeDto(value, "PinCodeResetDto"); | ||||||
|  |     if (value is Map) { | ||||||
|  |       final json = value.cast<String, dynamic>(); | ||||||
|  | 
 | ||||||
|  |       return PinCodeResetDto( | ||||||
|  |         password: mapValueOfType<String>(json, r'password'), | ||||||
|  |         pinCode: mapValueOfType<String>(json, r'pinCode'), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static List<PinCodeResetDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||||
|  |     final result = <PinCodeResetDto>[]; | ||||||
|  |     if (json is List && json.isNotEmpty) { | ||||||
|  |       for (final row in json) { | ||||||
|  |         final value = PinCodeResetDto.fromJson(row); | ||||||
|  |         if (value != null) { | ||||||
|  |           result.add(value); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return result.toList(growable: growable); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static Map<String, PinCodeResetDto> mapFromJson(dynamic json) { | ||||||
|  |     final map = <String, PinCodeResetDto>{}; | ||||||
|  |     if (json is Map && json.isNotEmpty) { | ||||||
|  |       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||||
|  |       for (final entry in json.entries) { | ||||||
|  |         final value = PinCodeResetDto.fromJson(entry.value); | ||||||
|  |         if (value != null) { | ||||||
|  |           map[entry.key] = value; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return map; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // maps a json object with a list of PinCodeResetDto-objects as value to a dart map | ||||||
|  |   static Map<String, List<PinCodeResetDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||||
|  |     final map = <String, List<PinCodeResetDto>>{}; | ||||||
|  |     if (json is Map && json.isNotEmpty) { | ||||||
|  |       // ignore: parameter_assignments | ||||||
|  |       json = json.cast<String, dynamic>(); | ||||||
|  |       for (final entry in json.entries) { | ||||||
|  |         map[entry.key] = PinCodeResetDto.listFromJson(entry.value, growable: growable,); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return map; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// The list of required keys that must be present in a JSON. | ||||||
|  |   static const requiredKeys = <String>{ | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| @@ -17,6 +17,7 @@ class SessionCreateResponseDto { | |||||||
|     required this.current, |     required this.current, | ||||||
|     required this.deviceOS, |     required this.deviceOS, | ||||||
|     required this.deviceType, |     required this.deviceType, | ||||||
|  |     this.expiresAt, | ||||||
|     required this.id, |     required this.id, | ||||||
|     required this.token, |     required this.token, | ||||||
|     required this.updatedAt, |     required this.updatedAt, | ||||||
| @@ -30,6 +31,14 @@ class SessionCreateResponseDto { | |||||||
| 
 | 
 | ||||||
|   String deviceType; |   String deviceType; | ||||||
| 
 | 
 | ||||||
|  |   /// | ||||||
|  |   /// Please note: This property should have been non-nullable! Since the specification file | ||||||
|  |   /// does not include a default value (using the "default:" property), however, the generated | ||||||
|  |   /// source code must fall back to having a nullable type. | ||||||
|  |   /// Consider adding a "default:" property in the specification file to hide this note. | ||||||
|  |   /// | ||||||
|  |   String? expiresAt; | ||||||
|  | 
 | ||||||
|   String id; |   String id; | ||||||
| 
 | 
 | ||||||
|   String token; |   String token; | ||||||
| @@ -42,6 +51,7 @@ class SessionCreateResponseDto { | |||||||
|     other.current == current && |     other.current == current && | ||||||
|     other.deviceOS == deviceOS && |     other.deviceOS == deviceOS && | ||||||
|     other.deviceType == deviceType && |     other.deviceType == deviceType && | ||||||
|  |     other.expiresAt == expiresAt && | ||||||
|     other.id == id && |     other.id == id && | ||||||
|     other.token == token && |     other.token == token && | ||||||
|     other.updatedAt == updatedAt; |     other.updatedAt == updatedAt; | ||||||
| @@ -53,12 +63,13 @@ class SessionCreateResponseDto { | |||||||
|     (current.hashCode) + |     (current.hashCode) + | ||||||
|     (deviceOS.hashCode) + |     (deviceOS.hashCode) + | ||||||
|     (deviceType.hashCode) + |     (deviceType.hashCode) + | ||||||
|  |     (expiresAt == null ? 0 : expiresAt!.hashCode) + | ||||||
|     (id.hashCode) + |     (id.hashCode) + | ||||||
|     (token.hashCode) + |     (token.hashCode) + | ||||||
|     (updatedAt.hashCode); |     (updatedAt.hashCode); | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String toString() => 'SessionCreateResponseDto[createdAt=$createdAt, current=$current, deviceOS=$deviceOS, deviceType=$deviceType, id=$id, token=$token, updatedAt=$updatedAt]'; |   String toString() => 'SessionCreateResponseDto[createdAt=$createdAt, current=$current, deviceOS=$deviceOS, deviceType=$deviceType, expiresAt=$expiresAt, id=$id, token=$token, updatedAt=$updatedAt]'; | ||||||
| 
 | 
 | ||||||
|   Map<String, dynamic> toJson() { |   Map<String, dynamic> toJson() { | ||||||
|     final json = <String, dynamic>{}; |     final json = <String, dynamic>{}; | ||||||
| @@ -66,6 +77,11 @@ class SessionCreateResponseDto { | |||||||
|       json[r'current'] = this.current; |       json[r'current'] = this.current; | ||||||
|       json[r'deviceOS'] = this.deviceOS; |       json[r'deviceOS'] = this.deviceOS; | ||||||
|       json[r'deviceType'] = this.deviceType; |       json[r'deviceType'] = this.deviceType; | ||||||
|  |     if (this.expiresAt != null) { | ||||||
|  |       json[r'expiresAt'] = this.expiresAt; | ||||||
|  |     } else { | ||||||
|  |     //  json[r'expiresAt'] = null; | ||||||
|  |     } | ||||||
|       json[r'id'] = this.id; |       json[r'id'] = this.id; | ||||||
|       json[r'token'] = this.token; |       json[r'token'] = this.token; | ||||||
|       json[r'updatedAt'] = this.updatedAt; |       json[r'updatedAt'] = this.updatedAt; | ||||||
| @@ -85,6 +101,7 @@ class SessionCreateResponseDto { | |||||||
|         current: mapValueOfType<bool>(json, r'current')!, |         current: mapValueOfType<bool>(json, r'current')!, | ||||||
|         deviceOS: mapValueOfType<String>(json, r'deviceOS')!, |         deviceOS: mapValueOfType<String>(json, r'deviceOS')!, | ||||||
|         deviceType: mapValueOfType<String>(json, r'deviceType')!, |         deviceType: mapValueOfType<String>(json, r'deviceType')!, | ||||||
|  |         expiresAt: mapValueOfType<String>(json, r'expiresAt'), | ||||||
|         id: mapValueOfType<String>(json, r'id')!, |         id: mapValueOfType<String>(json, r'id')!, | ||||||
|         token: mapValueOfType<String>(json, r'token')!, |         token: mapValueOfType<String>(json, r'token')!, | ||||||
|         updatedAt: mapValueOfType<String>(json, r'updatedAt')!, |         updatedAt: mapValueOfType<String>(json, r'updatedAt')!, | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								mobile/openapi/lib/model/session_response_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										19
									
								
								mobile/openapi/lib/model/session_response_dto.dart
									
									
									
										generated
									
									
									
								
							| @@ -17,6 +17,7 @@ class SessionResponseDto { | |||||||
|     required this.current, |     required this.current, | ||||||
|     required this.deviceOS, |     required this.deviceOS, | ||||||
|     required this.deviceType, |     required this.deviceType, | ||||||
|  |     this.expiresAt, | ||||||
|     required this.id, |     required this.id, | ||||||
|     required this.updatedAt, |     required this.updatedAt, | ||||||
|   }); |   }); | ||||||
| @@ -29,6 +30,14 @@ class SessionResponseDto { | |||||||
| 
 | 
 | ||||||
|   String deviceType; |   String deviceType; | ||||||
| 
 | 
 | ||||||
|  |   /// | ||||||
|  |   /// Please note: This property should have been non-nullable! Since the specification file | ||||||
|  |   /// does not include a default value (using the "default:" property), however, the generated | ||||||
|  |   /// source code must fall back to having a nullable type. | ||||||
|  |   /// Consider adding a "default:" property in the specification file to hide this note. | ||||||
|  |   /// | ||||||
|  |   String? expiresAt; | ||||||
|  | 
 | ||||||
|   String id; |   String id; | ||||||
| 
 | 
 | ||||||
|   String updatedAt; |   String updatedAt; | ||||||
| @@ -39,6 +48,7 @@ class SessionResponseDto { | |||||||
|     other.current == current && |     other.current == current && | ||||||
|     other.deviceOS == deviceOS && |     other.deviceOS == deviceOS && | ||||||
|     other.deviceType == deviceType && |     other.deviceType == deviceType && | ||||||
|  |     other.expiresAt == expiresAt && | ||||||
|     other.id == id && |     other.id == id && | ||||||
|     other.updatedAt == updatedAt; |     other.updatedAt == updatedAt; | ||||||
| 
 | 
 | ||||||
| @@ -49,11 +59,12 @@ class SessionResponseDto { | |||||||
|     (current.hashCode) + |     (current.hashCode) + | ||||||
|     (deviceOS.hashCode) + |     (deviceOS.hashCode) + | ||||||
|     (deviceType.hashCode) + |     (deviceType.hashCode) + | ||||||
|  |     (expiresAt == null ? 0 : expiresAt!.hashCode) + | ||||||
|     (id.hashCode) + |     (id.hashCode) + | ||||||
|     (updatedAt.hashCode); |     (updatedAt.hashCode); | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String toString() => 'SessionResponseDto[createdAt=$createdAt, current=$current, deviceOS=$deviceOS, deviceType=$deviceType, id=$id, updatedAt=$updatedAt]'; |   String toString() => 'SessionResponseDto[createdAt=$createdAt, current=$current, deviceOS=$deviceOS, deviceType=$deviceType, expiresAt=$expiresAt, id=$id, updatedAt=$updatedAt]'; | ||||||
| 
 | 
 | ||||||
|   Map<String, dynamic> toJson() { |   Map<String, dynamic> toJson() { | ||||||
|     final json = <String, dynamic>{}; |     final json = <String, dynamic>{}; | ||||||
| @@ -61,6 +72,11 @@ class SessionResponseDto { | |||||||
|       json[r'current'] = this.current; |       json[r'current'] = this.current; | ||||||
|       json[r'deviceOS'] = this.deviceOS; |       json[r'deviceOS'] = this.deviceOS; | ||||||
|       json[r'deviceType'] = this.deviceType; |       json[r'deviceType'] = this.deviceType; | ||||||
|  |     if (this.expiresAt != null) { | ||||||
|  |       json[r'expiresAt'] = this.expiresAt; | ||||||
|  |     } else { | ||||||
|  |     //  json[r'expiresAt'] = null; | ||||||
|  |     } | ||||||
|       json[r'id'] = this.id; |       json[r'id'] = this.id; | ||||||
|       json[r'updatedAt'] = this.updatedAt; |       json[r'updatedAt'] = this.updatedAt; | ||||||
|     return json; |     return json; | ||||||
| @@ -79,6 +95,7 @@ class SessionResponseDto { | |||||||
|         current: mapValueOfType<bool>(json, r'current')!, |         current: mapValueOfType<bool>(json, r'current')!, | ||||||
|         deviceOS: mapValueOfType<String>(json, r'deviceOS')!, |         deviceOS: mapValueOfType<String>(json, r'deviceOS')!, | ||||||
|         deviceType: mapValueOfType<String>(json, r'deviceType')!, |         deviceType: mapValueOfType<String>(json, r'deviceType')!, | ||||||
|  |         expiresAt: mapValueOfType<String>(json, r'expiresAt'), | ||||||
|         id: mapValueOfType<String>(json, r'id')!, |         id: mapValueOfType<String>(json, r'id')!, | ||||||
|         updatedAt: mapValueOfType<String>(json, r'updatedAt')!, |         updatedAt: mapValueOfType<String>(json, r'updatedAt')!, | ||||||
|       ); |       ); | ||||||
|   | |||||||
							
								
								
									
										125
									
								
								mobile/openapi/lib/model/session_unlock_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								mobile/openapi/lib/model/session_unlock_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | |||||||
|  | // | ||||||
|  | // AUTO-GENERATED FILE, DO NOT MODIFY! | ||||||
|  | // | ||||||
|  | // @dart=2.18 | ||||||
|  | 
 | ||||||
|  | // ignore_for_file: unused_element, unused_import | ||||||
|  | // ignore_for_file: always_put_required_named_parameters_first | ||||||
|  | // ignore_for_file: constant_identifier_names | ||||||
|  | // ignore_for_file: lines_longer_than_80_chars | ||||||
|  | 
 | ||||||
|  | part of openapi.api; | ||||||
|  | 
 | ||||||
|  | class SessionUnlockDto { | ||||||
|  |   /// Returns a new [SessionUnlockDto] instance. | ||||||
|  |   SessionUnlockDto({ | ||||||
|  |     this.password, | ||||||
|  |     this.pinCode, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   /// | ||||||
|  |   /// Please note: This property should have been non-nullable! Since the specification file | ||||||
|  |   /// does not include a default value (using the "default:" property), however, the generated | ||||||
|  |   /// source code must fall back to having a nullable type. | ||||||
|  |   /// Consider adding a "default:" property in the specification file to hide this note. | ||||||
|  |   /// | ||||||
|  |   String? password; | ||||||
|  | 
 | ||||||
|  |   /// | ||||||
|  |   /// Please note: This property should have been non-nullable! Since the specification file | ||||||
|  |   /// does not include a default value (using the "default:" property), however, the generated | ||||||
|  |   /// source code must fall back to having a nullable type. | ||||||
|  |   /// Consider adding a "default:" property in the specification file to hide this note. | ||||||
|  |   /// | ||||||
|  |   String? pinCode; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   bool operator ==(Object other) => identical(this, other) || other is SessionUnlockDto && | ||||||
|  |     other.password == password && | ||||||
|  |     other.pinCode == pinCode; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   int get hashCode => | ||||||
|  |     // ignore: unnecessary_parenthesis | ||||||
|  |     (password == null ? 0 : password!.hashCode) + | ||||||
|  |     (pinCode == null ? 0 : pinCode!.hashCode); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => 'SessionUnlockDto[password=$password, pinCode=$pinCode]'; | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toJson() { | ||||||
|  |     final json = <String, dynamic>{}; | ||||||
|  |     if (this.password != null) { | ||||||
|  |       json[r'password'] = this.password; | ||||||
|  |     } else { | ||||||
|  |     //  json[r'password'] = null; | ||||||
|  |     } | ||||||
|  |     if (this.pinCode != null) { | ||||||
|  |       json[r'pinCode'] = this.pinCode; | ||||||
|  |     } else { | ||||||
|  |     //  json[r'pinCode'] = null; | ||||||
|  |     } | ||||||
|  |     return json; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Returns a new [SessionUnlockDto] instance and imports its values from | ||||||
|  |   /// [value] if it's a [Map], null otherwise. | ||||||
|  |   // ignore: prefer_constructors_over_static_methods | ||||||
|  |   static SessionUnlockDto? fromJson(dynamic value) { | ||||||
|  |     upgradeDto(value, "SessionUnlockDto"); | ||||||
|  |     if (value is Map) { | ||||||
|  |       final json = value.cast<String, dynamic>(); | ||||||
|  | 
 | ||||||
|  |       return SessionUnlockDto( | ||||||
|  |         password: mapValueOfType<String>(json, r'password'), | ||||||
|  |         pinCode: mapValueOfType<String>(json, r'pinCode'), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static List<SessionUnlockDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||||
|  |     final result = <SessionUnlockDto>[]; | ||||||
|  |     if (json is List && json.isNotEmpty) { | ||||||
|  |       for (final row in json) { | ||||||
|  |         final value = SessionUnlockDto.fromJson(row); | ||||||
|  |         if (value != null) { | ||||||
|  |           result.add(value); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return result.toList(growable: growable); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static Map<String, SessionUnlockDto> mapFromJson(dynamic json) { | ||||||
|  |     final map = <String, SessionUnlockDto>{}; | ||||||
|  |     if (json is Map && json.isNotEmpty) { | ||||||
|  |       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||||
|  |       for (final entry in json.entries) { | ||||||
|  |         final value = SessionUnlockDto.fromJson(entry.value); | ||||||
|  |         if (value != null) { | ||||||
|  |           map[entry.key] = value; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return map; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // maps a json object with a list of SessionUnlockDto-objects as value to a dart map | ||||||
|  |   static Map<String, List<SessionUnlockDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||||
|  |     final map = <String, List<SessionUnlockDto>>{}; | ||||||
|  |     if (json is Map && json.isNotEmpty) { | ||||||
|  |       // ignore: parameter_assignments | ||||||
|  |       json = json.cast<String, dynamic>(); | ||||||
|  |       for (final entry in json.entries) { | ||||||
|  |         map[entry.key] = SessionUnlockDto.listFromJson(entry.value, growable: growable,); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return map; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// The list of required keys that must be present in a JSON. | ||||||
|  |   static const requiredKeys = <String>{ | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| @@ -2377,7 +2377,7 @@ | |||||||
|           "content": { |           "content": { | ||||||
|             "application/json": { |             "application/json": { | ||||||
|               "schema": { |               "schema": { | ||||||
|                 "$ref": "#/components/schemas/PinCodeChangeDto" |                 "$ref": "#/components/schemas/PinCodeResetDto" | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
| @@ -2470,15 +2470,40 @@ | |||||||
|         ] |         ] | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "/auth/pin-code/verify": { |     "/auth/session/lock": { | ||||||
|       "post": { |       "post": { | ||||||
|         "operationId": "verifyPinCode", |         "operationId": "lockAuthSession", | ||||||
|  |         "parameters": [], | ||||||
|  |         "responses": { | ||||||
|  |           "200": { | ||||||
|  |             "description": "" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "security": [ | ||||||
|  |           { | ||||||
|  |             "bearer": [] | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "cookie": [] | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "api_key": [] | ||||||
|  |           } | ||||||
|  |         ], | ||||||
|  |         "tags": [ | ||||||
|  |           "Authentication" | ||||||
|  |         ] | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "/auth/session/unlock": { | ||||||
|  |       "post": { | ||||||
|  |         "operationId": "unlockAuthSession", | ||||||
|         "parameters": [], |         "parameters": [], | ||||||
|         "requestBody": { |         "requestBody": { | ||||||
|           "content": { |           "content": { | ||||||
|             "application/json": { |             "application/json": { | ||||||
|               "schema": { |               "schema": { | ||||||
|                 "$ref": "#/components/schemas/PinCodeSetupDto" |                 "$ref": "#/components/schemas/SessionUnlockDto" | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
| @@ -5695,6 +5720,41 @@ | |||||||
|         ] |         ] | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "/sessions/{id}/lock": { | ||||||
|  |       "post": { | ||||||
|  |         "operationId": "lockSession", | ||||||
|  |         "parameters": [ | ||||||
|  |           { | ||||||
|  |             "name": "id", | ||||||
|  |             "required": true, | ||||||
|  |             "in": "path", | ||||||
|  |             "schema": { | ||||||
|  |               "format": "uuid", | ||||||
|  |               "type": "string" | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         ], | ||||||
|  |         "responses": { | ||||||
|  |           "204": { | ||||||
|  |             "description": "" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "security": [ | ||||||
|  |           { | ||||||
|  |             "bearer": [] | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "cookie": [] | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "api_key": [] | ||||||
|  |           } | ||||||
|  |         ], | ||||||
|  |         "tags": [ | ||||||
|  |           "Sessions" | ||||||
|  |         ] | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "/shared-links": { |     "/shared-links": { | ||||||
|       "get": { |       "get": { | ||||||
|         "operationId": "getAllSharedLinks", |         "operationId": "getAllSharedLinks", | ||||||
| @@ -9327,6 +9387,9 @@ | |||||||
|       }, |       }, | ||||||
|       "AuthStatusResponseDto": { |       "AuthStatusResponseDto": { | ||||||
|         "properties": { |         "properties": { | ||||||
|  |           "expiresAt": { | ||||||
|  |             "type": "string" | ||||||
|  |           }, | ||||||
|           "isElevated": { |           "isElevated": { | ||||||
|             "type": "boolean" |             "type": "boolean" | ||||||
|           }, |           }, | ||||||
| @@ -9335,6 +9398,9 @@ | |||||||
|           }, |           }, | ||||||
|           "pinCode": { |           "pinCode": { | ||||||
|             "type": "boolean" |             "type": "boolean" | ||||||
|  |           }, | ||||||
|  |           "pinExpiresAt": { | ||||||
|  |             "type": "string" | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         "required": [ |         "required": [ | ||||||
| @@ -11096,6 +11162,7 @@ | |||||||
|           "session.read", |           "session.read", | ||||||
|           "session.update", |           "session.update", | ||||||
|           "session.delete", |           "session.delete", | ||||||
|  |           "session.lock", | ||||||
|           "sharedLink.create", |           "sharedLink.create", | ||||||
|           "sharedLink.read", |           "sharedLink.read", | ||||||
|           "sharedLink.update", |           "sharedLink.update", | ||||||
| @@ -11297,6 +11364,18 @@ | |||||||
|         ], |         ], | ||||||
|         "type": "object" |         "type": "object" | ||||||
|       }, |       }, | ||||||
|  |       "PinCodeResetDto": { | ||||||
|  |         "properties": { | ||||||
|  |           "password": { | ||||||
|  |             "type": "string" | ||||||
|  |           }, | ||||||
|  |           "pinCode": { | ||||||
|  |             "example": "123456", | ||||||
|  |             "type": "string" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "type": "object" | ||||||
|  |       }, | ||||||
|       "PinCodeSetupDto": { |       "PinCodeSetupDto": { | ||||||
|         "properties": { |         "properties": { | ||||||
|           "pinCode": { |           "pinCode": { | ||||||
| @@ -12109,6 +12188,9 @@ | |||||||
|           "deviceType": { |           "deviceType": { | ||||||
|             "type": "string" |             "type": "string" | ||||||
|           }, |           }, | ||||||
|  |           "expiresAt": { | ||||||
|  |             "type": "string" | ||||||
|  |           }, | ||||||
|           "id": { |           "id": { | ||||||
|             "type": "string" |             "type": "string" | ||||||
|           }, |           }, | ||||||
| @@ -12144,6 +12226,9 @@ | |||||||
|           "deviceType": { |           "deviceType": { | ||||||
|             "type": "string" |             "type": "string" | ||||||
|           }, |           }, | ||||||
|  |           "expiresAt": { | ||||||
|  |             "type": "string" | ||||||
|  |           }, | ||||||
|           "id": { |           "id": { | ||||||
|             "type": "string" |             "type": "string" | ||||||
|           }, |           }, | ||||||
| @@ -12161,6 +12246,18 @@ | |||||||
|         ], |         ], | ||||||
|         "type": "object" |         "type": "object" | ||||||
|       }, |       }, | ||||||
|  |       "SessionUnlockDto": { | ||||||
|  |         "properties": { | ||||||
|  |           "password": { | ||||||
|  |             "type": "string" | ||||||
|  |           }, | ||||||
|  |           "pinCode": { | ||||||
|  |             "example": "123456", | ||||||
|  |             "type": "string" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "type": "object" | ||||||
|  |       }, | ||||||
|       "SharedLinkCreateDto": { |       "SharedLinkCreateDto": { | ||||||
|         "properties": { |         "properties": { | ||||||
|           "albumId": { |           "albumId": { | ||||||
|   | |||||||
| @@ -512,18 +512,28 @@ export type LogoutResponseDto = { | |||||||
|     redirectUri: string; |     redirectUri: string; | ||||||
|     successful: boolean; |     successful: boolean; | ||||||
| }; | }; | ||||||
| export type PinCodeChangeDto = { | export type PinCodeResetDto = { | ||||||
|     newPinCode: string; |  | ||||||
|     password?: string; |     password?: string; | ||||||
|     pinCode?: string; |     pinCode?: string; | ||||||
| }; | }; | ||||||
| export type PinCodeSetupDto = { | export type PinCodeSetupDto = { | ||||||
|     pinCode: string; |     pinCode: string; | ||||||
| }; | }; | ||||||
|  | export type PinCodeChangeDto = { | ||||||
|  |     newPinCode: string; | ||||||
|  |     password?: string; | ||||||
|  |     pinCode?: string; | ||||||
|  | }; | ||||||
|  | export type SessionUnlockDto = { | ||||||
|  |     password?: string; | ||||||
|  |     pinCode?: string; | ||||||
|  | }; | ||||||
| export type AuthStatusResponseDto = { | export type AuthStatusResponseDto = { | ||||||
|  |     expiresAt?: string; | ||||||
|     isElevated: boolean; |     isElevated: boolean; | ||||||
|     password: boolean; |     password: boolean; | ||||||
|     pinCode: boolean; |     pinCode: boolean; | ||||||
|  |     pinExpiresAt?: string; | ||||||
| }; | }; | ||||||
| export type ValidateAccessTokenResponseDto = { | export type ValidateAccessTokenResponseDto = { | ||||||
|     authStatus: boolean; |     authStatus: boolean; | ||||||
| @@ -1075,6 +1085,7 @@ export type SessionResponseDto = { | |||||||
|     current: boolean; |     current: boolean; | ||||||
|     deviceOS: string; |     deviceOS: string; | ||||||
|     deviceType: string; |     deviceType: string; | ||||||
|  |     expiresAt?: string; | ||||||
|     id: string; |     id: string; | ||||||
|     updatedAt: string; |     updatedAt: string; | ||||||
| }; | }; | ||||||
| @@ -1089,6 +1100,7 @@ export type SessionCreateResponseDto = { | |||||||
|     current: boolean; |     current: boolean; | ||||||
|     deviceOS: string; |     deviceOS: string; | ||||||
|     deviceType: string; |     deviceType: string; | ||||||
|  |     expiresAt?: string; | ||||||
|     id: string; |     id: string; | ||||||
|     token: string; |     token: string; | ||||||
|     updatedAt: string; |     updatedAt: string; | ||||||
| @@ -2066,13 +2078,13 @@ export function logout(opts?: Oazapfts.RequestOpts) { | |||||||
|         method: "POST" |         method: "POST" | ||||||
|     })); |     })); | ||||||
| } | } | ||||||
| export function resetPinCode({ pinCodeChangeDto }: { | export function resetPinCode({ pinCodeResetDto }: { | ||||||
|     pinCodeChangeDto: PinCodeChangeDto; |     pinCodeResetDto: PinCodeResetDto; | ||||||
| }, opts?: Oazapfts.RequestOpts) { | }, opts?: Oazapfts.RequestOpts) { | ||||||
|     return oazapfts.ok(oazapfts.fetchText("/auth/pin-code", oazapfts.json({ |     return oazapfts.ok(oazapfts.fetchText("/auth/pin-code", oazapfts.json({ | ||||||
|         ...opts, |         ...opts, | ||||||
|         method: "DELETE", |         method: "DELETE", | ||||||
|         body: pinCodeChangeDto |         body: pinCodeResetDto | ||||||
|     }))); |     }))); | ||||||
| } | } | ||||||
| export function setupPinCode({ pinCodeSetupDto }: { | export function setupPinCode({ pinCodeSetupDto }: { | ||||||
| @@ -2093,13 +2105,19 @@ export function changePinCode({ pinCodeChangeDto }: { | |||||||
|         body: pinCodeChangeDto |         body: pinCodeChangeDto | ||||||
|     }))); |     }))); | ||||||
| } | } | ||||||
| export function verifyPinCode({ pinCodeSetupDto }: { | export function lockAuthSession(opts?: Oazapfts.RequestOpts) { | ||||||
|     pinCodeSetupDto: PinCodeSetupDto; |     return oazapfts.ok(oazapfts.fetchText("/auth/session/lock", { | ||||||
|  |         ...opts, | ||||||
|  |         method: "POST" | ||||||
|  |     })); | ||||||
|  | } | ||||||
|  | export function unlockAuthSession({ sessionUnlockDto }: { | ||||||
|  |     sessionUnlockDto: SessionUnlockDto; | ||||||
| }, opts?: Oazapfts.RequestOpts) { | }, opts?: Oazapfts.RequestOpts) { | ||||||
|     return oazapfts.ok(oazapfts.fetchText("/auth/pin-code/verify", oazapfts.json({ |     return oazapfts.ok(oazapfts.fetchText("/auth/session/unlock", oazapfts.json({ | ||||||
|         ...opts, |         ...opts, | ||||||
|         method: "POST", |         method: "POST", | ||||||
|         body: pinCodeSetupDto |         body: sessionUnlockDto | ||||||
|     }))); |     }))); | ||||||
| } | } | ||||||
| export function getAuthStatus(opts?: Oazapfts.RequestOpts) { | export function getAuthStatus(opts?: Oazapfts.RequestOpts) { | ||||||
| @@ -2952,6 +2970,14 @@ export function deleteSession({ id }: { | |||||||
|         method: "DELETE" |         method: "DELETE" | ||||||
|     })); |     })); | ||||||
| } | } | ||||||
|  | export function lockSession({ id }: { | ||||||
|  |     id: string; | ||||||
|  | }, opts?: Oazapfts.RequestOpts) { | ||||||
|  |     return oazapfts.ok(oazapfts.fetchText(`/sessions/${encodeURIComponent(id)}/lock`, { | ||||||
|  |         ...opts, | ||||||
|  |         method: "POST" | ||||||
|  |     })); | ||||||
|  | } | ||||||
| export function getAllSharedLinks({ albumId }: { | export function getAllSharedLinks({ albumId }: { | ||||||
|     albumId?: string; |     albumId?: string; | ||||||
| }, opts?: Oazapfts.RequestOpts) { | }, opts?: Oazapfts.RequestOpts) { | ||||||
| @@ -3709,6 +3735,7 @@ export enum Permission { | |||||||
|     SessionRead = "session.read", |     SessionRead = "session.read", | ||||||
|     SessionUpdate = "session.update", |     SessionUpdate = "session.update", | ||||||
|     SessionDelete = "session.delete", |     SessionDelete = "session.delete", | ||||||
|  |     SessionLock = "session.lock", | ||||||
|     SharedLinkCreate = "sharedLink.create", |     SharedLinkCreate = "sharedLink.create", | ||||||
|     SharedLinkRead = "sharedLink.read", |     SharedLinkRead = "sharedLink.read", | ||||||
|     SharedLinkUpdate = "sharedLink.update", |     SharedLinkUpdate = "sharedLink.update", | ||||||
|   | |||||||
| @@ -9,7 +9,9 @@ import { | |||||||
|   LoginResponseDto, |   LoginResponseDto, | ||||||
|   LogoutResponseDto, |   LogoutResponseDto, | ||||||
|   PinCodeChangeDto, |   PinCodeChangeDto, | ||||||
|  |   PinCodeResetDto, | ||||||
|   PinCodeSetupDto, |   PinCodeSetupDto, | ||||||
|  |   SessionUnlockDto, | ||||||
|   SignUpDto, |   SignUpDto, | ||||||
|   ValidateAccessTokenResponseDto, |   ValidateAccessTokenResponseDto, | ||||||
| } from 'src/dtos/auth.dto'; | } from 'src/dtos/auth.dto'; | ||||||
| @@ -98,14 +100,21 @@ export class AuthController { | |||||||
|  |  | ||||||
|   @Delete('pin-code') |   @Delete('pin-code') | ||||||
|   @Authenticated() |   @Authenticated() | ||||||
|   async resetPinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeChangeDto): Promise<void> { |   async resetPinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeResetDto): Promise<void> { | ||||||
|     return this.service.resetPinCode(auth, dto); |     return this.service.resetPinCode(auth, dto); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Post('pin-code/verify') |   @Post('session/unlock') | ||||||
|   @HttpCode(HttpStatus.OK) |   @HttpCode(HttpStatus.OK) | ||||||
|   @Authenticated() |   @Authenticated() | ||||||
|   async verifyPinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeSetupDto): Promise<void> { |   async unlockAuthSession(@Auth() auth: AuthDto, @Body() dto: SessionUnlockDto): Promise<void> { | ||||||
|     return this.service.verifyPinCode(auth, dto); |     return this.service.unlockSession(auth, dto); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Post('session/lock') | ||||||
|  |   @HttpCode(HttpStatus.OK) | ||||||
|  |   @Authenticated() | ||||||
|  |   async lockAuthSession(@Auth() auth: AuthDto): Promise<void> { | ||||||
|  |     return this.service.lockSession(auth); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -37,4 +37,11 @@ export class SessionController { | |||||||
|   deleteSession(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> { |   deleteSession(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> { | ||||||
|     return this.service.delete(auth, id); |     return this.service.delete(auth, id); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   @Post(':id/lock') | ||||||
|  |   @Authenticated({ permission: Permission.SESSION_LOCK }) | ||||||
|  |   @HttpCode(HttpStatus.NO_CONTENT) | ||||||
|  |   lockSession(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> { | ||||||
|  |     return this.service.lock(auth, id); | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -232,6 +232,7 @@ export type Session = { | |||||||
|   id: string; |   id: string; | ||||||
|   createdAt: Date; |   createdAt: Date; | ||||||
|   updatedAt: Date; |   updatedAt: Date; | ||||||
|  |   expiresAt: Date | null; | ||||||
|   deviceOS: string; |   deviceOS: string; | ||||||
|   deviceType: string; |   deviceType: string; | ||||||
|   pinExpiresAt: Date | null; |   pinExpiresAt: Date | null; | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								server/src/db.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								server/src/db.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -344,7 +344,7 @@ export interface Sessions { | |||||||
|   deviceType: Generated<string>; |   deviceType: Generated<string>; | ||||||
|   id: Generated<string>; |   id: Generated<string>; | ||||||
|   parentId: string | null; |   parentId: string | null; | ||||||
|   expiredAt: Date | null; |   expiresAt: Date | null; | ||||||
|   token: string; |   token: string; | ||||||
|   updatedAt: Generated<Timestamp>; |   updatedAt: Generated<Timestamp>; | ||||||
|   updateId: Generated<string>; |   updateId: Generated<string>; | ||||||
|   | |||||||
| @@ -93,6 +93,8 @@ export class PinCodeResetDto { | |||||||
|   password?: string; |   password?: string; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export class SessionUnlockDto extends PinCodeResetDto {} | ||||||
|  |  | ||||||
| export class PinCodeChangeDto extends PinCodeResetDto { | export class PinCodeChangeDto extends PinCodeResetDto { | ||||||
|   @PinCode() |   @PinCode() | ||||||
|   newPinCode!: string; |   newPinCode!: string; | ||||||
| @@ -139,4 +141,6 @@ export class AuthStatusResponseDto { | |||||||
|   pinCode!: boolean; |   pinCode!: boolean; | ||||||
|   password!: boolean; |   password!: boolean; | ||||||
|   isElevated!: boolean; |   isElevated!: boolean; | ||||||
|  |   expiresAt?: string; | ||||||
|  |   pinExpiresAt?: string; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -24,6 +24,7 @@ export class SessionResponseDto { | |||||||
|   id!: string; |   id!: string; | ||||||
|   createdAt!: string; |   createdAt!: string; | ||||||
|   updatedAt!: string; |   updatedAt!: string; | ||||||
|  |   expiresAt?: string; | ||||||
|   current!: boolean; |   current!: boolean; | ||||||
|   deviceType!: string; |   deviceType!: string; | ||||||
|   deviceOS!: string; |   deviceOS!: string; | ||||||
| @@ -37,6 +38,7 @@ export const mapSession = (entity: Session, currentId?: string): SessionResponse | |||||||
|   id: entity.id, |   id: entity.id, | ||||||
|   createdAt: entity.createdAt.toISOString(), |   createdAt: entity.createdAt.toISOString(), | ||||||
|   updatedAt: entity.updatedAt.toISOString(), |   updatedAt: entity.updatedAt.toISOString(), | ||||||
|  |   expiresAt: entity.expiresAt?.toISOString(), | ||||||
|   current: currentId === entity.id, |   current: currentId === entity.id, | ||||||
|   deviceOS: entity.deviceOS, |   deviceOS: entity.deviceOS, | ||||||
|   deviceType: entity.deviceType, |   deviceType: entity.deviceType, | ||||||
|   | |||||||
| @@ -148,6 +148,7 @@ export enum Permission { | |||||||
|   SESSION_READ = 'session.read', |   SESSION_READ = 'session.read', | ||||||
|   SESSION_UPDATE = 'session.update', |   SESSION_UPDATE = 'session.update', | ||||||
|   SESSION_DELETE = 'session.delete', |   SESSION_DELETE = 'session.delete', | ||||||
|  |   SESSION_LOCK = 'session.lock', | ||||||
|  |  | ||||||
|   SHARED_LINK_CREATE = 'sharedLink.create', |   SHARED_LINK_CREATE = 'sharedLink.create', | ||||||
|   SHARED_LINK_READ = 'sharedLink.read', |   SHARED_LINK_READ = 'sharedLink.read', | ||||||
|   | |||||||
| @@ -199,6 +199,15 @@ where | |||||||
|   "partners"."sharedById" in ($1) |   "partners"."sharedById" in ($1) | ||||||
|   and "partners"."sharedWithId" = $2 |   and "partners"."sharedWithId" = $2 | ||||||
|  |  | ||||||
|  | -- AccessRepository.session.checkOwnerAccess | ||||||
|  | select | ||||||
|  |   "sessions"."id" | ||||||
|  | from | ||||||
|  |   "sessions" | ||||||
|  | where | ||||||
|  |   "sessions"."id" in ($1) | ||||||
|  |   and "sessions"."userId" = $2 | ||||||
|  |  | ||||||
| -- AccessRepository.stack.checkOwnerAccess | -- AccessRepository.stack.checkOwnerAccess | ||||||
| select | select | ||||||
|   "stacks"."id" |   "stacks"."id" | ||||||
|   | |||||||
| @@ -1,12 +1,14 @@ | |||||||
| -- NOTE: This file is auto generated by ./sql-generator | -- NOTE: This file is auto generated by ./sql-generator | ||||||
|  |  | ||||||
| -- SessionRepository.search | -- SessionRepository.get | ||||||
| select | select | ||||||
|   * |   "id", | ||||||
|  |   "expiresAt", | ||||||
|  |   "pinExpiresAt" | ||||||
| from | from | ||||||
|   "sessions" |   "sessions" | ||||||
| where | where | ||||||
|   "sessions"."updatedAt" <= $1 |   "id" = $1 | ||||||
|  |  | ||||||
| -- SessionRepository.getByToken | -- SessionRepository.getByToken | ||||||
| select | select | ||||||
| @@ -37,8 +39,8 @@ from | |||||||
| where | where | ||||||
|   "sessions"."token" = $1 |   "sessions"."token" = $1 | ||||||
|   and ( |   and ( | ||||||
|     "sessions"."expiredAt" is null |     "sessions"."expiresAt" is null | ||||||
|     or "sessions"."expiredAt" > $2 |     or "sessions"."expiresAt" > $2 | ||||||
|   ) |   ) | ||||||
|  |  | ||||||
| -- SessionRepository.getByUserId | -- SessionRepository.getByUserId | ||||||
| @@ -50,6 +52,10 @@ from | |||||||
|   and "users"."deletedAt" is null |   and "users"."deletedAt" is null | ||||||
| where | where | ||||||
|   "sessions"."userId" = $1 |   "sessions"."userId" = $1 | ||||||
|  |   and ( | ||||||
|  |     "sessions"."expiresAt" is null | ||||||
|  |     or "sessions"."expiresAt" > $2 | ||||||
|  |   ) | ||||||
| order by | order by | ||||||
|   "sessions"."updatedAt" desc, |   "sessions"."updatedAt" desc, | ||||||
|   "sessions"."createdAt" desc |   "sessions"."createdAt" desc | ||||||
| @@ -58,3 +64,10 @@ order by | |||||||
| delete from "sessions" | delete from "sessions" | ||||||
| where | where | ||||||
|   "id" = $1::uuid |   "id" = $1::uuid | ||||||
|  |  | ||||||
|  | -- SessionRepository.lockAll | ||||||
|  | update "sessions" | ||||||
|  | set | ||||||
|  |   "pinExpiresAt" = $1 | ||||||
|  | where | ||||||
|  |   "userId" = $2 | ||||||
|   | |||||||
| @@ -306,6 +306,25 @@ class NotificationAccess { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | class SessionAccess { | ||||||
|  |   constructor(private db: Kysely<DB>) {} | ||||||
|  |  | ||||||
|  |   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||||
|  |   @ChunkedSet({ paramIndex: 1 }) | ||||||
|  |   async checkOwnerAccess(userId: string, sessionIds: Set<string>) { | ||||||
|  |     if (sessionIds.size === 0) { | ||||||
|  |       return new Set<string>(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return this.db | ||||||
|  |       .selectFrom('sessions') | ||||||
|  |       .select('sessions.id') | ||||||
|  |       .where('sessions.id', 'in', [...sessionIds]) | ||||||
|  |       .where('sessions.userId', '=', userId) | ||||||
|  |       .execute() | ||||||
|  |       .then((sessions) => new Set(sessions.map((session) => session.id))); | ||||||
|  |   } | ||||||
|  | } | ||||||
| class StackAccess { | class StackAccess { | ||||||
|   constructor(private db: Kysely<DB>) {} |   constructor(private db: Kysely<DB>) {} | ||||||
|  |  | ||||||
| @@ -456,6 +475,7 @@ export class AccessRepository { | |||||||
|   notification: NotificationAccess; |   notification: NotificationAccess; | ||||||
|   person: PersonAccess; |   person: PersonAccess; | ||||||
|   partner: PartnerAccess; |   partner: PartnerAccess; | ||||||
|  |   session: SessionAccess; | ||||||
|   stack: StackAccess; |   stack: StackAccess; | ||||||
|   tag: TagAccess; |   tag: TagAccess; | ||||||
|   timeline: TimelineAccess; |   timeline: TimelineAccess; | ||||||
| @@ -469,6 +489,7 @@ export class AccessRepository { | |||||||
|     this.notification = new NotificationAccess(db); |     this.notification = new NotificationAccess(db); | ||||||
|     this.person = new PersonAccess(db); |     this.person = new PersonAccess(db); | ||||||
|     this.partner = new PartnerAccess(db); |     this.partner = new PartnerAccess(db); | ||||||
|  |     this.session = new SessionAccess(db); | ||||||
|     this.stack = new StackAccess(db); |     this.stack = new StackAccess(db); | ||||||
|     this.tag = new TagAccess(db); |     this.tag = new TagAccess(db); | ||||||
|     this.timeline = new TimelineAccess(db); |     this.timeline = new TimelineAccess(db); | ||||||
|   | |||||||
| @@ -20,20 +20,20 @@ export class SessionRepository { | |||||||
|       .where((eb) => |       .where((eb) => | ||||||
|         eb.or([ |         eb.or([ | ||||||
|           eb('updatedAt', '<=', DateTime.now().minus({ days: 90 }).toJSDate()), |           eb('updatedAt', '<=', DateTime.now().minus({ days: 90 }).toJSDate()), | ||||||
|           eb.and([eb('expiredAt', 'is not', null), eb('expiredAt', '<=', DateTime.now().toJSDate())]), |           eb.and([eb('expiresAt', 'is not', null), eb('expiresAt', '<=', DateTime.now().toJSDate())]), | ||||||
|         ]), |         ]), | ||||||
|       ) |       ) | ||||||
|       .returning(['id', 'deviceOS', 'deviceType']) |       .returning(['id', 'deviceOS', 'deviceType']) | ||||||
|       .execute(); |       .execute(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @GenerateSql({ params: [{ updatedBefore: DummyValue.DATE }] }) |   @GenerateSql({ params: [DummyValue.UUID] }) | ||||||
|   search(options: SessionSearchOptions) { |   get(id: string) { | ||||||
|     return this.db |     return this.db | ||||||
|       .selectFrom('sessions') |       .selectFrom('sessions') | ||||||
|       .selectAll() |       .select(['id', 'expiresAt', 'pinExpiresAt']) | ||||||
|       .where('sessions.updatedAt', '<=', options.updatedBefore) |       .where('id', '=', id) | ||||||
|       .execute(); |       .executeTakeFirst(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @GenerateSql({ params: [DummyValue.STRING] }) |   @GenerateSql({ params: [DummyValue.STRING] }) | ||||||
| @@ -52,7 +52,7 @@ export class SessionRepository { | |||||||
|       ]) |       ]) | ||||||
|       .where('sessions.token', '=', token) |       .where('sessions.token', '=', token) | ||||||
|       .where((eb) => |       .where((eb) => | ||||||
|         eb.or([eb('sessions.expiredAt', 'is', null), eb('sessions.expiredAt', '>', DateTime.now().toJSDate())]), |         eb.or([eb('sessions.expiresAt', 'is', null), eb('sessions.expiresAt', '>', DateTime.now().toJSDate())]), | ||||||
|       ) |       ) | ||||||
|       .executeTakeFirst(); |       .executeTakeFirst(); | ||||||
|   } |   } | ||||||
| @@ -64,6 +64,9 @@ export class SessionRepository { | |||||||
|       .innerJoin('users', (join) => join.onRef('users.id', '=', 'sessions.userId').on('users.deletedAt', 'is', null)) |       .innerJoin('users', (join) => join.onRef('users.id', '=', 'sessions.userId').on('users.deletedAt', 'is', null)) | ||||||
|       .selectAll('sessions') |       .selectAll('sessions') | ||||||
|       .where('sessions.userId', '=', userId) |       .where('sessions.userId', '=', userId) | ||||||
|  |       .where((eb) => | ||||||
|  |         eb.or([eb('sessions.expiresAt', 'is', null), eb('sessions.expiresAt', '>', DateTime.now().toJSDate())]), | ||||||
|  |       ) | ||||||
|       .orderBy('sessions.updatedAt', 'desc') |       .orderBy('sessions.updatedAt', 'desc') | ||||||
|       .orderBy('sessions.createdAt', 'desc') |       .orderBy('sessions.createdAt', 'desc') | ||||||
|       .execute(); |       .execute(); | ||||||
| @@ -86,4 +89,9 @@ export class SessionRepository { | |||||||
|   async delete(id: string) { |   async delete(id: string) { | ||||||
|     await this.db.deleteFrom('sessions').where('id', '=', asUuid(id)).execute(); |     await this.db.deleteFrom('sessions').where('id', '=', asUuid(id)).execute(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   @GenerateSql({ params: [DummyValue.UUID] }) | ||||||
|  |   async lockAll(userId: string) { | ||||||
|  |     await this.db.updateTable('sessions').set({ pinExpiresAt: null }).where('userId', '=', userId).execute(); | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | import { Kysely, sql } from 'kysely'; | ||||||
|  |  | ||||||
|  | export async function up(db: Kysely<any>): Promise<void> { | ||||||
|  |   await sql`ALTER TABLE "sessions" RENAME "expiredAt" TO "expiresAt";`.execute(db); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export async function down(db: Kysely<any>): Promise<void> { | ||||||
|  |   await sql`ALTER TABLE "sessions" RENAME "expiresAt" TO "expiredAt";`.execute(db); | ||||||
|  | } | ||||||
| @@ -26,7 +26,7 @@ export class SessionTable { | |||||||
|   updatedAt!: Date; |   updatedAt!: Date; | ||||||
|  |  | ||||||
|   @Column({ type: 'timestamp with time zone', nullable: true }) |   @Column({ type: 'timestamp with time zone', nullable: true }) | ||||||
|   expiredAt!: Date | null; |   expiresAt!: Date | null; | ||||||
|  |  | ||||||
|   @ForeignKeyColumn(() => UserTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE' }) |   @ForeignKeyColumn(() => UserTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE' }) | ||||||
|   userId!: string; |   userId!: string; | ||||||
|   | |||||||
| @@ -924,13 +924,13 @@ describe(AuthService.name, () => { | |||||||
|       const user = factory.userAdmin(); |       const user = factory.userAdmin(); | ||||||
|       mocks.user.getForPinCode.mockResolvedValue({ pinCode: '123456 (hashed)', password: '' }); |       mocks.user.getForPinCode.mockResolvedValue({ pinCode: '123456 (hashed)', password: '' }); | ||||||
|       mocks.crypto.compareBcrypt.mockImplementation((a, b) => `${a} (hashed)` === b); |       mocks.crypto.compareBcrypt.mockImplementation((a, b) => `${a} (hashed)` === b); | ||||||
|       mocks.session.getByUserId.mockResolvedValue([currentSession]); |       mocks.session.lockAll.mockResolvedValue(void 0); | ||||||
|       mocks.session.update.mockResolvedValue(currentSession); |       mocks.session.update.mockResolvedValue(currentSession); | ||||||
|  |  | ||||||
|       await sut.resetPinCode(factory.auth({ user }), { pinCode: '123456' }); |       await sut.resetPinCode(factory.auth({ user }), { pinCode: '123456' }); | ||||||
|  |  | ||||||
|       expect(mocks.user.update).toHaveBeenCalledWith(user.id, { pinCode: null }); |       expect(mocks.user.update).toHaveBeenCalledWith(user.id, { pinCode: null }); | ||||||
|       expect(mocks.session.update).toHaveBeenCalledWith(currentSession.id, { pinExpiresAt: null }); |       expect(mocks.session.lockAll).toHaveBeenCalledWith(user.id); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should throw if the PIN code does not match', async () => { |     it('should throw if the PIN code does not match', async () => { | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ import { | |||||||
|   PinCodeChangeDto, |   PinCodeChangeDto, | ||||||
|   PinCodeResetDto, |   PinCodeResetDto, | ||||||
|   PinCodeSetupDto, |   PinCodeSetupDto, | ||||||
|  |   SessionUnlockDto, | ||||||
|   SignUpDto, |   SignUpDto, | ||||||
|   mapLoginResponse, |   mapLoginResponse, | ||||||
| } from 'src/dtos/auth.dto'; | } from 'src/dtos/auth.dto'; | ||||||
| @@ -123,24 +124,21 @@ export class AuthService extends BaseService { | |||||||
|  |  | ||||||
|   async resetPinCode(auth: AuthDto, dto: PinCodeResetDto) { |   async resetPinCode(auth: AuthDto, dto: PinCodeResetDto) { | ||||||
|     const user = await this.userRepository.getForPinCode(auth.user.id); |     const user = await this.userRepository.getForPinCode(auth.user.id); | ||||||
|     this.resetPinChecks(user, dto); |     this.validatePinCode(user, dto); | ||||||
|  |  | ||||||
|     await this.userRepository.update(auth.user.id, { pinCode: null }); |     await this.userRepository.update(auth.user.id, { pinCode: null }); | ||||||
|     const sessions = await this.sessionRepository.getByUserId(auth.user.id); |     await this.sessionRepository.lockAll(auth.user.id); | ||||||
|     for (const session of sessions) { |  | ||||||
|       await this.sessionRepository.update(session.id, { pinExpiresAt: null }); |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   async changePinCode(auth: AuthDto, dto: PinCodeChangeDto) { |   async changePinCode(auth: AuthDto, dto: PinCodeChangeDto) { | ||||||
|     const user = await this.userRepository.getForPinCode(auth.user.id); |     const user = await this.userRepository.getForPinCode(auth.user.id); | ||||||
|     this.resetPinChecks(user, dto); |     this.validatePinCode(user, dto); | ||||||
|  |  | ||||||
|     const hashed = await this.cryptoRepository.hashBcrypt(dto.newPinCode, SALT_ROUNDS); |     const hashed = await this.cryptoRepository.hashBcrypt(dto.newPinCode, SALT_ROUNDS); | ||||||
|     await this.userRepository.update(auth.user.id, { pinCode: hashed }); |     await this.userRepository.update(auth.user.id, { pinCode: hashed }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private resetPinChecks( |   private validatePinCode( | ||||||
|     user: { pinCode: string | null; password: string | null }, |     user: { pinCode: string | null; password: string | null }, | ||||||
|     dto: { pinCode?: string; password?: string }, |     dto: { pinCode?: string; password?: string }, | ||||||
|   ) { |   ) { | ||||||
| @@ -474,23 +472,27 @@ export class AuthService extends BaseService { | |||||||
|     throw new UnauthorizedException('Invalid user token'); |     throw new UnauthorizedException('Invalid user token'); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   async verifyPinCode(auth: AuthDto, dto: PinCodeSetupDto): Promise<void> { |   async unlockSession(auth: AuthDto, dto: SessionUnlockDto): Promise<void> { | ||||||
|     const user = await this.userRepository.getForPinCode(auth.user.id); |  | ||||||
|     if (!user) { |  | ||||||
|       throw new UnauthorizedException(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     this.resetPinChecks(user, { pinCode: dto.pinCode }); |  | ||||||
|  |  | ||||||
|     if (!auth.session) { |     if (!auth.session) { | ||||||
|       throw new BadRequestException('Session is missing'); |       throw new BadRequestException('This endpoint can only be used with a session token'); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     const user = await this.userRepository.getForPinCode(auth.user.id); | ||||||
|  |     this.validatePinCode(user, { pinCode: dto.pinCode }); | ||||||
|  |  | ||||||
|     await this.sessionRepository.update(auth.session.id, { |     await this.sessionRepository.update(auth.session.id, { | ||||||
|       pinExpiresAt: new Date(DateTime.now().plus({ minutes: 15 }).toJSDate()), |       pinExpiresAt: DateTime.now().plus({ minutes: 15 }).toJSDate(), | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   async lockSession(auth: AuthDto): Promise<void> { | ||||||
|  |     if (!auth.session) { | ||||||
|  |       throw new BadRequestException('This endpoint can only be used with a session token'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     await this.sessionRepository.update(auth.session.id, { pinExpiresAt: null }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   private async createLoginResponse(user: UserAdmin, loginDetails: LoginDetails) { |   private async createLoginResponse(user: UserAdmin, loginDetails: LoginDetails) { | ||||||
|     const token = this.cryptoRepository.randomBytesAsText(32); |     const token = this.cryptoRepository.randomBytesAsText(32); | ||||||
|     const tokenHashed = this.cryptoRepository.hashSha256(token); |     const tokenHashed = this.cryptoRepository.hashSha256(token); | ||||||
| @@ -526,10 +528,14 @@ export class AuthService extends BaseService { | |||||||
|       throw new UnauthorizedException(); |       throw new UnauthorizedException(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     const session = auth.session ? await this.sessionRepository.get(auth.session.id) : undefined; | ||||||
|  |  | ||||||
|     return { |     return { | ||||||
|       pinCode: !!user.pinCode, |       pinCode: !!user.pinCode, | ||||||
|       password: !!user.password, |       password: !!user.password, | ||||||
|       isElevated: !!auth.session?.hasElevatedPermission, |       isElevated: !!auth.session?.hasElevatedPermission, | ||||||
|  |       expiresAt: session?.expiresAt?.toISOString(), | ||||||
|  |       pinExpiresAt: session?.pinExpiresAt?.toISOString(), | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ export class SessionService extends BaseService { | |||||||
|     const session = await this.sessionRepository.create({ |     const session = await this.sessionRepository.create({ | ||||||
|       parentId: auth.session.id, |       parentId: auth.session.id, | ||||||
|       userId: auth.user.id, |       userId: auth.user.id, | ||||||
|       expiredAt: dto.duration ? DateTime.now().plus({ seconds: dto.duration }).toJSDate() : null, |       expiresAt: dto.duration ? DateTime.now().plus({ seconds: dto.duration }).toJSDate() : null, | ||||||
|       deviceType: dto.deviceType, |       deviceType: dto.deviceType, | ||||||
|       deviceOS: dto.deviceOS, |       deviceOS: dto.deviceOS, | ||||||
|       token: tokenHashed, |       token: tokenHashed, | ||||||
| @@ -49,6 +49,11 @@ export class SessionService extends BaseService { | |||||||
|     await this.sessionRepository.delete(id); |     await this.sessionRepository.delete(id); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   async lock(auth: AuthDto, id: string): Promise<void> { | ||||||
|  |     await this.requireAccess({ auth, permission: Permission.SESSION_LOCK, ids: [id] }); | ||||||
|  |     await this.sessionRepository.update(id, { pinExpiresAt: null }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   async deleteAll(auth: AuthDto): Promise<void> { |   async deleteAll(auth: AuthDto): Promise<void> { | ||||||
|     const sessions = await this.sessionRepository.getByUserId(auth.user.id); |     const sessions = await this.sessionRepository.getByUserId(auth.user.id); | ||||||
|     for (const session of sessions) { |     for (const session of sessions) { | ||||||
|   | |||||||
| @@ -280,6 +280,13 @@ const checkOtherAccess = async (access: AccessRepository, request: OtherAccessRe | |||||||
|       return await access.partner.checkUpdateAccess(auth.user.id, ids); |       return await access.partner.checkUpdateAccess(auth.user.id, ids); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     case Permission.SESSION_READ: | ||||||
|  |     case Permission.SESSION_UPDATE: | ||||||
|  |     case Permission.SESSION_DELETE: | ||||||
|  |     case Permission.SESSION_LOCK: { | ||||||
|  |       return access.session.checkOwnerAccess(auth.user.id, ids); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     case Permission.STACK_READ: { |     case Permission.STACK_READ: { | ||||||
|       return access.stack.checkOwnerAccess(auth.user.id, ids); |       return access.stack.checkOwnerAccess(auth.user.id, ids); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -50,6 +50,10 @@ export const newAccessRepositoryMock = (): IAccessRepositoryMock => { | |||||||
|       checkUpdateAccess: vitest.fn().mockResolvedValue(new Set()), |       checkUpdateAccess: vitest.fn().mockResolvedValue(new Set()), | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|  |     session: { | ||||||
|  |       checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), | ||||||
|  |     }, | ||||||
|  |  | ||||||
|     stack: { |     stack: { | ||||||
|       checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), |       checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), | ||||||
|     }, |     }, | ||||||
|   | |||||||
| @@ -127,7 +127,7 @@ const sessionFactory = (session: Partial<Session> = {}) => ({ | |||||||
|   deviceType: 'mobile', |   deviceType: 'mobile', | ||||||
|   token: 'abc123', |   token: 'abc123', | ||||||
|   parentId: null, |   parentId: null, | ||||||
|   expiredAt: null, |   expiresAt: null, | ||||||
|   userId: newUuid(), |   userId: newUuid(), | ||||||
|   pinExpiresAt: newDate(), |   pinExpiresAt: newDate(), | ||||||
|   ...session, |   ...session, | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|  |   import { goto } from '$app/navigation'; | ||||||
|   import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte'; |   import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte'; | ||||||
|   import ChangeDate from '$lib/components/photos-page/actions/change-date-action.svelte'; |   import ChangeDate from '$lib/components/photos-page/actions/change-date-action.svelte'; | ||||||
|   import ChangeLocation from '$lib/components/photos-page/actions/change-location-action.svelte'; |   import ChangeLocation from '$lib/components/photos-page/actions/change-location-action.svelte'; | ||||||
| @@ -10,11 +11,12 @@ | |||||||
|   import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte'; |   import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte'; | ||||||
|   import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte'; |   import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte'; | ||||||
|   import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte'; |   import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte'; | ||||||
|   import { AssetAction } from '$lib/constants'; |   import { AppRoute, AssetAction } from '$lib/constants'; | ||||||
|   import { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; |   import { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; | ||||||
|   import { AssetStore } from '$lib/stores/assets-store.svelte'; |   import { AssetStore } from '$lib/stores/assets-store.svelte'; | ||||||
|   import { AssetVisibility } from '@immich/sdk'; |   import { AssetVisibility, lockAuthSession } from '@immich/sdk'; | ||||||
|   import { mdiDotsVertical } from '@mdi/js'; |   import { Button } from '@immich/ui'; | ||||||
|  |   import { mdiDotsVertical, mdiLockOutline } from '@mdi/js'; | ||||||
|   import { onDestroy } from 'svelte'; |   import { onDestroy } from 'svelte'; | ||||||
|   import { t } from 'svelte-i18n'; |   import { t } from 'svelte-i18n'; | ||||||
|   import type { PageData } from './$types'; |   import type { PageData } from './$types'; | ||||||
| @@ -42,6 +44,11 @@ | |||||||
|     assetInteraction.clearMultiselect(); |     assetInteraction.clearMultiselect(); | ||||||
|     assetStore.removeAssets(assetIds); |     assetStore.removeAssets(assetIds); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|  |   const handleLock = async () => { | ||||||
|  |     await lockAuthSession(); | ||||||
|  |     await goto(AppRoute.PHOTOS); | ||||||
|  |   }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <!-- Multi-selection mode app bar --> | <!-- Multi-selection mode app bar --> | ||||||
| @@ -62,6 +69,12 @@ | |||||||
| {/if} | {/if} | ||||||
|  |  | ||||||
| <UserPageLayout hideNavbar={assetInteraction.selectionActive} title={data.meta.title} scrollbar={false}> | <UserPageLayout hideNavbar={assetInteraction.selectionActive} title={data.meta.title} scrollbar={false}> | ||||||
|  |   {#snippet buttons()} | ||||||
|  |     <Button size="small" variant="filled" color="warning" leadingIcon={mdiLockOutline} onclick={handleLock}> | ||||||
|  |       {$t('lock')} | ||||||
|  |     </Button> | ||||||
|  |   {/snippet} | ||||||
|  |  | ||||||
|   <AssetGrid |   <AssetGrid | ||||||
|     enableRouting={true} |     enableRouting={true} | ||||||
|     {assetStore} |     {assetStore} | ||||||
|   | |||||||
| @@ -8,14 +8,12 @@ import type { PageLoad } from './$types'; | |||||||
|  |  | ||||||
| export const load = (async ({ params, url }) => { | export const load = (async ({ params, url }) => { | ||||||
|   await authenticate(url); |   await authenticate(url); | ||||||
|  |  | ||||||
|   const { isElevated, pinCode } = await getAuthStatus(); |   const { isElevated, pinCode } = await getAuthStatus(); | ||||||
|  |  | ||||||
|   if (!isElevated || !pinCode) { |   if (!isElevated || !pinCode) { | ||||||
|     const continuePath = encodeURIComponent(url.pathname); |     redirect(302, `${AppRoute.AUTH_PIN_PROMPT}?continue=${encodeURIComponent(url.pathname + url.search)}`); | ||||||
|     const redirectPath = `${AppRoute.AUTH_PIN_PROMPT}?continue=${continuePath}`; |  | ||||||
|  |  | ||||||
|     redirect(302, redirectPath); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const asset = await getAssetInfoFromParam(params); |   const asset = await getAssetInfoFromParam(params); | ||||||
|   const $t = await getFormatter(); |   const $t = await getFormatter(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,9 +3,8 @@ | |||||||
|   import AuthPageLayout from '$lib/components/layouts/AuthPageLayout.svelte'; |   import AuthPageLayout from '$lib/components/layouts/AuthPageLayout.svelte'; | ||||||
|   import PinCodeCreateForm from '$lib/components/user-settings-page/PinCodeCreateForm.svelte'; |   import PinCodeCreateForm from '$lib/components/user-settings-page/PinCodeCreateForm.svelte'; | ||||||
|   import PincodeInput from '$lib/components/user-settings-page/PinCodeInput.svelte'; |   import PincodeInput from '$lib/components/user-settings-page/PinCodeInput.svelte'; | ||||||
|   import { AppRoute } from '$lib/constants'; |  | ||||||
|   import { handleError } from '$lib/utils/handle-error'; |   import { handleError } from '$lib/utils/handle-error'; | ||||||
|   import { verifyPinCode } from '@immich/sdk'; |   import { unlockAuthSession } from '@immich/sdk'; | ||||||
|   import { Icon } from '@immich/ui'; |   import { Icon } from '@immich/ui'; | ||||||
|   import { mdiLockOpenVariantOutline, mdiLockOutline, mdiLockSmart } from '@mdi/js'; |   import { mdiLockOpenVariantOutline, mdiLockOutline, mdiLockSmart } from '@mdi/js'; | ||||||
|   import { t } from 'svelte-i18n'; |   import { t } from 'svelte-i18n'; | ||||||
| @@ -23,17 +22,15 @@ | |||||||
|   let hasPinCode = $derived(data.hasPinCode); |   let hasPinCode = $derived(data.hasPinCode); | ||||||
|   let pinCode = $state(''); |   let pinCode = $state(''); | ||||||
|  |  | ||||||
|   const onPinFilled = async (code: string, withDelay = false) => { |   const handleUnlockSession = async (code: string) => { | ||||||
|     try { |     try { | ||||||
|       await verifyPinCode({ pinCodeSetupDto: { pinCode: code } }); |       await unlockAuthSession({ sessionUnlockDto: { pinCode: code } }); | ||||||
|  |  | ||||||
|       isVerified = true; |       isVerified = true; | ||||||
|  |  | ||||||
|       if (withDelay) { |  | ||||||
|       await new Promise((resolve) => setTimeout(resolve, 1000)); |       await new Promise((resolve) => setTimeout(resolve, 1000)); | ||||||
|       } |  | ||||||
|  |  | ||||||
|       void goto(data.continuePath ?? AppRoute.LOCKED); |       await goto(data.continueUrl); | ||||||
|     } catch (error) { |     } catch (error) { | ||||||
|       handleError(error, $t('wrong_pin_code')); |       handleError(error, $t('wrong_pin_code')); | ||||||
|       isBadPinCode = true; |       isBadPinCode = true; | ||||||
| @@ -64,7 +61,7 @@ | |||||||
|           bind:value={pinCode} |           bind:value={pinCode} | ||||||
|           tabindexStart={1} |           tabindexStart={1} | ||||||
|           pinLength={6} |           pinLength={6} | ||||||
|           onFilled={(pinCode) => onPinFilled(pinCode, true)} |           onFilled={handleUnlockSession} | ||||||
|         /> |         /> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | import { AppRoute } from '$lib/constants'; | ||||||
| import { authenticate } from '$lib/utils/auth'; | import { authenticate } from '$lib/utils/auth'; | ||||||
| import { getFormatter } from '$lib/utils/i18n'; | import { getFormatter } from '$lib/utils/i18n'; | ||||||
| import { getAuthStatus } from '@immich/sdk'; | import { getAuthStatus } from '@immich/sdk'; | ||||||
| @@ -8,8 +9,6 @@ export const load = (async ({ url }) => { | |||||||
|  |  | ||||||
|   const { pinCode } = await getAuthStatus(); |   const { pinCode } = await getAuthStatus(); | ||||||
|  |  | ||||||
|   const continuePath = url.searchParams.get('continue'); |  | ||||||
|  |  | ||||||
|   const $t = await getFormatter(); |   const $t = await getFormatter(); | ||||||
|  |  | ||||||
|   return { |   return { | ||||||
| @@ -17,6 +16,6 @@ export const load = (async ({ url }) => { | |||||||
|       title: $t('pin_verification'), |       title: $t('pin_verification'), | ||||||
|     }, |     }, | ||||||
|     hasPinCode: !!pinCode, |     hasPinCode: !!pinCode, | ||||||
|     continuePath, |     continueUrl: url.searchParams.get('continue') || AppRoute.LOCKED, | ||||||
|   }; |   }; | ||||||
| }) satisfies PageLoad; | }) satisfies PageLoad; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user