Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Backend host port for the normal containerized app
APP_PORT=8080

# Backend host port for live-reload dev mode
APP_DEV_PORT=8081
9 changes: 4 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
FROM gradle:8-jdk-alpine AS build
FROM gradle:8.5-jdk17 AS build
WORKDIR /app
COPY . .
RUN ./gradlew clean bootJar
RUN ./gradlew --no-daemon clean bootJar


FROM openjdk:17
FROM eclipse-temurin:17-jre
USER nobody
WORKDIR /app
COPY --from=build /app/build/libs/*.jar app.jar

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
ENTRYPOINT ["java", "-jar", "app.jar"]
112 changes: 98 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,103 @@ Autobank is a Kotlin Spring Boot application designed for managing financial rec

## 🚀 Quick Start

### Running the Application
Use one of these three modes depending on what you want to test.

1. **Clone the repository**
```bash
git clone https://github.com/appKom/gnocchitony.git
cd gnocchitony
```
### Copy-Paste Cheatsheet

2. **Configure environment variables**
```bash
# Local backend (no token required)
./gradlew bootRun

3. **Run the application**
```bash
./gradlew bootRun
```
# Docker backend + database
docker compose up -d --build

The application will start on `http://localhost:8080`
# Docker backend + database with live reload
docker compose --profile dev up -d dev-database backend-dev

# Test production auth mode locally (token required)
./gradlew bootRun --args='--environment=prod'

# Stop Docker services
docker compose down
docker compose --profile dev down
```

### 1. Local Development (fastest feedback)

```bash
./gradlew bootRun
```

What happens:

- Runs backend on port 8080 from your machine.
- Uses `application-local.properties`.
- `environment=dev` disables JWT auth, so you can call APIs without a token.

Useful URLs:

- API: `http://localhost:8080`
- Swagger UI: `http://localhost:8080/swagger-ui/index.html`

### 2. Docker Development (backend + database)

```bash
docker compose up -d --build
```

What happens:

- Starts MySQL and backend in containers.
- Backend is on `http://localhost:8080` (or `APP_PORT` from `.env`).
- MySQL is internal to Docker network (not published on host).
- Uses local profile (`SPRING_PROFILES_ACTIVE=local`), so `environment=dev` and no JWT required.

Stop:

```bash
docker compose down
```

### 3. Docker Development with Live Reload (Kotlin changes)

```bash
docker compose --profile dev up -d dev-database backend-dev
```

What happens:

- Runs backend via Gradle `bootRun --continuous` inside container.
- Mounts your project into the container.
- Code changes trigger rebuild/restart automatically.
- Backend is exposed on `http://localhost:8081` by default (`APP_DEV_PORT`).

Stop:

```bash
docker compose --profile dev down
```

### 4. Test Production Auth Locally (require token)

If you want to test real JWT/Auth0 behavior, run with `environment=prod`.

Local JVM:

```bash
./gradlew bootRun --args='--environment=prod'
```

Docker:

```bash
ENVIRONMENT=prod docker compose up -d --build
```

What changes in prod mode:

- All protected endpoints require Bearer token.
- Auth0 issuer/audience validation is enforced.

## 📋 Features

Expand Down Expand Up @@ -58,11 +139,15 @@ All API endpoints require authentication via Bearer token:
Authorization: Bearer <access_token>
```

**Note**: In development mode (`environment=dev`), security is disabled for easier testing.
Authentication mode is controlled by `environment`:

- `environment=prod`: JWT/Auth0 is required.
- Any non-prod value (for example `environment=dev`): token auth is disabled for easier local development.

## 🛠️ Development

### Project Structure

```
src/main/kotlin/com/example/autobank/
├── controller/ # REST controllers
Expand Down Expand Up @@ -94,4 +179,3 @@ For complete API documentation, see [docs/api-routes.md](docs/api-routes.md).
## 📄 License

This project is part of NTNU Online's application suite.

53 changes: 49 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,57 @@ services:
image: mysql:8.0
container_name: spring-local-mysq
environment:
MYSQL_ROOT_PASSWORD: gLp(43<Ze1d9
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
MYSQL_ROOT_HOST: "%"
MYSQL_DATABASE: autobankdevdb
ports:
- "3306:3306"
expose:
- "3306"
volumes:
- mysql_dev_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 20

backend:
build:
context: .
dockerfile: Dockerfile
container_name: autobank-backend
depends_on:
dev-database:
condition: service_healthy
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://dev-database:3306/autobankdevdb?serverTimezone=UTC
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: ""
SPRING_PROFILES_ACTIVE: local
PORT: "8080"
ports:
- "${APP_PORT:-8080}:8080"

backend-dev:
image: gradle:8.5-jdk17
container_name: autobank-backend-dev
profiles: ["dev"]
working_dir: /app
depends_on:
dev-database:
condition: service_healthy
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://dev-database:3306/autobankdevdb?serverTimezone=UTC
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: ""
SPRING_PROFILES_ACTIVE: local
PORT: "8080"
volumes:
- .:/app
- gradle_cache:/home/gradle/.gradle
command: ["./gradlew", "--no-daemon", "bootRun", "--continuous"]
ports:
- "${APP_DEV_PORT:-8081}:8080"

volumes:
mysql_dev_data:
mysql_dev_data:
gradle_cache:
15 changes: 15 additions & 0 deletions src/main/kotlin/com/example/autobank/security/SecurityConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class SecurityConfig() {
@Value("\${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private val issuer: String = String()

@Value("\${environment}")
private val environment: String = String()

@Bean
fun jwtDecoder(): JwtDecoder {
val jwtDecoder = JwtDecoders.fromOidcIssuerLocation(issuer) as NimbusJwtDecoder
Expand All @@ -50,6 +53,18 @@ class SecurityConfig() {
@Bean
@Throws(Exception::class)
fun filterChain(http: HttpSecurity): SecurityFilterChain {

if (environment != "prod") {
http
.cors { it.configurationSource(corsConfigurationSource()) }
.csrf { it.disable() }
.authorizeHttpRequests { auth ->
auth.anyRequest().permitAll()
}

return http.build()
}

http
.cors { it.configurationSource(corsConfigurationSource()) } // Add this
.csrf { it.disable() } // Typically disabled for APIs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class AuthenticationService(
@Value("\${auth0.domain}") private val domain: String,
@Value("\${environment}") private val environment: String,
@Value("\${api.base.domain}") private val apiBaseDomain: String,
@Value("\${dev.user.sub:local-dev-user}") private val localDevUserSub: String,
@Value("\${dev.user.full-name:Local Dev User}") private val localDevUserFullName: String,
) {

private val restTemplate = RestTemplate()
Expand All @@ -53,13 +55,15 @@ class AuthenticationService(
return if (authentication is JwtAuthenticationToken) {
val token = authentication.token
token.getClaim("sub")
} else if (environment != "prod") {
localDevUserSub
} else {
""
}
}

fun getFullName(): String {
return "";
return if (environment != "prod") localDevUserFullName else ""
}

fun getAccessToken(): String {
Expand Down
Loading