Skip to content

User reg with GitHub profile data#4#98

Open
ghost wants to merge 4 commits into
mainfrom
UserRegWithGithubProfileData#4
Open

User reg with GitHub profile data#4#98
ghost wants to merge 4 commits into
mainfrom
UserRegWithGithubProfileData#4

Conversation

@ghost
Copy link
Copy Markdown

@ghost ghost commented Jan 7, 2025

Created logic to get Github-user data and save that information in variables. Then creating a new user in database based on the values we have from our GitHub-user data. As of now, we can create a new user but if we try to create an another one with same email address, the projekt will crash. You need to remove the created user from database (user table) to try it again.

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced user authentication with GitHub OAuth2 integration
    • Implemented automatic user creation and management
  • Improvements

    • Added support for handling user data persistence
    • Expanded user repository capabilities with JPA repository
    • Configured default values for user email and password
  • Configuration Updates

    • Updated OAuth2 client registration scopes
    • Modified server header strategy

These changes improve the application's user management and authentication processes, providing a more robust and flexible user experience.

Fredrick Kristoffersson added 3 commits January 7, 2025 15:56
@ghost ghost linked an issue Jan 7, 2025 that may be closed by this pull request
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 7, 2025

Walkthrough

The pull request introduces enhancements to the user management system, focusing on OAuth2 authentication and user creation. The changes include modifications to the LoginController, UserService, User entity, and UserRepository. A new id field is added to the User entity, and the repository now extends JpaRepository. The application properties are updated to specify GitHub OAuth2 scopes, and the database schema is modified to provide default values for email and password columns.

Changes

File Change Summary
src/main/java/.../controller/LoginController.java Added UserService dependency and enhanced index method to handle user creation and existence checks
src/main/java/.../user/UserService.java Added existsById() and save() methods for user management
src/main/java/.../user/entity/User.java Added id field with @Id and @GeneratedValue annotations
src/main/java/.../user/repository/UserRepository.java Switched from ListCrudRepository to JpaRepository
src/main/resources/application.properties Added GitHub OAuth2 scope and reinforced forward headers strategy
src/main/resources/schema.sql Added default values for email and password columns

Sequence Diagram

sequenceDiagram
    participant OAuth2 as OAuth2 Provider
    participant LoginController as Login Controller
    participant UserService as User Service
    participant UserRepository as User Repository
    participant Database as Database

    OAuth2->>LoginController: Authenticate User
    LoginController->>UserService: Check User Existence
    UserService->>UserRepository: existsById(userId)
    UserRepository-->>UserService: User Exists/Not Exists
    alt User Does Not Exist
        LoginController->>UserService: Create New User
        UserService->>UserRepository: save(user)
        UserRepository->>Database: Insert User
    else User Exists
        LoginController->>LoginController: Log User Exists
    end
Loading

Poem

🐰 A Rabbit's OAuth Tale 🔐

With scopes and IDs, we dance and twirl,
User creation in a magical swirl
Default values, a safety net
Authentication without a fret!
Code hops along, smooth and light 🚀


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?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jan 7, 2025

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🔭 Outside diff range comments (2)
src/main/java/org/fungover/system2024/user/entity/User.java (2)

Line range hint 23-24: Follow Java naming conventions.

Use camelCase instead of snake_case for field names in Java classes.

-private String first_name;
+private String firstName;

-private String last_name;
+private String lastName;

Also applies to: 28-29


Line range hint 48-63: Update equals() and add hashCode() method.

The equals() method should include the id field for proper entity comparison, and a corresponding hashCode() method should be implemented.

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    User user = (User) o;

+   if (id != null ? !id.equals(user.id) : user.id != null) return false;
-   if (!first_name.equals(user.first_name)) return false;
-   if (!last_name.equals(user.last_name)) return false;
+   if (!firstName.equals(user.firstName)) return false;
+   if (!lastName.equals(user.lastName)) return false;
    if (!email.equals(user.email)) return false;
    return password.equals(user.password);
}

+@Override
+public int hashCode() {
+    int result = id != null ? id.hashCode() : 0;
+    result = 31 * result + firstName.hashCode();
+    result = 31 * result + lastName.hashCode();
+    result = 31 * result + email.hashCode();
+    result = 31 * result + password.hashCode();
+    return result;
+}
🧹 Nitpick comments (4)
src/main/java/org/fungover/system2024/controller/LoginController.java (2)

18-19: Use Constructor Injection Instead of Field Injection

Using constructor injection is recommended over field injection (@Autowired on fields) as it promotes immutability, better testability, and follows best practices in Spring.

You can modify the code as follows:

@Controller
public class LoginController {
    private final UserService userService;
    private final Logger logger = LoggerFactory.getLogger(LoginController.class);

    public LoginController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/")
    public String index(@AuthenticationPrincipal OAuth2User principal, Model model) {
        // existing code...
    }
}

63-63: Add Error Handling for User Creation

