-
Notifications
You must be signed in to change notification settings - Fork 3
Phase 1A: Identity & Access — carddemo-auth Spring Boot module #210
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
Open
devin-ai-integration
wants to merge
5
commits into
devin/1781281050-modernization-blueprint
Choose a base branch
from
devin/1781281980-phase1a-auth-service
base: devin/1781281050-modernization-blueprint
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
05903b2
feat: Phase 1A - Identity & Access Spring Boot service (carddemo-auth)
devin-ai-integration[bot] 4a388ec
fix: address Devin Review findings
devin-ai-integration[bot] 5cb12e5
fix: add @Transactional, DataIntegrity handler, and password size limit
devin-ai-integration[bot] 0f82207
fix: prevent timing side-channel and remove default JWT secret
devin-ai-integration[bot] c8ca565
fix: use valid BCrypt hash for timing-attack mitigation
devin-ai-integration[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,3 +6,4 @@ batch-app/markers/* | |
| /.idea/ | ||
| .DS_Store | ||
| **/.DS_Store | ||
| **/target/ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| # CardDemo Auth Service | ||
|
|
||
| Identity & Access Management microservice for the CardDemo system, migrated from legacy COBOL programs to Spring Boot 3.x. | ||
|
|
||
| ## Legacy COBOL Mapping | ||
|
|
||
| | COBOL Program | Function | Java Equivalent | | ||
| |---|---|---| | ||
| | `COSGN00C` | User sign-on (authentication) | `POST /auth/login` — validates credentials, returns JWT | | ||
| | `COUSR00C` | List users | `GET /users` — returns all users (admin) or own profile (user) | | ||
| | `COUSR01C` | Add user | `POST /users` — creates a new user (admin only) | | ||
| | `COUSR02C` | Update user | `PUT /users/{id}` — updates user fields | | ||
| | `COUSR03C` | Delete user | `DELETE /users/{id}` — removes a user (admin only) | | ||
|
|
||
| ### Data Model Mapping (Copybook `CSUSR01Y`) | ||
|
|
||
| | COBOL Field | PIC | Java Field | Type | Notes | | ||
| |---|---|---|---|---| | ||
| | `SEC-USR-ID` | `X(08)` | `userId` | `String(8)` | Primary key | | ||
| | `SEC-USR-FNAME` | `X(20)` | `firstName` | `String(20)` | | | ||
| | `SEC-USR-LNAME` | `X(20)` | `lastName` | `String(20)` | | | ||
| | `SEC-USR-PWD` | `X(08)` | `password` | `String` | BCrypt hash (replaces plaintext COBOL storage) | | ||
| | `SEC-USR-TYPE` | `X(01)` | `userType` | `Enum(ADMIN,USER)` | 'A' → ADMIN, 'U' → USER | | ||
| | `SEC-USR-FILLER` | `X(23)` | — | — | Not migrated (padding) | | ||
|
|
||
| ## Architecture | ||
|
|
||
| - **Framework**: Spring Boot 3.2.x | ||
| - **Security**: Spring Security + JWT (stateless) | ||
| - **Persistence**: Spring Data JPA with PostgreSQL (H2 for tests) | ||
| - **Password Storage**: BCrypt (replacing COBOL plaintext `PIC X(08)`) | ||
|
|
||
| ## API Endpoints | ||
|
|
||
| ### Authentication | ||
| ``` | ||
| POST /auth/login | ||
| Body: { "userId": "ADMIN01", "password": "secret" } | ||
| Response: { "token": "eyJ...", "userId": "ADMIN01", "userType": "ADMIN" } | ||
| ``` | ||
|
|
||
| ### User Management (requires Bearer token) | ||
| ``` | ||
| GET /users — List users (admin: all, user: own profile only) | ||
| POST /users — Create user (admin only) | ||
| PUT /users/{id} — Update user (admin: any, user: own profile only) | ||
| DELETE /users/{id} — Delete user (admin only) | ||
| ``` | ||
|
|
||
| ## Role-Based Access Control | ||
|
|
||
| | Role | GET /users | POST /users | PUT /users/{id} | DELETE /users/{id} | | ||
| |---|---|---|---|---| | ||
| | ADMIN | All users | Yes | Any user | Yes | | ||
| | USER | Own profile only | No | Own profile only (cannot change role) | No | | ||
|
|
||
| ## Running Locally | ||
|
|
||
| ### Prerequisites | ||
| - Java 17+ | ||
| - Maven 3.8+ | ||
| - PostgreSQL 14+ (or use the H2 test profile) | ||
|
|
||
| ### Build & Test | ||
| ```bash | ||
| cd services/carddemo-auth | ||
| mvn clean package | ||
| ``` | ||
|
|
||
| ### Run | ||
| ```bash | ||
| # Set environment variables for PostgreSQL connection | ||
| export DB_USERNAME=carddemo | ||
| export DB_PASSWORD=carddemo | ||
| export JWT_SECRET=your-256-bit-secret-key-here | ||
|
|
||
| mvn spring-boot:run | ||
| ``` | ||
|
|
||
| ## Security Improvements Over Legacy | ||
|
|
||
| 1. **Password Hashing**: BCrypt replaces plaintext COBOL `PIC X(08)` passwords | ||
| 2. **Stateless Auth**: JWT tokens replace CICS session-based security | ||
| 3. **Role Enforcement**: Programmatic RBAC replaces RACF group checks | ||
| 4. **Input Validation**: Bean Validation replaces manual COBOL field checks | ||
| 5. **Audit-Ready**: Structured JSON responses replace BMS screen output |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
|
|
||
| <parent> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter-parent</artifactId> | ||
| <version>3.2.5</version> | ||
| <relativePath/> | ||
| </parent> | ||
|
|
||
| <groupId>com.cardemo</groupId> | ||
| <artifactId>carddemo-auth</artifactId> | ||
| <version>1.0.0-SNAPSHOT</version> | ||
| <name>CardDemo Auth Service</name> | ||
| <description>Identity and Access Management service for CardDemo - migrated from COBOL COSGN00C/COUSR* programs</description> | ||
|
|
||
| <properties> | ||
| <java.version>17</java.version> | ||
| <jjwt.version>0.12.5</jjwt.version> | ||
| </properties> | ||
|
|
||
| <dependencies> | ||
| <!-- Spring Boot Starters --> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter-web</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter-data-jpa</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter-security</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter-validation</artifactId> | ||
| </dependency> | ||
|
|
||
| <!-- JWT --> | ||
| <dependency> | ||
| <groupId>io.jsonwebtoken</groupId> | ||
| <artifactId>jjwt-api</artifactId> | ||
| <version>${jjwt.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>io.jsonwebtoken</groupId> | ||
| <artifactId>jjwt-impl</artifactId> | ||
| <version>${jjwt.version}</version> | ||
| <scope>runtime</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>io.jsonwebtoken</groupId> | ||
| <artifactId>jjwt-jackson</artifactId> | ||
| <version>${jjwt.version}</version> | ||
| <scope>runtime</scope> | ||
| </dependency> | ||
|
|
||
| <!-- PostgreSQL Driver --> | ||
| <dependency> | ||
| <groupId>org.postgresql</groupId> | ||
| <artifactId>postgresql</artifactId> | ||
| <scope>runtime</scope> | ||
| </dependency> | ||
|
|
||
| <!-- H2 for testing --> | ||
| <dependency> | ||
| <groupId>com.h2database</groupId> | ||
| <artifactId>h2</artifactId> | ||
| <scope>test</scope> | ||
| </dependency> | ||
|
|
||
| <!-- Test dependencies --> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter-test</artifactId> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.springframework.security</groupId> | ||
| <artifactId>spring-security-test</artifactId> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| </dependencies> | ||
|
|
||
| <build> | ||
| <plugins> | ||
| <plugin> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-maven-plugin</artifactId> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
| </project> |
12 changes: 12 additions & 0 deletions
12
services/carddemo-auth/src/main/java/com/cardemo/auth/CardDemoAuthApplication.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package com.cardemo.auth; | ||
|
|
||
| import org.springframework.boot.SpringApplication; | ||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
|
|
||
| @SpringBootApplication | ||
| public class CardDemoAuthApplication { | ||
|
|
||
| public static void main(String[] args) { | ||
| SpringApplication.run(CardDemoAuthApplication.class, args); | ||
| } | ||
| } |
50 changes: 50 additions & 0 deletions
50
services/carddemo-auth/src/main/java/com/cardemo/auth/config/SecurityConfig.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| package com.cardemo.auth.config; | ||
|
|
||
| import com.cardemo.auth.security.JwtAuthenticationFilter; | ||
| import jakarta.servlet.http.HttpServletResponse; | ||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.http.HttpMethod; | ||
| import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; | ||
| import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
| import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
| import org.springframework.security.config.http.SessionCreationPolicy; | ||
| import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
| import org.springframework.security.crypto.password.PasswordEncoder; | ||
| import org.springframework.security.web.SecurityFilterChain; | ||
| import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
|
|
||
| @Configuration | ||
| @EnableWebSecurity | ||
| @EnableMethodSecurity | ||
| public class SecurityConfig { | ||
|
|
||
| private final JwtAuthenticationFilter jwtAuthenticationFilter; | ||
|
|
||
| public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) { | ||
| this.jwtAuthenticationFilter = jwtAuthenticationFilter; | ||
| } | ||
|
|
||
| @Bean | ||
| public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { | ||
| http | ||
| .csrf(csrf -> csrf.disable()) | ||
| .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) | ||
| .exceptionHandling(ex -> ex | ||
| .authenticationEntryPoint((request, response, authException) -> | ||
| response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized")) | ||
| ) | ||
| .authorizeHttpRequests(auth -> auth | ||
| .requestMatchers(HttpMethod.POST, "/auth/login").permitAll() | ||
| .anyRequest().authenticated() | ||
| ) | ||
| .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); | ||
|
|
||
| return http.build(); | ||
| } | ||
|
|
||
| @Bean | ||
| public PasswordEncoder passwordEncoder() { | ||
| return new BCryptPasswordEncoder(); | ||
| } | ||
| } | ||
28 changes: 28 additions & 0 deletions
28
services/carddemo-auth/src/main/java/com/cardemo/auth/controller/AuthController.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package com.cardemo.auth.controller; | ||
|
|
||
| import com.cardemo.auth.dto.LoginRequest; | ||
| import com.cardemo.auth.dto.LoginResponse; | ||
| import com.cardemo.auth.service.AuthService; | ||
| import jakarta.validation.Valid; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.PostMapping; | ||
| import org.springframework.web.bind.annotation.RequestBody; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| @RestController | ||
| @RequestMapping("/auth") | ||
| public class AuthController { | ||
|
|
||
| private final AuthService authService; | ||
|
|
||
| public AuthController(AuthService authService) { | ||
| this.authService = authService; | ||
| } | ||
|
|
||
| @PostMapping("/login") | ||
| public ResponseEntity<LoginResponse> login(@Valid @RequestBody LoginRequest request) { | ||
| LoginResponse response = authService.authenticate(request); | ||
| return ResponseEntity.ok(response); | ||
| } | ||
| } |
47 changes: 47 additions & 0 deletions
47
services/carddemo-auth/src/main/java/com/cardemo/auth/controller/GlobalExceptionHandler.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| package com.cardemo.auth.controller; | ||
|
|
||
| import com.cardemo.auth.service.AuthService.AuthenticationException; | ||
| import com.cardemo.auth.service.UserService.UserAlreadyExistsException; | ||
| import com.cardemo.auth.service.UserService.UserNotFoundException; | ||
| import org.springframework.dao.DataIntegrityViolationException; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.MethodArgumentNotValidException; | ||
| import org.springframework.web.bind.annotation.ExceptionHandler; | ||
| import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
|
|
||
| import java.util.Map; | ||
|
|
||
| @RestControllerAdvice | ||
| public class GlobalExceptionHandler { | ||
|
|
||
| @ExceptionHandler(AuthenticationException.class) | ||
| public ResponseEntity<Map<String, String>> handleAuthenticationException(AuthenticationException ex) { | ||
| return ResponseEntity.status(HttpStatus.UNAUTHORIZED) | ||
| .body(Map.of("error", ex.getMessage())); | ||
| } | ||
|
|
||
| @ExceptionHandler(UserNotFoundException.class) | ||
| public ResponseEntity<Map<String, String>> handleUserNotFound(UserNotFoundException ex) { | ||
| return ResponseEntity.status(HttpStatus.NOT_FOUND) | ||
| .body(Map.of("error", ex.getMessage())); | ||
| } | ||
|
|
||
| @ExceptionHandler(UserAlreadyExistsException.class) | ||
| public ResponseEntity<Map<String, String>> handleUserAlreadyExists(UserAlreadyExistsException ex) { | ||
| return ResponseEntity.status(HttpStatus.CONFLICT) | ||
| .body(Map.of("error", ex.getMessage())); | ||
| } | ||
|
|
||
| @ExceptionHandler(DataIntegrityViolationException.class) | ||
| public ResponseEntity<Map<String, String>> handleDataIntegrity(DataIntegrityViolationException ex) { | ||
| return ResponseEntity.status(HttpStatus.CONFLICT) | ||
| .body(Map.of("error", "Resource conflict")); | ||
| } | ||
|
|
||
| @ExceptionHandler(MethodArgumentNotValidException.class) | ||
| public ResponseEntity<Map<String, String>> handleValidation(MethodArgumentNotValidException ex) { | ||
| return ResponseEntity.status(HttpStatus.BAD_REQUEST) | ||
| .body(Map.of("error", "Validation failed: " + (ex.getFieldError() != null ? ex.getFieldError().getDefaultMessage() : ex.getMessage()))); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
🚩 No brute-force / rate-limiting protection on the login endpoint
The
POST /auth/loginendpoint ispermitAll()(line 38 of SecurityConfig) with no rate limiting or account lockout mechanism. While this isn't strictly a code bug (it's a missing feature), it's worth flagging for a security-focused auth service: an attacker can make unlimited login attempts. The original COBOLCOSGN00Cprogram typically had lockout after N failed attempts. Consider adding Spring's rate limiting, a lockout counter, or delegating to an API gateway rate limiter.Was this helpful? React with 👍 or 👎 to provide feedback.
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.
Acknowledged — rate limiting / account lockout is a valid hardening concern. The original COBOL
COSGN00Cdid enforce lockout after failed attempts. This can be added in a follow-up (e.g., a failed-attempt counter in theuserstable or an API gateway rate limiter like Bucket4j or Spring Cloud Gateway filters). Keeping this PR scoped to the core auth functionality for now.