-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: auth #6
feat: auth #6
Conversation
WalkthroughThe changes introduce several enhancements to the project, including the addition of new dependencies for testing and security, the introduction of new classes for authorization and error handling, and a reorganization of existing classes into a more structured package hierarchy. A README file for testing practices has been added, along with various test classes to validate the functionality of newly introduced components such as the Changes
Sequence Diagram(s)sequenceDiagram
participant C as Client
participant I as AuthorizationInterceptor
participant S as SpaceService
participant R as Response
participant A as AuthorizationService
C->>I: Sends request to /space
I->>I: preHandle() method called
I->>A: Checks authorization
A->>S: Validates request
S->>R: Returns response
I->>C: Returns response
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Outside diff range, codebase verification and nitpick comments (2)
src/test/README.md (2)
4-4
: Remove trailing punctuation in heading.The heading should not have trailing punctuation.
Apply this diff to fix the heading:
-## Do not use `Mockito`. Use `MockK` instead. +## Do not use `Mockito`. Use `MockK` insteadTools
Markdownlint
4-4: Punctuation: '.'
Trailing punctuation in heading(MD026, no-trailing-punctuation)
6-6
: Convert bare URL to Markdown link.Bare URLs should be converted to Markdown links for better readability.
Apply this diff to fix the URL:
-See https://github.com/Ninja-Squad/springmockk for more information. +See [springmockk](https://github.com/Ninja-Squad/springmockk) for more information.Tools
Markdownlint
6-6: null
Bare URL used(MD034, no-bare-urls)
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (13)
- pom.xml (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/AuthorizationInterceptor.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/common/configuration/WebMvcConfig.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/common/error/BadRequestError.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/common/error/BaseError.kt (2 hunks)
- src/main/kotlin/org/rucca/cheese/common/error/GlobalErrorHandler.kt (2 hunks)
- src/main/kotlin/org/rucca/cheese/common/error/InternalServerError.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/common/error/NotFoundError.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/common/persistent/BaseEntity.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/space/SpaceEntity.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/space/SpaceService.kt (1 hunks)
- src/test/README.md (1 hunks)
- src/test/kotlin/org/rucca/cheese/common/ConfigTest.kt (1 hunks)
Files skipped from review due to trivial changes (7)
- src/main/kotlin/org/rucca/cheese/common/error/BadRequestError.kt
- src/main/kotlin/org/rucca/cheese/common/error/BaseError.kt
- src/main/kotlin/org/rucca/cheese/common/error/GlobalErrorHandler.kt
- src/main/kotlin/org/rucca/cheese/common/error/InternalServerError.kt
- src/main/kotlin/org/rucca/cheese/common/persistent/BaseEntity.kt
- src/main/kotlin/org/rucca/cheese/space/SpaceEntity.kt
- src/main/kotlin/org/rucca/cheese/space/SpaceService.kt
Additional context used
Markdownlint
src/test/README.md
4-4: Punctuation: '.'
Trailing punctuation in heading(MD026, no-trailing-punctuation)
6-6: null
Bare URL used(MD034, no-bare-urls)
Additional comments not posted (18)
src/test/README.md (3)
1-1
: Title is clear and concise.The title effectively communicates the content of the README file.
2-2
: Introduction is clear.The introductory sentence provides useful context for the reader.
5-5
: Explanation is clear and informative.The reason for using
MockK
overMockito
is well-explained.src/main/kotlin/org/rucca/cheese/auth/AuthorizationInterceptor.kt (5)
1-1
: Package declaration is correct.The package declaration follows the standard convention.
3-6
: Import statements are correct.The import statements are necessary for the functionality of the class.
8-8
:@Component
annotation is correctly used.The
@Component
annotation indicates that the class is a Spring component.
9-9
: Class declaration is correct.The class declaration follows standard conventions.
10-12
:preHandle
method is correctly overridden.The
preHandle
method is correctly overridden and currently allows all requests to proceed.src/main/kotlin/org/rucca/cheese/common/error/NotFoundError.kt (3)
1-1
: Package declaration is correct.The package declaration reflects the new package structure.
3-4
: Import statements are correct.The import statements are necessary for the functionality of the class.
6-11
: Class declaration and constructor are correct.The class declaration and constructor follow standard conventions. The use of
IdType
without the fully qualified name improves readability.src/main/kotlin/org/rucca/cheese/common/configuration/WebMvcConfig.kt (3)
1-8
: LGTM! Package declaration and imports are appropriate.The package declaration and imports are necessary for the functionality provided in the file.
10-11
: LGTM! Class declaration and constructor are appropriate.The class declaration and constructor follow best practices.
12-14
: LGTM! Method implementation is appropriate.The
addInterceptors
method correctly adds theAuthorizationInterceptor
.src/test/kotlin/org/rucca/cheese/common/ConfigTest.kt (3)
1-12
: LGTM! Package declaration and imports are appropriate.The package declaration and imports are necessary for the functionality provided in the file.
13-17
: LGTM! Class declaration and constructor are appropriate.The class declaration and constructor follow best practices.
19-23
: LGTM! Method implementation is appropriate.The
testAddAuthorizationInterceptor
method correctly tests theAuthorizationInterceptor
.pom.xml (1)
116-121
: LGTM! Dependency addition is appropriate.The added dependency for
springmockk
is necessary for testing and correctly added.
594b56c
to
52b6153
Compare
…onService.audit() before each controller method is called
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
Outside diff range and nitpick comments (7)
src/main/kotlin/org/rucca/cheese/auth/exception/NoGuardOrNoAuthAnnotationException.kt (1)
1-9
: LGTM with a minor suggestion!The custom exception class is well-defined and follows the best practices for exception handling in Kotlin. The exception message is clear and informative, including relevant details like the controller and method names.
Consider using string templates for constructing the exception message, which is more idiomatic and efficient in Kotlin:
- RuntimeException( - "Any method in a controller must have a @Guard or @NoAuth annotation. " + - "However, method $methodName in controller $controllerName does not have any.") + RuntimeException( + "Any method in a controller must have a @Guard or @NoAuth annotation. " + + "However, method $methodName in controller $controllerName does not have any.")src/main/resources/application.properties (1)
12-12
: Consider using environment variables or profiles for the legacy URL.The addition of the
application.legacy-url
property is a good practice for externalizing configuration. However, hardcoding the URL ashttp://localhost:3000
may not be suitable for all environments.Consider using environment variables or Spring profiles to manage the legacy URL for different environments (e.g., local, staging, production). This allows for easier configuration and deployment across various environments without modifying the codebase.
For example, you can define the legacy URL as an environment variable:
application.legacy-url=${LEGACY_URL:http://localhost:3000}
This way, the value can be overridden by setting the
LEGACY_URL
environment variable, and it defaults tohttp://localhost:3000
if the environment variable is not set.README.md (1)
11-14
: Streamlined setup process looks good! Consider rephrasing the Windows-specific instructions.The changes to the setup process, which now involve running the
doc/script/dependency-start.sh
anddoc/script/dependency-restart.sh
scripts, greatly simplify the process for users. The instructions are clear and concise, making it easier to set up the necessary services.To add variety to the writing and address the static analysis hint, consider rephrasing the sentence mentioning "You may need to remove
sudo
..." for Windows users. For example:-You may need to remove ```sudo``` and run the commands one by one manually if you want to do it on Windows in Docker Desktop +For Windows users running Docker Desktop, it might be necessary to omit ```sudo``` and execute the commands individually.Tools
LanguageTool
[style] ~11-~11: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...esult in all sorts of errors. You also need to set up several services. We recommend u...(REP_NEED_TO_VB)
[style] ~13-~13: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...Desktop to start the services. (You may need to removesudo
and run the commands ...(REP_NEED_TO_VB)
design/DB/CREATE.sql (2)
69-71
: The foreign key constraint is good, but consider using a more meaningful name.Adding the foreign key constraint to link the
user_profile
table with theavatar
table is crucial for maintaining referential integrity. However, the constraint nameFKo5dcemd97atrmjapi9x4s1j32
seems to be auto-generated and may not be very meaningful.Consider using a more descriptive and human-readable name for the constraint, such as
fk_user_profile_avatar
. This will make the schema more self-explanatory and easier to understand for other developers.ALTER TABLE IF EXISTS public.user_profile ADD CONSTRAINT fk_user_profile_avatar FOREIGN KEY (avatar_id) REFERENCES avatar;
73-74
: The foreign key constraint is good, but consider using a more meaningful name.Adding the foreign key constraint to link the
user_profile
table with theuser
table is crucial for maintaining referential integrity. However, the constraint nameFKqcd5nmg7d7ement27tt9sf3bi
seems to be auto-generated and may not be very meaningful.Consider using a more descriptive and human-readable name for the constraint, such as
fk_user_profile_user
. This will make the schema more self-explanatory and easier to understand for other developers.ALTER TABLE IF EXISTS public.user_profile ADD CONSTRAINT fk_user_profile_user FOREIGN KEY (user_id) REFERENCES public."user";src/test/kotlin/org/rucca/cheese/auth/UserCreatorService.kt (2)
30-50
: LGTM! Consider adding validation for the input parameters.The
createUser
method is correctly implemented and follows the best practices for password hashing and data persistence. However, to ensure data integrity, consider adding validation for the input parameters, such as:
- Checking for empty or null values.
- Validating the email format.
- Ensuring the username and nickname meet the required length and format.
- Verifying that the username and email are unique.
52-70
: LGTM! Consider adding more specific exception handling and logging.The
login
method is correctly implemented and follows the best practices for making HTTP requests and handling responses. However, consider the following improvements:
Add more specific exception handling and logging to help with debugging and troubleshooting. For example, you can catch specific exceptions like
IOException
orJSONException
and log the error message along with the relevant request and response details.Address the static analysis hint by logging the original exception before throwing the
RuntimeException
. This will help preserve the original exception information for better error tracking.Here's an example of how you can implement the suggested improvements:
try { return result.getJSONObject("data").getString("accessToken") } catch (e: Exception) { logger.error("Failed to extract JWT token from response: $result", e) throw RuntimeException("Failed to login: $result", e) }Tools
detekt
[warning] 67-67: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (40)
- .github/workflows/build.yml (1 hunks)
- README.md (1 hunks)
- design/DB/CREATE.sql (3 hunks)
- doc/script/dependency-restart.sh (1 hunks)
- doc/script/dependency-start.sh (1 hunks)
- pom.xml (1 hunks)
- src/main/kotlin/org/rucca/cheese/BackendApplication.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/AuthorizationAspect.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/AuthorizationService.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/CustomAuthLogics.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/Definitions.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/OwnerIds.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/annotation/Guard.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/annotation/NoAuth.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/annotation/ResourceId.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/error/AuthenticationRequiredError.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/error/InvalidTokenError.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/error/PermissionDeniedError.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/error/TokenExpiredError.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/exception/DuplicatedResourceIdAnnotationException.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/exception/NoGuardOrNoAuthAnnotationException.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/exception/ResourceIdTypeMismatchException.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/exception/UnsupportedRoleException.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/common/config/ApplicationConfig.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/common/persistent/BaseEntity.kt (2 hunks)
- src/main/kotlin/org/rucca/cheese/space/SpaceController.kt (2 hunks)
- src/main/kotlin/org/rucca/cheese/space/SpaceService.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/user/RoleBasedAuthLogicService.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/user/RolePermissionService.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/user/UserEntity.kt (3 hunks)
- src/main/kotlin/org/rucca/cheese/user/UserProfileEntity.kt (2 hunks)
- src/main/resources/application.properties (1 hunks)
- src/test/README.md (1 hunks)
- src/test/kotlin/org/rucca/cheese/api/SpaceTest.kt (2 hunks)
- src/test/kotlin/org/rucca/cheese/auth/AuthorizationAspectTest.kt (1 hunks)
- src/test/kotlin/org/rucca/cheese/auth/AuthorizationServiceTest.kt (1 hunks)
- src/test/kotlin/org/rucca/cheese/auth/ExampleController.kt (1 hunks)
- src/test/kotlin/org/rucca/cheese/auth/ExampleRestController.kt (1 hunks)
- src/test/kotlin/org/rucca/cheese/auth/UserCreatorService.kt (1 hunks)
- src/test/kotlin/org/rucca/cheese/auth/UserCreatorServiceTest.kt (1 hunks)
Files skipped from review due to trivial changes (1)
- src/main/kotlin/org/rucca/cheese/space/SpaceService.kt
Additional context used
detekt
src/main/kotlin/org/rucca/cheese/auth/annotation/NoAuth.kt
[warning] 3-3: An empty default constructor can be removed.
(detekt.empty-blocks.EmptyDefaultConstructor)
src/main/kotlin/org/rucca/cheese/auth/annotation/ResourceId.kt
[warning] 3-3: An empty default constructor can be removed.
(detekt.empty-blocks.EmptyDefaultConstructor)
src/main/kotlin/org/rucca/cheese/user/RoleBasedAuthLogicService.kt
[warning] 58-58: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
src/test/kotlin/org/rucca/cheese/auth/UserCreatorService.kt
[warning] 67-67: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
src/main/kotlin/org/rucca/cheese/auth/AuthorizationService.kt
[warning] 93-93: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 95-95: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
Markdownlint
src/test/README.md
4-4: Punctuation: '.'
Trailing punctuation in heading(MD026, no-trailing-punctuation)
6-6: null
Bare URL used(MD034, no-bare-urls)
LanguageTool
README.md
[style] ~11-~11: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...esult in all sorts of errors. You also need to set up several services. We recommend u...(REP_NEED_TO_VB)
[style] ~13-~13: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...Desktop to start the services. (You may need to removesudo
and run the commands ...(REP_NEED_TO_VB)
Additional comments not posted (68)
src/main/kotlin/org/rucca/cheese/auth/annotation/NoAuth.kt (1)
1-3
: LGTM!The
NoAuth
annotation class is correctly defined and follows the Kotlin syntax. The@Target
and@MustBeDocumented
annotations are used appropriately.Regarding the static analysis warning about the empty default constructor, it can be safely ignored as annotation classes in Kotlin do not require a constructor.
Tools
detekt
[warning] 3-3: An empty default constructor can be removed.
(detekt.empty-blocks.EmptyDefaultConstructor)
src/main/kotlin/org/rucca/cheese/auth/annotation/ResourceId.kt (1)
1-3
: LGTM!The
ResourceId
annotation is correctly defined and targeted for value parameters. The@MustBeDocumented
annotation ensures that the annotation is included in the Javadoc.The warning about the empty default constructor can be safely ignored because annotations in Kotlin do not require an explicit constructor.
Tools
detekt
[warning] 3-3: An empty default constructor can be removed.
(detekt.empty-blocks.EmptyDefaultConstructor)
src/main/kotlin/org/rucca/cheese/auth/exception/UnsupportedRoleException.kt (1)
1-5
: LGTM!The custom exception class
UnsupportedRoleException
is well-defined and follows best practices:
- The class name clearly conveys the purpose of the exception.
- Extending
RuntimeException
is appropriate for an unchecked exception.- The constructor includes the unsupported role in the exception message using string interpolation.
- The exception class is defined in a separate file and placed in an appropriate package hierarchy.
Great job!
src/main/kotlin/org/rucca/cheese/auth/annotation/Guard.kt (1)
1-8
: LGTM!The
Guard
annotation class is well-designed and follows best practices:
- The class name and property names are clear and descriptive.
- The annotation targets functions, which is appropriate for a guard annotation.
- The
@MustBeDocumented
ensures that the annotation is included in the generated documentation.- The properties
action
andresourceType
provide flexibility in defining different actions and resource types.The annotation can be easily applied to functions that require authorization checks, promoting a clean and maintainable codebase.
src/main/kotlin/org/rucca/cheese/auth/error/InvalidTokenError.kt (1)
1-6
: LGTM!The
InvalidTokenError
class is correctly defined and follows the Kotlin syntax. The class extendsBaseError
, which is likely a custom error class defined in theorg.rucca.cheese.common.error
package. The class is defined in a package that follows a structured hierarchy, which is a good practice for organizing code. The class name and the error message are descriptive and convey the purpose of the error. TheHttpStatus.UNAUTHORIZED
is an appropriate status code for an invalid token error.src/main/kotlin/org/rucca/cheese/auth/error/TokenExpiredError.kt (1)
1-6
: LGTM!The
TokenExpiredError
class is well-defined and follows the project's error handling pattern. The package structure is well-organized, and the error class is appropriately named and scoped to the authentication module. The HTTP status code and error message are suitable for the token expiration scenario.src/test/README.md (1)
1-6
: LGTM!The README file provides clear and useful testing guidelines. The recommendation to use
MockK
overMockito
for better Kotlin support is valid and will help improve the quality and maintainability of the test suite.Tools
Markdownlint
4-4: Punctuation: '.'
Trailing punctuation in heading(MD026, no-trailing-punctuation)
6-6: null
Bare URL used(MD034, no-bare-urls)
src/main/kotlin/org/rucca/cheese/auth/exception/ResourceIdTypeMismatchException.kt (1)
1-9
: LGTM!The custom exception class
ResourceIdTypeMismatchException
is well-defined and follows the best practices for exception handling in Kotlin. The exception message is informative and includes relevant details about the context where the exception occurred. The exception class is defined in an appropriate package structure and extendsRuntimeException
, which is suitable for unchecked exceptions.src/main/kotlin/org/rucca/cheese/common/config/ApplicationConfig.kt (2)
1-11
: LGTM!The
ApplicationConfig
class is correctly implemented as a Spring component with configuration properties binding. The use oflateinit
for the properties is appropriate since they will be initialized by the Spring container.
9-10
: Verify the configuration properties.Ensure that the
application.legacyUrl
andapplication.jwtSecret
properties are correctly defined in the application configuration file (e.g.,application.properties
orapplication.yml
).Run the following script to verify the configuration properties:
src/main/kotlin/org/rucca/cheese/auth/error/AuthenticationRequiredError.kt (1)
1-10
: LGTM!The
AuthenticationRequiredError
class is well-structured and follows best practices. It extends theBaseError
class and provides a clear and concise error message for authentication required scenarios. The HTTP status code and error code are appropriate for this type of error.The class encapsulates the necessary information and can be easily integrated with the existing error handling mechanism.
src/main/kotlin/org/rucca/cheese/auth/exception/DuplicatedResourceIdAnnotationException.kt (1)
1-9
: LGTM!The custom exception class
DuplicatedResourceIdAnnotationException
is well-defined and serves a specific purpose. The exception message is informative and includes relevant details such as the controller and method names for easier debugging. The exception class extendsRuntimeException
, which is appropriate for an unchecked exception. The package structure also follows the standard convention for exception classes.src/main/kotlin/org/rucca/cheese/BackendApplication.kt (1)
3-3
: LGTM!The changes to enable configuration properties in the
BackendApplication
class are implemented correctly. The@EnableConfigurationProperties
annotation is used appropriately with theApplicationConfig
class as the argument. This will allow the application to bind external configuration properties to theApplicationConfig
class, making it more flexible and maintainable.The import statement for the
ApplicationConfig
class is also added correctly.Great job!
Also applies to: 5-5, 8-8
src/test/kotlin/org/rucca/cheese/auth/ExampleController.kt (2)
10-11
: LGTM!The class is correctly defined as a Spring MVC controller using the
@Controller
annotation.
12-18
: Provide more context for@Guard
and@ResourceId
, and replace the hardcoded response.A few observations:
The usage of
@GetMapping
and@RequestParam
annotations looks good for defining the endpoint and extracting theid
parameter.To better understand the usage of the
@Guard
and@ResourceId
annotations, could you please provide more context about what these annotations do and how they are processed? This will help ensure they are being used correctly in this context.The method currently returns a hardcoded string. For a real implementation, this should be replaced with the actual business logic.
- fun withId( - @RequestParam("id") @ResourceId id: IdType, - ): String { - return "example_1" - } + fun withId( + @RequestParam("id") @ResourceId id: IdType, + ): String { + // TODO: Replace with actual implementation + return "example_1" + }Verification successful
Annotations
@Guard
and@ResourceId
are used correctly; replace hardcoded response.The
@Guard
annotation is used for authorization by specifying an action and resource type, and the@ResourceId
annotation marks parameters as resource identifiers. Both are used appropriately in thewithId
method. Consider replacing the hardcoded response with actual business logic for a real implementation.
Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the usage of `@Guard` and `@ResourceId` annotations. # Test: Search for the annotation definitions. Expect: Definitions that explain their purpose. rg --type kotlin -A 10 $'@Target|@Retention' | rg $'Guard|ResourceId'Length of output: 711
src/main/kotlin/org/rucca/cheese/auth/error/PermissionDeniedError.kt (1)
8-16
: LGTM!The
PermissionDeniedError
class is well-defined and follows a clear structure. The constructor parameters provide relevant information about the permission denied error, and the error message is informative, including the specific action, resource type, and resource ID. The use ofHttpStatus.FORBIDDEN
status code is appropriate for permission denied errors.src/test/kotlin/org/rucca/cheese/auth/UserCreatorServiceTest.kt (1)
8-14
: LGTM!The
UserCreatorServiceTest
class is correctly set up as a Spring Boot integration test with the@SpringBootTest
annotation. TheUserCreatorService
dependency is correctly autowired in the constructor, and the logger is initialized correctly.src/main/kotlin/org/rucca/cheese/auth/Definitions.kt (1)
1-30
: LGTM!The code in this file is well-structured, follows best practices, and provides a clear definition of data classes related to authorization. The use of nullable types allows flexibility in defining the properties of the data classes. The package declaration and imports are correct and follow the standard conventions.
Great job!
.github/workflows/build.yml (1)
28-29
: Ensure thedependency-start.sh
script correctly sets up the required services.I noticed that the PostgreSQL and Elasticsearch service definitions have been removed in favor of running a custom
dependency-start.sh
script. This change could impact the build environment and the reliability of tests that depend on these services.To ensure a smooth transition, please provide more information about the
dependency-start.sh
script:
- What services does it set up?
- How does it handle the initialization and configuration of these services?
- Are there any specific requirements or dependencies for running this script?
Additionally, I recommend thoroughly verifying the test results with this new setup to ensure that all tests pass and the build remains stable.
To verify the test results, you can run the following script:
Please let me know if you have any further questions or if there's anything else I can assist with regarding this change.
Verification successful
The
dependency-start.sh
script appears to correctly set up the required services.The script sets up Docker containers for Elasticsearch and PostgreSQL, configures them with necessary environment variables, and initializes the database schema. This approach should maintain the build environment's stability and reliability.
- Ensure that the script is executed correctly in the CI environment to verify its effectiveness.
Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify that all tests pass with the new dependency setup. # Test: Run the tests and check the exit code. Expect: Exit code 0 (success). mvn test echo "Test exit code: $?" # Test: Check for test failures in the output. Expect: No test failures. mvn test | grep -i "test.*failed"Length of output: 237
Script:
#!/bin/bash # Locate the dependency-start.sh script and display its contents for review. fd dependency-start.sh --exec catLength of output: 2188
src/test/kotlin/org/rucca/cheese/auth/AuthorizationServiceTest.kt (1)
1-34
: LGTM!The test class is well-structured and follows the standard practices for Spring Boot and JUnit 5 testing. It tests the core functionality of
AuthorizationService
to verify the authorization token. The use of constructor injection for dependencies and@SpringBootTest
annotation for integration testing are good practices. The@TestInstance(TestInstance.Lifecycle.PER_CLASS)
annotation is correctly used to create a single instance of the test class for all the tests, which is required for using@BeforeAll
annotation.src/main/kotlin/org/rucca/cheese/auth/OwnerIds.kt (3)
1-7
: LGTM!The package declaration, imports, and typealias are well-defined and follow best practices.
8-16
: LGTM!The
OwnerIds
class and itsregister
method are well-implemented. The method performs the necessary check to prevent duplicate registrations and throws a clear exception if a duplicate registration is attempted.
18-30
: LGTM!The
getOwnerIdGetter
method is well-implemented. It retrieves the owner ID provider for a given resource type and resource ID, and returns anIdGetter
object that caches the owner ID for efficient access. The method handles the case when no owner ID provider is found by returningnull
, allowing the caller to handle it appropriately.src/main/kotlin/org/rucca/cheese/user/RolePermissionService.kt (1)
21-33
: LGTM!The function correctly creates an
Authorization
object for a standard user with the appropriate permissions.src/main/kotlin/org/rucca/cheese/auth/CustomAuthLogics.kt (2)
6-14
: LGTM!The
CustomAuthLogicHandler
type alias provides a clear and concise way to define the signature of custom authorization logic handlers. The parameters cover the essential aspects needed for authorization decisions, and returning a Boolean allows the handler to indicate whether the authorization should be granted or denied.
16-39
: LGTM!The
CustomAuthLogics
class provides a simple and flexible way to manage and invoke custom authorization logic handlers. Theregister
method ensures that a handler with the same name is not registered twice, and theinvoke
method retrieves the registered handler by name and invokes it with the provided parameters. The class encapsulates the storage and invocation of custom authorization logic handlers, promoting modularity and reusability.src/main/kotlin/org/rucca/cheese/common/persistent/BaseEntity.kt (2)
15-15
: Clarify the intended usage of theIdGetter
type alias.The addition of the
IdGetter
type alias enhances the code's expressiveness by providing a clear representation of functions that retrieve entity IDs. However, it's currently unused in the provided code. Could you please clarify how and where you intend to use this type alias?
1-1
: Verify the impact of the package change on other files.The package change improves the organization and clarity of the code. However, ensure that the import statements in other files that use this class have been updated accordingly.
Run the following script to verify the import statements:
Verification successful
Package change successfully integrated.
The package change for
BaseEntity
has been successfully updated across the codebase. The old import statement is no longer present, and the new import statement is correctly used in relevant files.
- The old import statement
import org.rucca.cheese.common.BaseEntity
was not found.- The new import statement
import org.rucca.cheese.common.persistent.BaseEntity
is present insrc/main/kotlin/org/rucca/cheese/space/SpaceEntity.kt
.Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the import statements for the `BaseEntity` class. # Test: Search for the old import statement. Expect: No results. rg --type kotlin $'import org\.rucca\.cheese\.common\.BaseEntity' # Test: Search for the new import statement. Expect: Only valid usages. rg --type kotlin -A 5 $'import org\.rucca\.cheese\.common\.persistent\.BaseEntity'Length of output: 659
src/test/kotlin/org/rucca/cheese/auth/ExampleRestController.kt (3)
13-16
: Verify if the function is intended to be publicly accessible without any authorization.The function logic is correct. However, please confirm if this endpoint is intended to be publicly accessible without any authorization. If not, consider adding the appropriate authorization annotations.
41-47
: LGTM!The function is correctly implemented with the
@Guard
annotation and a@ResourceId
parameter of the expected type.
49-53
: LGTM!The function is correctly implemented with the
@NoAuth
annotation, indicating that it does not require any authorization.src/test/kotlin/org/rucca/cheese/api/SpaceTest.kt (4)
3-6
: LGTM!The new imports are necessary for the changes made in the test class.
15-31
: LGTM!The changes to the test class setup are necessary to efficiently create a user and obtain an authentication token before running the tests. The
@BeforeAll
method ensures that these steps are performed only once, which is more efficient than creating a new user for each test.
34-35
: LGTM!The update to the
testGetSpaceAndNotFound
test method is necessary to ensure that the request is properly authenticated when attempting to access a non-existent space. This change makes the test more accurate in reflecting the behavior of the API endpoint for authenticated users.
Line range hint
1-42
: Overall, the changes made to theSpaceTest.kt
file are well-structured and follow best practices for testing.The updates to the test class setup and the
testGetSpaceAndNotFound
test method are necessary to ensure that the tests accurately reflect the behavior of the API endpoints for authenticated users. The file maintains a clear structure, with descriptive test method names and comprehensive assertions covering the important aspects of the API response.Great job on these changes! 👍
README.md (1)
Line range hint
1-10
:
No comments on the unchanged code segments.Also applies to: 15-41
Tools
LanguageTool
[style] ~11-~11: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...esult in all sorts of errors. You also need to set up several services. We recommend u...(REP_NEED_TO_VB)
[style] ~13-~13: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...Desktop to start the services. (You may need to removesudo
and run the commands ...(REP_NEED_TO_VB)
[style] ~17-~17: This phrase is redundant (‘I’ stands for ‘interfaces’). Use simply “APIs”.
Context: ...shell or PowerShell. This will generate API interfaces from the OpenAPI specification in ```de...(ACRONYM_TAUTOLOGY)
src/main/kotlin/org/rucca/cheese/space/SpaceController.kt (1)
Line range hint
19-23
: LGTM!The addition of the
@Guard
annotation to thegetSpace
function is a good security practice. It correctly enforces the requirement of the "query" permission for "space" before allowing access to the space data. The annotation parameters are properly specified, and the overall structure and logic of the function remain intact.src/main/kotlin/org/rucca/cheese/user/RoleBasedAuthLogicService.kt (1)
16-35
: LGTM!The
initialize
function correctly registers the "role-based" custom authorization logic with theAuthorizationService
. The logic is properly calling theaudit
function with the provided parameters.src/main/kotlin/org/rucca/cheese/user/UserEntity.kt (4)
29-29
: LGTM!The import statement is necessary for defining the
UserRepository
interface.
54-54
: LGTM!The addition of the
insertable = false
attribute to thecreatedAt
andupdatedAt
fields is appropriate as the values of these fields are likely automatically generated by the database.Also applies to: 58-58
63-64
: LGTM!The addition of the
UserRepository
interface extendingJpaRepository
is a great enhancement as it provides CRUD operations forUserEntity
, enabling easier data access and manipulation for user entities.
39-39
: Verify the class usage.The renaming of the
User
class toUserEntity
clarifies the purpose of the class as an entity in the context of JPA. However, ensure that all references to theUser
class have been updated to match the new name.Run the following script to verify the class usage:
Verification successful
Renaming Verification Successful
The renaming of the
User
class toUserEntity
has been successfully propagated throughout the codebase. All references to the class have been updated accordingly, and no issues were found. The remaining references toUser
in test files pertain to method names or variables, not the class itself.Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify all references to the `User` class have been updated to `UserEntity`. # Test: Search for the old class name. Expect: No occurrences. rg --type kotlin $'User\b'Length of output: 28
Script:
#!/bin/bash # Description: Verify all references to the `UserEntity` class. # Test: Search for the new class name. Expect: Occurrences in the codebase. rg --type kotlin $'UserEntity\b' -A 3Length of output: 39
Script:
#!/bin/bash # Description: Verify all references to the `User` and `UserEntity` classes. # Test: Search for both class names in the codebase. rg --type kotlin 'User\b|UserEntity\b' -A 3Length of output: 4885
design/DB/CREATE.sql (2)
24-26
: LGTM!The changes to the
user_profile
table look good. Adding theavatar_id
anduser_id
columns with appropriate foreign key constraints establishes the necessary relationships between the tables. TheUNIQUE
constraint onuser_id
ensures data integrity by enforcing a one-to-one relationship between users and their profiles.
50-55
: Looks good!The creation of the
avatar
table is a nice addition to the schema. It provides a dedicated place to store user profile avatars. The auto-incrementingid
column using a sequence is a good choice for generating unique identifiers for each avatar.src/main/kotlin/org/rucca/cheese/user/UserProfileEntity.kt (3)
31-35
: LGTM!The
Avatar
entity class is correctly implemented with an auto-generatedid
field using a sequence.
54-69
: LGTM!The changes to the
UserProfile
class are correctly implemented:
- The
createdAt
andupdatedAt
fields are now non-insertable, ensuring their values are automatically set by the database.- The one-to-one relationship with
UserEntity
and many-to-one relationship withAvatar
are correctly implemented using the appropriate annotations and fetch types.
72-72
: LGTM!The
UserProfileRepository
interface is correctly implemented, extendingJpaRepository
forUserProfile
, which will facilitate CRUD operations on user profiles.src/main/kotlin/org/rucca/cheese/auth/AuthorizationAspect.kt (1)
18-73
: LGTM!The
AuthorizationAspect
is well-implemented and follows best practices:
- It focuses on a single responsibility of handling authorization checks.
- It covers important scenarios such as missing annotations, duplicated
@ResourceId
annotations, and type mismatches.- It retrieves the authorization token from the request header using
RequestContextHolder
, which is a common practice in Spring applications.- It delegates the actual authorization audit to the
AuthorizationService
, promoting a clean separation of concerns.The aspect is ready to be merged.
src/test/kotlin/org/rucca/cheese/auth/UserCreatorService.kt (5)
72-74
: LGTM!The
testUsername
method is correctly implemented and generates a random test username as expected.
76-78
: LGTM!The
testPassword
method is correctly implemented and returns a hardcoded test password as expected.
80-82
: LGTM!The
testEmail
method is correctly implemented and generates a random test email as expected.
84-86
: LGTM!The
testNickname
method is correctly implemented and returns a hardcoded test nickname as expected.
92-94
: LGTM!The
testIntro
method is correctly implemented and returns a hardcoded test intro as expected.src/test/kotlin/org/rucca/cheese/auth/AuthorizationAspectTest.kt (9)
41-44
: LGTM!The test method correctly verifies the handling of
NoGuardOrNoAuthAnnotationException
byGlobalErrorHandler
when the required annotations are missing.
47-50
: LGTM!The test method correctly verifies the handling of
DuplicatedResourceIdAnnotationException
byGlobalErrorHandler
when multiple@ResourceId
annotations are present.
53-56
: LGTM!The test method correctly verifies the handling of
ResourceIdTypeMismatchException
byGlobalErrorHandler
when the type of the@ResourceId
parameter doesn't match the method parameter.
59-62
: LGTM!The test method correctly verifies that
AuthorizationService.audit
is called withnull
token when no authorization header is present in the request.
65-68
: LGTM!The test method correctly verifies that
AuthorizationService.audit
is called with the token from the "Authorization" header.
71-74
: LGTM!The test method correctly verifies that
AuthorizationService.audit
is called with the token from the "authorization" header, regardless of the header name's case.
77-80
: LGTM!The test method correctly verifies that
AuthorizationService.audit
is called with the token and theid
parameter value when both are present in the request.
83-86
: LGTM!The test method correctly verifies that
AuthorizationService.audit
is not called when the@NoAuth
annotation is present on the controller method.
89-92
: LGTM!The test method correctly verifies that
AuthorizationService.audit
is called with the token and theid
parameter value when testing a controller that uses@RestController
instead of@Controller
.src/main/kotlin/org/rucca/cheese/auth/AuthorizationService.kt (2)
29-36
: LGTM!The function correctly delegates the authorization audit to the overloaded
audit
function after verifying the token.
38-78
: LGTM!The function correctly performs authorization audit based on the provided
Authorization
object. It thoroughly checks the permission rules to determine if the requested operation is permitted. The logic for checking the permission rules is comprehensive and covers all the necessary conditions. The error handling for denied operations is also appropriate.pom.xml (4)
116-121
: LGTM!The addition of the
springmockk
dependency with version 4.0.2 and test scope is a great choice to enhance the project's unit testing capabilities. It provides a powerful mocking framework that integrates well with Spring Boot applications.
122-126
: Verify the usage and configuration ofbcrypt
.The addition of the
bcrypt
dependency is a good choice for secure password hashing. However, please ensure that the library is used correctly and that the hashing parameters (e.g., salt rounds) are appropriately configured to provide strong security.
127-131
: LGTM!The addition of the
jersey-client
dependency with version 3.1.8 is a good choice for integrating RESTful web services into the project. It provides a robust framework for building client-server interactions.
132-136
: Verify the usage and security of JSON Web Tokens.The addition of the
java-jwt
dependency is a good choice for implementing JSON Web Tokens (JWTs) in the project, likely for authentication and authorization purposes. However, please ensure that the library is used correctly and that the JWTs are properly secured (e.g., using strong signing algorithms, validating token expiration, etc.) to prevent vulnerabilities.
fun verify(token: String?): Authorization { | ||
var tokenWithoutBearer = token | ||
if (token.isNullOrEmpty()) throw AuthenticationRequiredError() | ||
if (token.indexOf("Bearer ") == 0) tokenWithoutBearer = token.substring(7) | ||
else if (token.indexOf("bearer ") == 0) tokenWithoutBearer = token.substring(7) | ||
|
||
val payload: TokenPayload | ||
try { | ||
payload = | ||
objectMapper.readValue( | ||
verifier.verify(tokenWithoutBearer).getClaim("payload")?.toString() | ||
?: throw InvalidTokenError(), | ||
TokenPayload::class.java) | ||
} catch (e: TokenExpiredException) { | ||
throw TokenExpiredError() | ||
} catch (e: JWTVerificationException) { | ||
throw InvalidTokenError() | ||
} catch (e: JacksonException) { | ||
throw RuntimeException( | ||
"The token is valid, but the payload of the token is not a TokenPayload object." + | ||
" This is ether a bug or a malicious attack.", | ||
e) | ||
} | ||
|
||
if (payload.validUntil < System.currentTimeMillis()) throw TokenExpiredError() | ||
if (payload.signedAt > System.currentTimeMillis()) | ||
throw RuntimeException( | ||
"The token is valid, but it was signed in the future." + | ||
" This is a timezone bug or a malicious attack.") | ||
|
||
return payload.authorization | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Log the original exception before throwing custom exceptions.
The function implementation for token verification looks good overall. It properly verifies the token and handles various error scenarios.
However, as pointed out by the static analysis hints, the caught exceptions are swallowed without logging or propagating the original exception information. This can make it difficult to diagnose issues if an unexpected exception occurs.
To preserve the exception information for better diagnostics, consider logging the original exception before throwing the custom exceptions. For example:
} catch (e: TokenExpiredException) {
logger.error("Token expired", e)
throw TokenExpiredError()
} catch (e: JWTVerificationException) {
logger.error("Invalid token", e)
throw InvalidTokenError()
} catch (e: JacksonException) {
logger.error("Invalid token payload", e)
throw RuntimeException(...)
}
Tools
detekt
[warning] 93-93: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 95-95: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
Outside diff range and nitpick comments (6)
src/main/kotlin/org/rucca/cheese/auth/annotation/NoAuth.kt (1)
1-3
: LGTM! Consider removing the empty default constructor.The
NoAuth
annotation class is correctly defined and follows the Kotlin syntax. The@Target
and@MustBeDocumented
annotations are used appropriately.As suggested by the static analysis hint, the empty default constructor can be safely removed. Apply this diff to remove it:
-@Target(AnnotationTarget.FUNCTION) @MustBeDocumented annotation class NoAuth() +@Target(AnnotationTarget.FUNCTION) @MustBeDocumented annotation class NoAuthTools
detekt
[warning] 3-3: An empty default constructor can be removed.
(detekt.empty-blocks.EmptyDefaultConstructor)
src/test/README.md (1)
4-6
: Address Markdownlint hints.Please consider addressing the following Markdownlint hints to improve the formatting of the section:
- Remove the trailing punctuation (
.
) from the heading at line 4.- Wrap the bare URL at line 6 with angle brackets (
<>
).Apply this diff to address the Markdownlint hints:
-## Do not use `Mockito`. Use `MockK` instead. +## Do not use `Mockito`. Use `MockK` instead `Mockito` has poor support for Kotlin, so it is better to use `MockK` instead. -See https://github.com/Ninja-Squad/springmockk for more information. +See <https://github.com/Ninja-Squad/springmockk> for more information.Tools
Markdownlint
4-4: Punctuation: '.'
Trailing punctuation in heading(MD026, no-trailing-punctuation)
6-6: null
Bare URL used(MD034, no-bare-urls)
src/test/kotlin/org/rucca/cheese/auth/AuthorizationServiceTest.kt (1)
29-33
: Consider adding more test scenarios.The current test class only covers the happy path scenario for the
verify()
method. Consider adding more test methods to cover other scenarios, such as:
- Testing with an invalid token
- Testing with an expired token
- Testing with a token that belongs to a different user
- Testing with a token that has been revoked
This will help ensure that the
verify()
method behaves correctly in all possible scenarios.src/main/kotlin/org/rucca/cheese/auth/AuthorizationAspect.kt (1)
23-26
: Consider including other controller annotations in the pointcut expression.The pointcut expression correctly matches methods in classes annotated with
@RestController
or@Controller
. However, it may miss other controller annotations like@GraphQLController
.Consider updating the pointcut expression to include other relevant controller annotations used in the project:
-"@within(org.springframework.web.bind.annotation.RestController) || " + - "@within(org.springframework.stereotype.Controller)") +"@within(org.springframework.web.bind.annotation.RestController) || " + + "@within(org.springframework.stereotype.Controller) || " + + "@within(org.springframework.graphql.data.method.annotation.Controller)")src/test/kotlin/org/rucca/cheese/auth/UserCreatorService.kt (1)
30-50
: Validate input parameters in thecreateUser
method.The
createUser
method correctly creates a new user and user profile, and uses secure password hashing with BCrypt. However, consider adding validation for the input parameters to ensure data integrity and prevent invalid data from being persisted.src/main/kotlin/org/rucca/cheese/auth/AuthorizationService.kt (1)
1-112
: Consider adding more detailed documentation for the class and its methods.While the class and its methods are well-structured and named, it would be beneficial to add more detailed documentation explaining the purpose, parameters, and return values of each method. This will improve the overall maintainability and understandability of the code.
Tools
detekt
[warning] 93-93: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 95-95: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (40)
- .github/workflows/build.yml (1 hunks)
- README.md (1 hunks)
- design/DB/CREATE.sql (3 hunks)
- doc/script/dependency-restart.sh (1 hunks)
- doc/script/dependency-start.sh (1 hunks)
- pom.xml (1 hunks)
- src/main/kotlin/org/rucca/cheese/BackendApplication.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/AuthorizationAspect.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/AuthorizationService.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/CustomAuthLogics.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/Definitions.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/OwnerIds.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/annotation/Guard.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/annotation/NoAuth.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/annotation/ResourceId.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/error/AuthenticationRequiredError.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/error/InvalidTokenError.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/error/PermissionDeniedError.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/error/TokenExpiredError.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/exception/DuplicatedResourceIdAnnotationException.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/exception/NoGuardOrNoAuthAnnotationException.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/exception/ResourceIdTypeMismatchException.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/auth/exception/UnsupportedRoleException.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/common/config/ApplicationConfig.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/common/persistent/BaseEntity.kt (2 hunks)
- src/main/kotlin/org/rucca/cheese/space/SpaceController.kt (2 hunks)
- src/main/kotlin/org/rucca/cheese/space/SpaceService.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/user/RoleBasedAuthLogicService.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/user/RolePermissionService.kt (1 hunks)
- src/main/kotlin/org/rucca/cheese/user/UserEntity.kt (3 hunks)
- src/main/kotlin/org/rucca/cheese/user/UserProfileEntity.kt (2 hunks)
- src/main/resources/application.properties (1 hunks)
- src/test/README.md (1 hunks)
- src/test/kotlin/org/rucca/cheese/api/SpaceTest.kt (2 hunks)
- src/test/kotlin/org/rucca/cheese/auth/AuthorizationAspectTest.kt (1 hunks)
- src/test/kotlin/org/rucca/cheese/auth/AuthorizationServiceTest.kt (1 hunks)
- src/test/kotlin/org/rucca/cheese/auth/ExampleController.kt (1 hunks)
- src/test/kotlin/org/rucca/cheese/auth/ExampleRestController.kt (1 hunks)
- src/test/kotlin/org/rucca/cheese/auth/UserCreatorService.kt (1 hunks)
- src/test/kotlin/org/rucca/cheese/auth/UserCreatorServiceTest.kt (1 hunks)
Files skipped from review due to trivial changes (1)
- src/main/kotlin/org/rucca/cheese/space/SpaceService.kt
Additional context used
detekt
src/main/kotlin/org/rucca/cheese/auth/annotation/NoAuth.kt
[warning] 3-3: An empty default constructor can be removed.
(detekt.empty-blocks.EmptyDefaultConstructor)
src/main/kotlin/org/rucca/cheese/auth/annotation/ResourceId.kt
[warning] 3-3: An empty default constructor can be removed.
(detekt.empty-blocks.EmptyDefaultConstructor)
src/main/kotlin/org/rucca/cheese/user/RoleBasedAuthLogicService.kt
[warning] 58-58: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
src/test/kotlin/org/rucca/cheese/auth/UserCreatorService.kt
[warning] 67-67: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
src/main/kotlin/org/rucca/cheese/auth/AuthorizationService.kt
[warning] 93-93: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 95-95: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
Markdownlint
src/test/README.md
4-4: Punctuation: '.'
Trailing punctuation in heading(MD026, no-trailing-punctuation)
6-6: null
Bare URL used(MD034, no-bare-urls)
LanguageTool
README.md
[style] ~11-~11: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...esult in all sorts of errors. You also need to set up several services. We recommend u...(REP_NEED_TO_VB)
[style] ~13-~13: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...Desktop to start the services. (You may need to removesudo
and run the commands ...(REP_NEED_TO_VB)
Additional comments not posted (72)
src/main/kotlin/org/rucca/cheese/auth/annotation/ResourceId.kt (2)
1-3
: LGTM!The
ResourceId
annotation is correctly defined and targeted for value parameters. The@MustBeDocumented
annotation ensures that theResourceId
annotation is included in the Javadoc.Tools
detekt
[warning] 3-3: An empty default constructor can be removed.
(detekt.empty-blocks.EmptyDefaultConstructor)
3-3
: Ignore the static analysis warning.The static analysis warning about an empty default constructor is a false positive because annotations in Kotlin do not require a constructor.
Tools
detekt
[warning] 3-3: An empty default constructor can be removed.
(detekt.empty-blocks.EmptyDefaultConstructor)
src/main/kotlin/org/rucca/cheese/auth/exception/UnsupportedRoleException.kt (1)
1-5
: LGTM!The
UnsupportedRoleException
class is well-defined and follows best practices:
- The package declaration and directory structure are consistent.
- The class name clearly conveys the purpose of the exception.
- Extending
RuntimeException
is appropriate for an unchecked exception.- The constructor takes the unsupported role as a parameter and includes it in the exception message.
The exception can be effectively used to handle cases where an unsupported role is encountered during authorization.
src/main/kotlin/org/rucca/cheese/auth/annotation/Guard.kt (1)
1-8
: LGTM!The
Guard
annotation class is well-defined and follows good practices:
- The class name and property names are clear and descriptive.
- The annotation is appropriately targeted for functions.
- The
@MustBeDocumented
annotation ensures that the annotation will be included in the generated documentation.- The
action
andresourceType
properties provide flexibility in defining the authorization requirements for functions.The annotation can be effectively used to specify the required authorization for executing functions, and an authorization mechanism can utilize the
action
andresourceType
properties to determine if the user has the necessary permissions.src/main/kotlin/org/rucca/cheese/auth/error/InvalidTokenError.kt (1)
1-6
: LGTM!The
InvalidTokenError
class is well-defined and follows the project's error handling pattern. The package structure, error message, and associated HTTP status code are appropriate for representing an invalid token scenario.src/main/kotlin/org/rucca/cheese/auth/error/TokenExpiredError.kt (1)
1-6
: LGTM!The
TokenExpiredError
class is correctly defined and follows the best practices:
- The class extends
BaseError
which is likely a custom error class defined in the project.- The class is defined in a package that follows a structured hierarchy.
- The class constructor is using
HttpStatus.UNAUTHORIZED
which is the appropriate HTTP status code for an expired token.- The class constructor is using a clear and concise error message.
src/test/README.md (2)
1-3
: LGTM!The introduction section is clear and concise.
4-6
: LGTM!The recommendation to use
MockK
instead ofMockito
for mocking in Kotlin is valid and useful for developers. The link to thespringmockk
library is also helpful for more information.Tools
Markdownlint
4-4: Punctuation: '.'
Trailing punctuation in heading(MD026, no-trailing-punctuation)
6-6: null
Bare URL used(MD034, no-bare-urls)
src/main/kotlin/org/rucca/cheese/auth/exception/ResourceIdTypeMismatchException.kt (1)
1-9
: LGTM!The custom exception class
ResourceIdTypeMismatchException
is well-defined and follows best practices:
- The exception class is defined in a separate file and package, promoting modularity and encapsulation.
- The exception class extends
RuntimeException
, which is appropriate for unchecked exceptions.- The exception message is informative and includes relevant details about the context where the exception occurred.
- The naming convention for the exception class is followed.
Great job!
src/main/kotlin/org/rucca/cheese/common/config/ApplicationConfig.kt (1)
1-11
: LGTM! Verify the configuration binding and consider handling missing values.The
ApplicationConfig
class is properly annotated with@Component
and@ConfigurationProperties
, allowing it to be automatically detected by Spring and have its properties bound to the corresponding configuration values.To verify the configuration binding, ensure that the
application.properties
orapplication.yml
file contains the following properties:application.legacy-url=<value> application.jwt-secret=<value>
Replace
<value>
with the actual values forlegacyUrl
andjwtSecret
.To handle scenarios where the
legacyUrl
orjwtSecret
properties might be missing from the configuration file, consider one of the following approaches:
- Provide default values for the properties:
lateinit var legacyUrl: String = "default-url" lateinit var jwtSecret: String = "default-secret"
- Make the properties nullable and handle null values appropriately:
var legacyUrl: String? = null var jwtSecret: String? = nullChoose the approach that best fits your application's requirements and handle the missing values accordingly in the code that uses these properties.
src/main/kotlin/org/rucca/cheese/auth/error/AuthenticationRequiredError.kt (1)
1-10
: LGTM!The
AuthenticationRequiredError
class is well-defined, extendingBaseError
and usingHttpStatus.UNAUTHORIZED
appropriately. The package structure and naming are also logical and clear. Good job!src/main/kotlin/org/rucca/cheese/auth/exception/NoGuardOrNoAuthAnnotationException.kt (1)
1-9
: LGTM!The custom exception class is well-defined and follows best practices:
- The package name and class name are appropriate and descriptive.
- Extending
RuntimeException
is suitable for an unchecked exception.- The constructor parameters provide relevant context for the exception.
- The error message is clear, informative, and uses string interpolation effectively.
Great job!
src/main/kotlin/org/rucca/cheese/auth/exception/DuplicatedResourceIdAnnotationException.kt (1)
1-9
: LGTM!The
DuplicatedResourceIdAnnotationException
class is well-defined and serves a specific purpose. The exception message provides useful information for debugging, and the class follows the naming convention for exceptions. The class is also placed in the appropriate package.src/main/kotlin/org/rucca/cheese/BackendApplication.kt (1)
3-3
: LGTM!The addition of the
@EnableConfigurationProperties
annotation withApplicationConfig
class as the argument is a good practice. It allows the application to bind external configuration properties to theApplicationConfig
class, making the application more flexible and maintainable by supporting dynamic configuration based on different environments or deployment scenarios.The import statement for the
ApplicationConfig
class is also correctly added.Also applies to: 5-5, 8-8
src/main/resources/application.properties (1)
12-12
: LGTM, but update the value in production environments.The addition of the
application.legacy-url
property is a good way to externalize the configuration for the legacy service endpoint. However, please ensure that the actual legacy service URL is configured in production environments, as the current valuehttp://localhost:3000
is likely a placeholder for development or testing purposes.src/test/kotlin/org/rucca/cheese/auth/ExampleController.kt (1)
10-19
: Clarify the purpose and usage of custom annotations.The
@Guard
and@ResourceId
annotations seem to be custom annotations used for authorization and resource identification respectively. However, their purpose and usage are not clear from the provided code context.Could you please provide more information on how these annotations are intended to be used and how they integrate with the overall authorization mechanism?
Additionally, consider using the
id
parameter in the endpoint implementation, as it is currently unused and the method returns a hardcoded string.Also, since this class is located under the
test
directory, please confirm if it is intended to be a test class or a sample implementation.src/main/kotlin/org/rucca/cheese/auth/error/PermissionDeniedError.kt (1)
1-16
: LGTM!The
PermissionDeniedError
class is well-structured and follows best practices:
- It is defined in an appropriate package hierarchy.
- It extends the
BaseError
class, which likely provides common error handling functionality.- The constructor parameters are relevant and provide necessary information about the unauthorized action, resource type, and resource ID.
- The error message is descriptive and includes the provided information.
- The HTTP status is set to
FORBIDDEN
, which is suitable for permission denied errors.Overall, the code is clean, readable, and follows a consistent structure. Great job!
src/test/kotlin/org/rucca/cheese/auth/UserCreatorServiceTest.kt (1)
8-14
: LGTM!The test class is correctly set up as a Spring Boot integration test, with
UserCreatorService
autowired in the constructor and a logger initialized.src/main/kotlin/org/rucca/cheese/auth/Definitions.kt (5)
5-5
: LGTM!Using a type alias for
String
is a good practice to provide a more descriptive and domain-specific name for the type. It can help improve code readability and maintainability by making the intent of the code more clear.
7-12
: LGTM!The data class
AuthorizedResource
is well-defined and represents a resource that can be authorized. The properties provide relevant information about the resource, such as the owner, types, IDs, and additional data. Making all properties nullable with default values provides flexibility in creating instances of the data class.
14-19
: LGTM!The data class
Permission
is well-defined and represents a permission that grants access to perform certain actions on a resource. The properties provide relevant information about the permission, such as the authorized actions, the resource on which the actions can be performed, and any additional custom logic associated with the permission.
21-24
: LGTM!The data class
Authorization
is well-defined and represents an authorization granted to a user. TheuserId
property identifies the user, and thepermissions
property specifies the list of permissions granted to the user.
26-30
: LGTM!The data class
TokenPayload
is well-defined and represents the payload of an authorization token. The properties provide relevant information about the token, such as the associatedAuthorization
object, the timestamp when the token was signed, and the timestamp until which the token is valid..github/workflows/build.yml (1)
28-29
: Verify the correctness and completeness of the service setup in the external script.The change introduces a new step that runs an external script (
doc/script/dependency-start.sh
) to set up the necessary services. While this change potentially streamlines the workflow, it also shifts the responsibility of service management to the external script.Please ensure that the script correctly and completely sets up the required services (PostgreSQL and Elasticsearch) that were previously defined in the workflow file. Consider adding comments or documentation to clarify the purpose and functionality of the script.
To verify the correctness and completeness of the service setup, please provide the contents of the
doc/script/dependency-start.sh
script for review. You can use the following command to retrieve the script:If you need any assistance in reviewing the script or have any questions, please let me know. I'd be happy to help!
Verification successful
Verification Successful: The external script correctly sets up the required services.
The
doc/script/dependency-start.sh
script successfully sets up the necessary services (Elasticsearch and PostgreSQL) and includes additional setup for thecheese-backend-dev
service. The script includes health checks and configuration adjustments, ensuring the services are correctly initialized and running. No further action is needed.
- The script correctly replaces the removed PostgreSQL and Elasticsearch service definitions from the workflow file.
Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Retrieve the `doc/script/dependency-start.sh` script for review. cat doc/script/dependency-start.shLength of output: 2189
src/test/kotlin/org/rucca/cheese/auth/AuthorizationServiceTest.kt (1)
1-34
: LGTM!The test class follows the standard structure and naming conventions for a JUnit 5 test class in Kotlin. The use of
@SpringBootTest
and@TestInstance(TestInstance.Lifecycle.PER_CLASS)
is appropriate for the test class. Theprepare()
method sets up the necessary test data before running the tests, and thetestVerify()
method tests theverify()
method ofAuthorizationService
as expected.src/main/kotlin/org/rucca/cheese/auth/OwnerIds.kt (1)
8-31
: LGTM!The
OwnerIds
class is well-designed and follows best practices:
- It adheres to the Single Responsibility Principle by focusing on managing owner ID providers.
- It properly handles duplicate registrations and throws a
RuntimeException
to prevent overwriting existing providers.- It lazily evaluates the owner ID using the registered provider, which is a good optimization.
- It uses appropriate data structures and type aliases to improve code readability and maintainability.
- It is properly encapsulated by keeping the
ownerIdProviders
map private and providing public functions to interact with it.Great job!
src/main/kotlin/org/rucca/cheese/user/RolePermissionService.kt (1)
21-33
: LGTM!The method correctly constructs the
Authorization
object for a standard user with the appropriate permission to query spaces.src/main/kotlin/org/rucca/cheese/auth/CustomAuthLogics.kt (1)
6-14
: LGTM!The
CustomAuthLogicHandler
type alias is well-defined and provides a clear contract for custom authorization logic handlers. The parameter names are descriptive and the Boolean return type is appropriate. Using a type alias here promotes code reuse and improves readability.src/main/kotlin/org/rucca/cheese/common/persistent/BaseEntity.kt (2)
15-15
: Provide more context on the usage ofIdGetter
.The addition of the
IdGetter
type alias is a good idea to enhance code readability and maintainability. However, it would be helpful to provide more context on how this type alias will be utilized in the codebase. Could you please share some examples or point me to the relevant code whereIdGetter
is being used?
1-1
: Verify the impact of the package change.The package reorganization improves the structure and clarity of the code. However, ensure that the import statements in other files that use the
BaseEntity
class have been updated to reflect the new package.Run the following script to verify the import statements:
Verification successful
Package change verification successful.
The package reorganization for
BaseEntity
has been successfully updated in the codebase. The old import statement is no longer present, and the new import statement is correctly used in at least one file. No further action is required.
- Verified in
src/main/kotlin/org/rucca/cheese/space/SpaceEntity.kt
.Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the import statements for the `BaseEntity` class. # Test: Search for the old import statement. Expect: No occurrences. rg --type kotlin $'import org\.rucca\.cheese\.common\.BaseEntity' # Test: Search for the new import statement. Expect: Only occurrences of the new import statement. rg --type kotlin $'import org\.rucca\.cheese\.common\.persistent\.BaseEntity'Length of output: 252
src/test/kotlin/org/rucca/cheese/auth/ExampleRestController.kt (5)
13-16
: Verify if the function is intended to be publicly accessible.The function logic is correct. However, please confirm if this endpoint is intended to be publicly accessible without any authorization checks. If not, consider adding the appropriate authorization annotations.
18-25
: Verify the purpose of having two resource IDs.The function logic is correct. However, please confirm the purpose of having two resource IDs as parameters. If only one resource ID is needed, consider removing the extra parameter to avoid confusion or misuse of the endpoint.
35-39
: Verify how the authorization will work without a resource ID.The function logic is correct. However, please confirm how the authorization logic will work without any resource ID parameter. If a resource ID is needed for proper authorization, consider adding it as a parameter.
41-47
: LGTM!The function logic is correct, and the usage of the
@ResourceId
annotation with theIdType
type is accurate.
49-53
: LGTM!The function logic is correct, and the usage of the
@NoAuth
annotation to bypass authorization is appropriate.src/test/kotlin/org/rucca/cheese/api/SpaceTest.kt (5)
3-6
: LGTM!The new imports are necessary for the changes made in the test class and look good.
15-22
: LGTM!The changes to the
SpaceTest
class look good:
- The
@TestInstance
annotation is necessary to allow the@BeforeAll
annotated method to be non-static.- The
UserCreatorService
dependency injection is necessary to create a user and obtain an authentication token before running the tests.
23-25
: LGTM!The new member variables
user
andtoken
look good:
- They are necessary to store the user and token information for use in the test methods.
- The
lateinit
annotation is appropriate as the variables will be initialized in the@BeforeAll
annotated method.
26-30
: LGTM!The new
prepare
method looks good:
- It is necessary to set up the user authentication before running the tests.
- The
@BeforeAll
annotation is appropriate to run the method once before all test methods.
34-35
: LGTM!The changes to the
testGetSpaceAndNotFound
test method look good:
- Including the "Authorization" header with a bearer token is necessary to test the space API with user authentication.
- The token is correctly obtained from the
prepare
method and used in the request header.README.md (1)
11-14
: LGTM!The streamlined instructions for setting up necessary services using Docker and scripts improve the user experience and make the process more straightforward. The guidance for Docker Desktop on Windows is a helpful addition for users on that platform.
Tools
LanguageTool
[style] ~11-~11: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...esult in all sorts of errors. You also need to set up several services. We recommend u...(REP_NEED_TO_VB)
[style] ~13-~13: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...Desktop to start the services. (You may need to removesudo
and run the commands ...(REP_NEED_TO_VB)
src/main/kotlin/org/rucca/cheese/space/SpaceController.kt (1)
19-19
: The addition of the@Guard
annotation is a positive change, but more information is needed to assess its effectiveness.The
@Guard
annotation added to thegetSpace
method is a good step towards enhancing the security of the API by restricting access to the method based on certain conditions. However, without more context about how the annotation is implemented and what the "query" and "space" parameters represent, it's difficult to fully assess the effectiveness of the access control logic.Could you please provide more information about the
@Guard
annotation, such as its implementation details and how it integrates with the existing authentication and authorization mechanisms in the project? This will help in better understanding the impact of this change on the project's security.src/main/kotlin/org/rucca/cheese/user/RoleBasedAuthLogicService.kt (1)
16-35
: LGTM!The
initialize
method correctly registers the custom authorization logic with theAuthorizationService
using a lambda function. The lambda function passes all the required parameters to theaudit
method.src/main/kotlin/org/rucca/cheese/user/UserEntity.kt (5)
29-29
: LGTM!The import statement is necessary and correctly added.
39-39
: LGTM!The class renaming enhances clarity and is consistent with the file name change.
54-54
: LGTM!The
insertable = false
attribute is correctly added to allow for automatic timestamping by the database.
58-58
: LGTM!The
insertable = false
attribute is correctly added to allow for automatic timestamping by the database.
64-64
: LGTM!The
UserRepository
interface is correctly defined and enhances the data access layer by providing a repository pattern for managing user entities.design/DB/CREATE.sql (4)
24-26
: LGTM!The changes to the
user_profile
table look good:
- The
avatar_id
foreign key establishes a relationship with theavatar
table.- The
UNIQUE
constraint onuser_id
ensures that each user profile is associated with a distinct user.- The
NOT NULL
constraints enforce data integrity.
50-55
: Looks good!The
avatar
table is properly defined with an auto-incrementing primary key using theavatar_id_seq
sequence. The table and column names follow naming conventions.
70-71
: LGTM!The foreign key constraint is correctly defined, establishing a relationship between the
user_profile
andavatar
tables.
73-74
: Looks good!The foreign key constraint is correctly defined, establishing a relationship between the
user_profile
anduser
tables.src/main/kotlin/org/rucca/cheese/user/UserProfileEntity.kt (3)
31-35
: LGTM!The
Avatar
class is correctly implemented as a JPA entity, with theid
field properly annotated for auto-generation using a sequence.
54-54
: LGTM!The changes to the
UserProfile
class are correctly implemented:
- The one-to-one relationship with
UserEntity
and many-to-one relationship withAvatar
are properly annotated using JPA.- Updating the
createdAt
andupdatedAt
fields to be non-insertable ensures their values are set by the database upon record creation and update.Also applies to: 58-58, 63-69
72-72
: LGTM!The
UserProfileRepository
interface is correctly declared, extendingJpaRepository
forUserProfile
. This will provide CRUD operations for user profiles out of the box.src/main/kotlin/org/rucca/cheese/auth/AuthorizationAspect.kt (2)
1-17
: LGTM!The package declaration and import statements are correct and relevant.
18-22
: LGTM!The
AuthorizationAspect
class is correctly defined as a Spring aspect and bean. The constructor injection ofAuthorizationService
is also properly done.src/test/kotlin/org/rucca/cheese/auth/UserCreatorService.kt (1)
72-94
: LGTM!The test data generation methods provide utility methods to generate unique test data for various fields. The use of random number generation ensures the uniqueness of the generated test data.
src/test/kotlin/org/rucca/cheese/auth/AuthorizationAspectTest.kt (9)
40-44
: LGTM!The test method is correctly testing the scenario where the endpoint does not have the
@Guard
or@NoAuth
annotation. It verifies that the expected exception is thrown and handled by theGlobalErrorHandler
.
46-50
: LGTM!The test method is correctly testing the scenario where the endpoint has multiple
@ResourceId
annotations. It verifies that the expected exception is thrown and handled by theGlobalErrorHandler
.
52-56
: LGTM!The test method is correctly testing the scenario where the type of the
@ResourceId
annotation does not match the type of the parameter. It verifies that the expected exception is thrown and handled by theGlobalErrorHandler
.
58-62
: LGTM!The test method is correctly testing the scenario where the endpoint does not have a resource ID and the request does not have a token. It verifies that the
AuthorizationService.audit
method is called with the expected arguments.
64-68
: LGTM!The test method is correctly testing the scenario where the endpoint does not have a resource ID and the request has a token in the
Authorization
header. It verifies that theAuthorizationService.audit
method is called with the expected arguments.
70-74
: LGTM!The test method is correctly testing the scenario where the endpoint does not have a resource ID and the request has a token in the
authorization
header. It verifies that theAuthorizationService.audit
method is called with the expected arguments.
76-80
: LGTM!The test method is correctly testing the scenario where the endpoint has a resource ID and the request has a token in the
authorization
header. It verifies that theAuthorizationService.audit
method is called with the expected arguments.
82-86
: LGTM!The test method is correctly testing the scenario where the endpoint has the
@NoAuth
annotation. It verifies that theAuthorizationService.audit
method is not called.
88-92
: LGTM!The test method is correctly testing the scenario where the endpoint is a Spring MVC controller and has a resource ID and the request has a token in the
authorization
header. It verifies that theAuthorizationService.audit
method is called with the expected arguments.src/main/kotlin/org/rucca/cheese/auth/AuthorizationService.kt (1)
1-112
: Overall, theAuthorizationService
class is well-structured and provides the necessary functionality for authorization.
- The class has a clear purpose and is well-organized.
- The
audit
method performs a series of checks to determine if an operation is permitted based on the provided authorization and resource details.- The
verify
method handles the verification of JWT tokens and returns the correspondingAuthorization
object.- The class uses appropriate dependencies and follows good practices such as using constants and logging.
Tools
detekt
[warning] 93-93: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 95-95: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
pom.xml (4)
116-121
: LGTM!Adding the
springmockk
dependency is a good practice for improving the testing of Spring components. The version 4.0.2 is a stable release, and the test scope is appropriate as this library is only needed for testing purposes.
122-126
: LGTM!Adding the
bcrypt
dependency is a good choice for secure password hashing. The version 0.10.2 is a stable release, and the dependency should be used in the main application code for password hashing.
127-131
: LGTM!Adding the
jersey-client
dependency suggests that the application will interact with external RESTful services. The version 3.1.8 is a recent stable release, and the dependency should be used in the main application code for making HTTP requests.
132-136
: LGTM!Adding the
java-jwt
dependency indicates that the application will use JSON Web Tokens (JWTs) for authentication and authorization. The version 4.4.0 is a stable release, and the dependency should be used in the main application code for generating and validating JWTs.
#!/bin/sh | ||
sudo systemctl start docker | ||
sudo docker restart elasticsearch postgres cheese_legacy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve the script's security, error handling, logging, and usability.
The script has the following issues:
- Using
sudo
in scripts can be a security risk as it grants elevated privileges. It's better to use a dedicated user with limited permissions to run docker commands. - The script is not checking for errors after running each command. If any command fails, the script will continue executing, which can lead to unexpected behavior.
- The script is not logging any information about the restart process, which can make it difficult to debug issues.
- The script is not checking if the docker daemon or the containers are already running before restarting them. Restarting a running container can cause downtime.
- The script is not providing any usage instructions or help text.
Consider applying the following changes to improve the script:
- Create a dedicated user with limited permissions to run docker commands and use that user instead of
sudo
. - Add error handling after each command to check if it succeeded. If a command fails, log an error message and exit the script with a non-zero status code.
- Add logging statements to record the start and end time of the script, and the status of each command.
- Check if the docker daemon and the containers are already running before restarting them. If they are running, log a message indicating that they are already running and skip the restart.
- Add a help text at the beginning of the script to provide usage instructions and explain what the script does.
Here's an example of how the script could be improved:
#!/bin/sh
set -e
log() {
echo "[$(date)] $1"
}
usage() {
echo "Usage: $0"
echo "Restarts the docker daemon and the specified docker containers."
echo "Requires docker to be installed and the current user to have permissions to run docker commands."
}
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
usage
exit 0
fi
log "Checking if docker is running..."
if ! docker info > /dev/null 2>&1; then
log "Docker is not running, starting it now..."
sudo systemctl start docker
else
log "Docker is already running, skipping start."
fi
for container in elasticsearch postgres cheese_legacy; do
log "Checking if $container is running..."
if ! docker inspect --format="{{.State.Running}}" $container 2>/dev/null | grep -q true; then
log "$container is not running, restarting it now..."
docker restart $container
else
log "$container is already running, skipping restart."
fi
done
log "Done."
This script includes the following improvements:
- It uses
set -e
to exit immediately if any command fails. - It defines a
log
function to log messages with a timestamp. - It defines a
usage
function to display help text when the script is called with-h
or--help
. - It checks if the docker daemon is running before starting it, and skips the start if it's already running.
- It checks if each container is running before restarting it, and skips the restart if it's already running.
- It logs messages indicating the status of each command.
Note that this script assumes that the current user has permissions to run docker commands without sudo
. If that's not the case, you'll need to create a dedicated user with limited permissions to run docker commands and use that user instead.
@@ -8,3 +8,6 @@ spring.datasource.password=postgres | |||
spring.datasource.driver-class-name=org.postgresql.Driver | |||
spring.jpa.show-sql=true | |||
spring.jpa.hibernate.ddl-auto=validate | |||
|
|||
application.legacy-url=http://localhost:3000 | |||
application.jwt-secret=test-secret |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a strong, randomly generated secret key in production environments.
The application.jwt-secret
property is used to configure the secret key for JWT signing or verification, which is critical for securing API endpoints and managing user authentication. However, the current value test-secret
is a weak secret and should never be used in production environments.
It's crucial to use a strong, randomly generated secret key in production environments to ensure the security of JWTs. Please update the value to a strong, randomly generated secret key before deploying to production.
Do you want me to generate a strong secret key or open a GitHub issue to track this task?
@Test | ||
fun createUserAndLogin() { | ||
val response = userCreatorService.createUser() | ||
val token = userCreatorService.login(response.username, response.password) | ||
logger.info("Token: $token") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add assertions and remove sensitive information logging.
The test method is missing assertions to validate the behavior of UserCreatorService
. Add assertions to check:
- The
response
fromcreateUser()
contains the expected data. - The
token
fromlogin()
is not null or empty.
Also, remove the logging of the token
at line 20 as it could be a security risk to log sensitive information.
fun getAuthorizationForUserWithRole( | ||
userId: IdType, | ||
role: String, | ||
): Authorization { | ||
return when (role) { | ||
"standard-user" -> getAuthorizationForStandardUser(userId) | ||
else -> throw IllegalArgumentException("Role '$role' is not supported") | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mask the role name in the exception message.
To avoid leaking potentially sensitive information about valid roles, consider masking the role name in the exception message.
Apply this diff to mask the role name:
- else -> throw IllegalArgumentException("Role '$role' is not supported")
+ else -> throw IllegalArgumentException("Unsupported role")
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
fun getAuthorizationForUserWithRole( | |
userId: IdType, | |
role: String, | |
): Authorization { | |
return when (role) { | |
"standard-user" -> getAuthorizationForStandardUser(userId) | |
else -> throw IllegalArgumentException("Role '$role' is not supported") | |
} | |
} | |
fun getAuthorizationForUserWithRole( | |
userId: IdType, | |
role: String, | |
): Authorization { | |
return when (role) { | |
"standard-user" -> getAuthorizationForStandardUser(userId) | |
else -> throw IllegalArgumentException("Unsupported role") | |
} | |
} |
class CustomAuthLogics { | ||
private val logics: MutableMap<String, CustomAuthLogicHandler> = mutableMapOf() | ||
|
||
fun register( | ||
name: String, | ||
handler: CustomAuthLogicHandler, | ||
) { | ||
if (logics.containsKey(name)) throw RuntimeException("Custom auth logic '$name' already exists.") | ||
logics[name] = handler | ||
} | ||
|
||
fun invoke( | ||
name: String, | ||
userId: IdType, | ||
action: AuthorizedAction, | ||
resourceType: String, | ||
resourceId: IdType?, | ||
resourceOwnerIdGetter: IdGetter?, | ||
customLogicData: Any?, | ||
): Boolean { | ||
val handler = logics[name] ?: throw RuntimeException("Custom auth logic '$name' not found.") | ||
return handler(userId, action, resourceType, resourceId, resourceOwnerIdGetter, customLogicData) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider making the class a singleton.
The CustomAuthLogics
class provides a clean and effective way to manage custom authorization logic handlers. The register
and invoke
methods are implemented correctly, with appropriate error handling.
To ensure that only one instance of CustomAuthLogics
is used throughout the application, consider making it a singleton. This can be achieved by making the constructor private and providing a static getInstance
method that returns the single instance of the class.
Here's how you can refactor the class to make it a singleton:
class CustomAuthLogics private constructor() {
private val logics: MutableMap<String, CustomAuthLogicHandler> = mutableMapOf()
companion object {
private val instance = CustomAuthLogics()
fun getInstance(): CustomAuthLogics {
return instance
}
}
// rest of the class remains the same
}
With this change, you can obtain the single instance of CustomAuthLogics
by calling CustomAuthLogics.getInstance()
instead of creating a new instance each time.
fun audit( | ||
userId: IdType, | ||
action: AuthorizedAction, | ||
resourceType: String, | ||
resourceId: IdType?, | ||
resourceOwnerIdGetter: IdGetter?, | ||
customLogicData: Any?, | ||
): Boolean { | ||
val role = | ||
(customLogicData as? Map<*, *>)?.get("role") as? String | ||
?: throw RuntimeException( | ||
"Role not found in customLogicData. This is ether a bug or a malicious attack.") | ||
val authorization = rolePermissionService.getAuthorizationForUserWithRole(userId, role) | ||
try { | ||
authorizationService.audit( | ||
authorization, | ||
action, | ||
resourceType, | ||
resourceId, | ||
) | ||
return true | ||
} catch (e: PermissionDeniedError) { | ||
return false | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a safer approach for role extraction and log swallowed exceptions.
- The extraction of the user's role from the
customLogicData
parameter is error-prone and can lead to aRuntimeException
if the role is not found. Consider using a safer approach, such as:
val role = customLogicData?.let { it as? Map<*, *> }?.get("role") as? String
if (role == null) {
// Log the error and return false or throw a more specific exception
return false
}
This way, if the role is not found, you can log the error and return false
or throw a more specific exception instead of crashing the application.
- The caught
PermissionDeniedError
exception is swallowed, potentially losing the original exception. It's important to log the exception before returningfalse
to help diagnose issues. Update the catch block to log the exception:
} catch (e: PermissionDeniedError) {
log.error("Permission denied for user $userId", e)
return false
}
Tools
detekt
[warning] 58-58: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
#!/bin/sh | ||
sudo systemctl start docker.service | ||
|
||
sudo docker network create cheese_network | ||
|
||
sudo docker run -d \ | ||
--name elasticsearch \ | ||
--network cheese_network \ | ||
-e discovery.type=single-node \ | ||
-e xpack.security.enabled=true \ | ||
-e ELASTIC_USERNAME=elastic \ | ||
-e ELASTIC_PASSWORD=elastic \ | ||
--health-cmd="curl http://localhost:9200/_cluster/health" \ | ||
--health-interval=10s \ | ||
--health-timeout=5s \ | ||
--health-retries=10 \ | ||
-p 9200:9200 \ | ||
docker.elastic.co/elasticsearch/elasticsearch:8.12.1 | ||
|
||
sudo docker run -d \ | ||
--name postgres \ | ||
--network cheese_network \ | ||
-e POSTGRES_PASSWORD=postgres \ | ||
--health-cmd="pg_isready" \ | ||
--health-interval=10s \ | ||
--health-timeout=5s \ | ||
--health-retries=5 \ | ||
-p 5432:5432 \ | ||
postgres | ||
echo "Wait for 5 seconds please..." | ||
sleep 5 | ||
sudo docker exec -i postgres bash << EOF | ||
sed -i -e 's/max_connections = 100/max_connections = 1000/' /var/lib/postgresql/data/postgresql.conf | ||
sed -i -e 's/shared_buffers = 128MB/shared_buffers = 2GB/' /var/lib/postgresql/data/postgresql.conf | ||
EOF | ||
sudo docker restart --time 0 postgres | ||
|
||
sudo docker pull ghcr.io/sageseekersociety/cheese-backend-dev:dev # Update local image | ||
sudo docker run -d \ | ||
--name cheese_legacy \ | ||
--network cheese_network \ | ||
-p 3000:3000 \ | ||
-e PORT=3000 \ | ||
-e JWT_SECRET="test-secret" \ | ||
-e PRISMA_DATABASE_URL="postgresql://postgres:postgres@postgres:5432/postgres?schema=public&connection_limit=16" \ | ||
-e ELASTICSEARCH_NODE=http://elasticsearch:9200/ \ | ||
-e ELASTICSEARCH_AUTH_USERNAME=elastic \ | ||
-e ELASTICSEARCH_AUTH_PASSWORD=elastic \ | ||
-e FILE_UPLOAD_PATH=/app/uploads \ | ||
-e DEFAULT_AVATAR_NAME=default.jpg \ | ||
-e EMAIL_SMTP_HOST=smtp.example.com \ | ||
-e EMAIL_SMTP_PORT=587 \ | ||
-e EMAIL_SMTP_SSL_ENABLE=true \ | ||
-e [email protected] \ | ||
-e EMAIL_SMTP_PASSWORD=a_super_strong_password \ | ||
-e EMAIL_DEFAULT_FROM="No Reply <[email protected]>" \ | ||
ghcr.io/sageseekersociety/cheese-backend-dev:dev \ | ||
bash -c ' | ||
if [ ! -f "FLAG_INIT" ]; then | ||
touch FLAG_INIT | ||
pnpm prisma db push | ||
fi | ||
pnpm start | ||
' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, the script is well-structured and follows a logical sequence of steps to set up the required dependencies using Docker. Here are a few suggestions for improvement:
-
Consider using a custom PostgreSQL configuration file or environment variables to set the
max_connections
andshared_buffers
parameters, instead of modifying the configuration directly inside the container. This will make the setup more maintainable and easier to update in the future. -
The flag file used to conditionally run database migrations is created inside the container, which means it will be lost if the container is removed or recreated. Consider using a volume mount or a separate script to manage the database migrations, so that the flag file persists across container restarts.
-
The script uses hardcoded values for the Docker image tags and environment variables. Consider using variables or a configuration file to make the script more maintainable and flexible. This will allow you to easily update the versions or configurations without modifying the script itself.
@Before( | ||
"@within(org.springframework.web.bind.annotation.RestController) || " + | ||
"@within(org.springframework.stereotype.Controller)") | ||
fun checkAuthorization(joinPoint: JoinPoint) { | ||
val method = (joinPoint.signature as MethodSignature).method | ||
val guardAnnotation: Guard? = method.getAnnotation<Guard>(Guard::class.java) | ||
val noAuthAnnotation: NoAuth? = method.getAnnotation<NoAuth>(NoAuth::class.java) | ||
if (noAuthAnnotation == null) { | ||
if (guardAnnotation == null) | ||
throw NoGuardOrNoAuthAnnotationException( | ||
joinPoint.target.javaClass.name, | ||
joinPoint.signature.name, | ||
) | ||
|
||
var resourceId: IdType? = null | ||
val parameterAnnotations = method.parameterAnnotations | ||
for (i in parameterAnnotations.indices) { | ||
for (annotation in parameterAnnotations[i]) { | ||
if (annotation is ResourceId) { | ||
if (resourceId != null) { | ||
throw DuplicatedResourceIdAnnotationException( | ||
joinPoint.target.javaClass.name, | ||
joinPoint.signature.name, | ||
) | ||
} | ||
val arg = joinPoint.args[i] | ||
if (arg !is IdType) { | ||
throw ResourceIdTypeMismatchException( | ||
joinPoint.target.javaClass.name, | ||
joinPoint.signature.name, | ||
) | ||
} | ||
resourceId = arg | ||
} | ||
} | ||
} | ||
|
||
val token: String? = | ||
(RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes) | ||
.request | ||
.getHeader("Authorization") | ||
|
||
authorizationService.audit( | ||
token, | ||
guardAnnotation.action, | ||
guardAnnotation.resourceType, | ||
resourceId, | ||
) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider refactoring the checkAuthorization
method for better readability and maintainability.
The checkAuthorization
method correctly implements the authorization logic. However, consider the following suggestions to improve the code:
- Break down the method into smaller methods to enhance readability and maintainability. For example, you can extract the logic for checking the
@ResourceId
annotation into a separate method. - Make the exception messages more descriptive to aid in debugging. Include details like the expected annotation or type in the message.
- Clarify if the
AuthorizationService.audit
method handles null values forresourceId
. If not, consider adding a null check before calling the method.
fun login(username: String, password: String): String { | ||
val client = ClientBuilder.newClient() | ||
val target = client.target(applicationConfig.legacyUrl).path("/users/auth/login") | ||
val request = | ||
""" | ||
{ | ||
"username": "$username", | ||
"password": "$password" | ||
} | ||
""" | ||
val response = target.request().post(Entity.entity(request, MediaType.APPLICATION_JSON)) | ||
val result = JSONObject(response.readEntity(String::class.java)) | ||
try { | ||
return result.getJSONObject("data").getString("accessToken") | ||
} catch (e: Exception) { | ||
throw RuntimeException("Failed to login: $result") | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle the swallowed exception in the login
method.
The login
method makes a REST API call to the legacy system for user login and handles exceptions by throwing a RuntimeException
with the error details. However, the caught exception is swallowed, which could lead to loss of the original exception.
To handle the swallowed exception, apply this diff:
try {
return result.getJSONObject("data").getString("accessToken")
} catch (e: Exception) {
- throw RuntimeException("Failed to login: $result")
+ throw RuntimeException("Failed to login: $result", e)
}
This will include the original exception as the cause of the thrown RuntimeException
, preserving the stack trace and original exception details.
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
fun login(username: String, password: String): String { | |
val client = ClientBuilder.newClient() | |
val target = client.target(applicationConfig.legacyUrl).path("/users/auth/login") | |
val request = | |
""" | |
{ | |
"username": "$username", | |
"password": "$password" | |
} | |
""" | |
val response = target.request().post(Entity.entity(request, MediaType.APPLICATION_JSON)) | |
val result = JSONObject(response.readEntity(String::class.java)) | |
try { | |
return result.getJSONObject("data").getString("accessToken") | |
} catch (e: Exception) { | |
throw RuntimeException("Failed to login: $result") | |
} | |
} | |
fun login(username: String, password: String): String { | |
val client = ClientBuilder.newClient() | |
val target = client.target(applicationConfig.legacyUrl).path("/users/auth/login") | |
val request = | |
""" | |
{ | |
"username": "$username", | |
"password": "$password" | |
} | |
""" | |
val response = target.request().post(Entity.entity(request, MediaType.APPLICATION_JSON)) | |
val result = JSONObject(response.readEntity(String::class.java)) | |
try { | |
return result.getJSONObject("data").getString("accessToken") | |
} catch (e: Exception) { | |
throw RuntimeException("Failed to login: $result", e) | |
} | |
} |
Tools
detekt
[warning] 67-67: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
} catch (e: TokenExpiredException) { | ||
throw TokenExpiredError() | ||
} catch (e: JWTVerificationException) { | ||
throw InvalidTokenError() | ||
} catch (e: JacksonException) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Address the swallowed exceptions to avoid losing the original exceptions.
The static analysis tool suggests that the caught exceptions JWTVerificationException
and JacksonException
are swallowed, which could lead to loss of the original exceptions.
Consider logging the caught exceptions or re-throwing them as custom exceptions to preserve the original exception details. For example:
} catch (e: JWTVerificationException) {
logger.error("Invalid token", e)
throw InvalidTokenError(e)
} catch (e: JacksonException) {
logger.error("Error parsing token payload", e)
throw RuntimeException("The token is valid, but the payload of the token is not a TokenPayload object. This is either a bug or a malicious attack.", e)
}
Also applies to: 95-97
Tools
detekt
[warning] 93-93: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 95-95: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
Summary by CodeRabbit
New Features
AuthorizationInterceptor
for centralized request authorization handling.WebMvcConfig
to configure Spring MVC with the new interceptor.AuthorizationService
for managing JWT-based authorization.CustomAuthLogics
for flexible custom authorization logic management.Guard
,NoAuth
, andResourceId
annotations for enhanced security controls.AuthorizationInterceptor
andAuthorizationService
.Bug Fixes
Documentation
MockK
.Chores