Currently, there is no error handling around userService.save(user);. If the save operation fails (e.g., due to database constraints), the application may crash.

Wrap the save operation in a try-catch block to handle potential exceptions:

try {
    userService.save(user);
    logger.info("New user created with email '{}'", user.getEmail());
} catch (Exception e) {
    logger.error("Failed to create new user: {}", e.getMessage());
    // handle the error appropriately
}
src/main/java/org/fungover/system2024/user/entity/User.java (1)

Line range hint 31-34: Consider case-insensitive email handling.

Since email addresses are case-insensitive, consider adding a PrePersist/PreUpdate hook to normalize email addresses to lowercase to prevent duplicate entries that differ only in case.

@Size (max = 100)
@NotBlank
@Email
@Column(name = "email", nullable = false, unique = true)
private String email;

+@PrePersist
+@PreUpdate
+private void prepareEmail() {
+    if (email != null) {
+        email = email.toLowerCase();
+    }
+}
src/main/resources/schema.sql (1)

7-8: Reconsider default values for required fields.

Using default values for email and password fields might mask validation issues and create security concerns. Consider:

  1. Removing default values and enforcing proper validation
  2. Using a more secure default password if defaults are necessary
  3. Adding a boolean flag to identify auto-generated credentials
-    email    VARCHAR(255) NOT NULL DEFAULT 'unkwown@mailaddress.something',
-    password VARCHAR(255) NOT NULL DEFAULT 'unknown_password',
+    email    VARCHAR(255) NOT NULL,
+    password VARCHAR(255) NOT NULL,
+    is_temporary_credentials BOOLEAN NOT NULL DEFAULT FALSE,
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69a7e9d and c0a1b8a.

📒 Files selected for processing (6)
  • src/main/java/org/fungover/system2024/controller/LoginController.java (3 hunks)
  • src/main/java/org/fungover/system2024/user/UserService.java (2 hunks)
  • src/main/java/org/fungover/system2024/user/entity/User.java (1 hunks)
  • src/main/java/org/fungover/system2024/user/repository/UserRepository.java (1 hunks)
  • src/main/resources/application.properties (1 hunks)
  • src/main/resources/schema.sql (1 hunks)
🔇 Additional comments (2)
src/main/java/org/fungover/system2024/user/entity/User.java (1)

17-19: LGTM! Proper ID configuration.

The ID field is correctly configured with auto-generation strategy.

src/main/resources/application.properties (1)

21-22: LGTM! Appropriate OAuth2 configuration.

The GitHub OAuth2 scopes are correctly configured with the minimum required permissions for user profile data access.

Comment on lines +36 to +66
if (!userService.existsById(userId)) {
User user = new User();

if(principal.getAttribute("email") !=null) {
user.setEmail(principal.getAttribute("email"));
} else {
user.setEmail("unknown@example.something");
}

if(principal.getAttribute("name") != null) {
user.setFirst_name(principal.getAttribute("name"));
} else {
user.setFirst_name("John");
}

if (principal.getAttribute("name") != null) {
user.setLast_name(principal.getAttribute("name"));
} else {
user.setLast_name("Doe");
}

if(principal.getAttribute("password") != null) {
user.setPassword(principal.getAttribute("password"));
} else {
user.setPassword("no password from this kid");
}

userService.save(user);

logger.info("New user created with ID '{}'", userId);
} else {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Check User Existence by Email Instead of ID

Relying on the OAuth2 provider's user ID may not be consistent across different providers or may not be unique in your system. Checking user existence by email ensures that duplicate accounts aren't created and respects uniqueness constraints on the email field.

Update the code to check for user existence by email:

String email = principal.getAttribute("email");
if (email != null && !userService.existsByEmail(email)) {
    // proceed to create user
} else {
    // handle existing user or missing email
}

Ensure that UserService and UserRepository have the existsByEmail method implemented.

Comment on lines +39 to +43
if(principal.getAttribute("email") !=null) {
user.setEmail(principal.getAttribute("email"));
} else {
user.setEmail("unknown@example.something");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle Missing Email Appropriately

Setting a default email like "unknown@example.something" when the email is not available can cause issues with uniqueness constraints and doesn't provide meaningful information.

Consider the following options:

  • Prompt the user to provide an email address.
  • Abort the registration process if the email is essential.
  • Modify the User entity to allow null emails if appropriate.

Ensure that the application handles cases where the email is missing without causing exceptions or invalid data entries.

Comment on lines +45 to +55
if(principal.getAttribute("name") != null) {
user.setFirst_name(principal.getAttribute("name"));
} else {
user.setFirst_name("John");
}

if (principal.getAttribute("name") != null) {
user.setLast_name(principal.getAttribute("name"));
} else {
user.setLast_name("Doe");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Properly Separate First Name and Last Name

Currently, both first_name and last_name are set to the same value from principal.getAttribute("name"), which likely contains the full name.

Refactor the code to split the full name:

String fullName = principal.getAttribute("name");
if (fullName != null) {
    String[] nameParts = fullName.split(" ", 2);
    user.setFirst_name(nameParts[0]);
    if (nameParts.length > 1) {
        user.setLast_name(nameParts[1]);
    } else {
        user.setLast_name("");
    }
} else {
    user.setFirst_name("John");
    user.setLast_name("Doe");
}

Alternatively, if the OAuth2 provider offers separate attributes for first name and last name (e.g., "given_name" and "family_name"), use those instead.

Comment on lines +57 to +61
if(principal.getAttribute("password") != null) {
user.setPassword(principal.getAttribute("password"));
} else {
user.setPassword("no password from this kid");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid Setting Password Directly and Handle Authentication Appropriately

OAuth2 providers typically do not supply a user's password. Setting a placeholder password like "no password from this kid" is insecure and may lead to security vulnerabilities.

Consider the following actions:

  • Remove the password handling in this context.
  • Modify the User entity to accommodate users authenticated via OAuth2 without requiring a password.
  • If a password is necessary for your application, consider generating a secure random password and prompting users to change it.

Example modification:

- if(principal.getAttribute("password") != null) {
-     user.setPassword(principal.getAttribute("password"));
- } else {
-     user.setPassword("no password from this kid");
- }

Instead, you might remove this block or adjust your User entity accordingly.

Comment on lines +36 to +37
if (!userService.existsById(userId)) {
User user = new User();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Verify User Identification Method

Using principal.getAttribute("id") as an Integer may not be reliable since the id attribute from OAuth2 providers can be a String or another data type. This might lead to ClassCastException at runtime.

Consider retrieving the user ID as a String:

- Integer userId = principal.getAttribute("id");
+ String userId = principal.getAttribute("id");

Ensure that the User entity and related methods are updated to accommodate a String type for the user ID.

Committable suggestion skipped: line range outside the PR's diff.

import org.springframework.data.repository.ListCrudRepository;

public interface UserRepository extends ListCrudRepository<User, Integer> {
public interface UserRepository extends JpaRepository<User, Integer> {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add Method to Check User Existence by Email

To support checking for existing users by email in the LoginController, add a method to the UserRepository.

Add the following method:

public interface UserRepository extends JpaRepository<User, String> {
    boolean existsByEmail(String email);
}

Note: Ensure that the User entity has the email field annotated with @Column(unique = true) if you want to enforce uniqueness at the database level.

Comment on lines +35 to +40
public boolean existsById(Integer userId) {
return userRepository.existsById(userId);
}
public void save(User user) {
userRepository.save(user);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Update Methods to Support Checking and Saving by Email

In line with checking user existence by email, add the existsByEmail method in UserService and adjust other methods if necessary.

Add the following method:

public boolean existsByEmail(String email) {
    return userRepository.existsByEmail(email);
}

Update the save method if you need to handle any additional logic related to email:

public void save(User user) {
    userRepository.save(user);
    log.info("User with email '{}' saved successfully.", user.getEmail());
}

import lombok.extern.slf4j.Slf4j;
import org.fungover.system2024.exception.ResourceNotFoundException;
import org.fungover.system2024.user.dto.UserDto;
import org.fungover.system2024.user.entity.User;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Ensure Consistent User ID Data Types Across the Application

Since the User entity uses Integer for the user ID, but OAuth2 might provide an ID as a String, consider updating the User entity and related repositories to use String for the user ID.

Update the User entity:

- private Integer id;
+ private String id;

Update the UserRepository and UserService accordingly to reflect this change.

Committable suggestion skipped: line range outside the PR's diff.

last_name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL DEFAULT 'unkwown@mailaddress.something',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix typo in default email value.

The word "unknown" is misspelled as "unkwown" in the default email value.

-    email    VARCHAR(255) NOT NULL DEFAULT 'unkwown@mailaddress.something',
+    email    VARCHAR(255) NOT NULL DEFAULT 'unknown@mailaddress.something',
📝 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.

Suggested change
email VARCHAR(255) NOT NULL DEFAULT 'unkwown@mailaddress.something',
email VARCHAR(255) NOT NULL DEFAULT 'unknown@mailaddress.something',

Copy link
Copy Markdown

@AnnaLenaO AnnaLenaO left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code is easy to read and follow. Very nice!
Coderabbitai suggests we can verify by user email in database, instead of user ID. That could also solve the problem with the project crashing if such a user already exists in database.

email VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL DEFAULT 'unkwown@mailaddress.something',
password VARCHAR(255) NOT NULL DEFAULT 'unknown_password',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest to remove DEFAULT 'unknown_password' due to security risk.
It might be possible to sync how the password it set, with som other implementation for the application.

import org.springframework.data.repository.ListCrudRepository;

public interface UserRepository extends ListCrudRepository<User, Integer> {
public interface UserRepository extends JpaRepository<User, Integer> {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this application it might be enough with extends ListCrudRepository or extends ListPagingAndSortingRepository, instead of the full etends JpaRepository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

User Registration with GitHub Profile Data

1 participant