Skip to content

Commit

Permalink
Merge pull request #59 from szymonpoltorak/devel
Browse files Browse the repository at this point in the history
Sprint 7 - merge
  • Loading branch information
szymonpoltorak authored Mar 4, 2024
2 parents ca020c2 + 24d6cea commit 4eccbc4
Show file tree
Hide file tree
Showing 115 changed files with 1,847 additions and 334 deletions.
68 changes: 60 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,76 @@
# Corn

Team project with aim to create an open source developer collaboration platform for small teams.
Team project with aim to create an open source developer collaboration platform for small teams. We are creating
application that is built of few components.

* Frontend client,
* Backend server,
* Keycloak Auth Server,
* Nginx Reverse Proxy,
* Postgres database.

Whole application has been made in spirit of clean code and architecture. We are thinking about security and performance
best practices. Project is deeply tested on backend site and uses Github Actions for CI/CD.

## How to run

Application uses docker compose technology to run all components. To run application you have to install docker.

We have to different ways to start application.

1. Using docker compose command directly

* Clone repository
* Go to root directory
* If you want development environment run

```bash
docker compose -f docker-compose.dev.yml up --build
```

* If you want production environment run

```bash
docker compose -f docker-compose.prod.yml up --build
```

2. Using scripts. We provided linux shell scripts that are able to run application in development and production mode. Just pick the right one and run it.
For example if you want to run application in development mode run:

```bash
cd scripts/dev

./run.dev.sh
```

## Tech stack

1. Frontend

* Angular 16,
* Angular 17,
* Angular Material,
* Angular PWA,
* Keycloak Service,
* Nginx,
* NgIcons,
* TypeScript,
* Scss,
* Angular Material
* Docker,
* Tailwind CSS,
* Scss

2. Backend

* Java 17,
* Spring Boot 3.15,
* Spring Boot 3.1.5,
* Spring Data Jpa,
* Spring Security,
* Spring Boot Validation,
* Hibernate,
* Postgres,
* JavaMailSender,
* OAuth2,
* OAuth2 Resource Server,
* Mockito,
* JUnit,
* H2 Database,
* KeyCloak,
* Mapstruct,
* Lombok
* Lombok.
11 changes: 11 additions & 0 deletions auth-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Corn Keycloak Auth Server

This is a Keycloak server that is used to authenticate users for the Corn project. We included here two parts.

* Keycloak Initializer,
* Keycloak Theme

First one is used for initializing configuration of keycloak application and second one is providing custom theme for
keycloak pages like login, register, etc.

Theme has been made using FreeMarker template engine and Tailwind CSS.
2 changes: 1 addition & 1 deletion auth-server/keycloak-initializer/Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

FROM alpine:3.19.1

RUN apk update --no-cache && apk add --no-cache gradle
RUN apk update --no-cache && apk upgrade --no-cache && apk add --no-cache gradle

WORKDIR /keycloak-initializer

Expand Down
4 changes: 2 additions & 2 deletions auth-server/keycloak-initializer/Dockerfile.prod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM alpine:3.19.1 AS build

RUN apk update --no-cache && apk add --no-cache gradle
RUN apk update --no-cache && apk upgrade --no-cache && apk add --no-cache gradle

WORKDIR /keycloak-initializer

Expand All @@ -18,7 +18,7 @@ FROM alpine:3.19.1

WORKDIR /keycloak-initializer

RUN apk update --no-cache && apk add --no-cache openjdk17-jre
RUN apk update --no-cache && apk upgrade --no-cache && apk add --no-cache openjdk17-jre

COPY --from=build /keycloak-initializer/keycloak-initializer.jar .

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,24 @@
import java.util.List;
import java.util.Map;

public class ExternalConfig {
public final class ExternalConfig {

public static final String KCCFG_OVERRIDE_EXISTING = System.getenv("KCCFG_OVERRIDE_EXISTING");
private static final String KCCFG_OVERRIDE_EXISTING = System.getenv("KCCFG_OVERRIDE_EXISTING");

public static final String KCCFG_LOGIN_THEME_NAME = System.getenv("KCCFG_LOGIN_THEME_NAME");

public static final String KC_SERVER_URL = System.getenv("KC_SERVER_URL");
private static final String KC_SERVER_URL = System.getenv("KC_SERVER_URL");

public static final String GITHUB_CLIENT_ID = System.getenv("GITHUB_CLIENT_ID");
public static final String GITHUB_CLIENT_SECRET = System.getenv("GITHUB_CLIENT_SECRET");
private static final String GITHUB_CLIENT_ID = System.getenv("GITHUB_CLIENT_ID");
private static final String GITHUB_CLIENT_SECRET = System.getenv("GITHUB_CLIENT_SECRET");

public static final String GOOGLE_CLIENT_ID = System.getenv("GOOGLE_CLIENT_ID");
public static final String GOOGLE_CLIENT_SECRET = System.getenv("GOOGLE_CLIENT_SECRET");
private static final String GOOGLE_CLIENT_ID = System.getenv("GOOGLE_CLIENT_ID");
private static final String GOOGLE_CLIENT_SECRET = System.getenv("GOOGLE_CLIENT_SECRET");

//
private ExternalConfig() {
}

public static Keycloak getAdmin() {
static Keycloak getAdmin() {
if(KC_SERVER_URL == null) {
throw new IllegalStateException("env KC_SERVER_URL not found");
}
Expand All @@ -38,41 +39,37 @@ public static Keycloak getAdmin() {
.build();
}

public static boolean shouldOverrideExistingConfiguration() {
return KCCFG_OVERRIDE_EXISTING != null && KCCFG_OVERRIDE_EXISTING.equalsIgnoreCase("true");
static boolean shouldOverrideExistingConfiguration() {
return "true".equalsIgnoreCase(KCCFG_OVERRIDE_EXISTING);
}

public static List<IdentityProviderRepresentation> getIdentityProviders() {
ArrayList<IdentityProviderRepresentation> providers = new ArrayList<>();
if(GITHUB_CLIENT_ID != null && !GITHUB_CLIENT_ID.isBlank() && GITHUB_CLIENT_SECRET != null && !GITHUB_CLIENT_SECRET.isBlank()) {
providers.add(new IdentityProviderRepresentation() {{
setAlias("github");
setEnabled(true);
setProviderId("github");
setConfig(Map.of(
"clientId", GITHUB_CLIENT_ID,
"clientSecret", GITHUB_CLIENT_SECRET
));
// V czy wymusić aktualizację danych profilu po pierwszym zalogowaniu przez oauth2
//setUpdateProfileFirstLoginMode("on");
// V ??
//setFirstBrokerLoginFlowAlias("first broker login");
// V napis na ekranie logowania
//setDisplayName("Zaloguj sie przez ~GITHUB~");
}});
List<IdentityProviderRepresentation> providers = new ArrayList<>(3);

if(isIdentityProviderConfigured(GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET)) {
providers.add(newInstance("github", GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET));
}
if(GOOGLE_CLIENT_ID != null && !GOOGLE_CLIENT_ID.isBlank() && GOOGLE_CLIENT_SECRET != null && !GOOGLE_CLIENT_SECRET.isBlank()) {
providers.add(new IdentityProviderRepresentation() {{
setAlias("google");
setEnabled(true);
setProviderId("google");
setConfig(Map.of(
"clientId", GOOGLE_CLIENT_ID,
"clientSecret", GOOGLE_CLIENT_SECRET
));
}});
if(isIdentityProviderConfigured(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET)) {
providers.add(newInstance("google", GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET));
}
return Collections.unmodifiableList(providers);
}

private static IdentityProviderRepresentation newInstance(String provider, String clientId, String clientSecret) {
IdentityProviderRepresentation idp = new IdentityProviderRepresentation();

idp.setAlias(provider);
idp.setEnabled(true);
idp.setProviderId(provider);
idp.setConfig(Map.of(
"clientId", clientId,
"clientSecret", clientSecret
));
return idp;
}

private static boolean isIdentityProviderConfigured(String clientId, String clientSecret) {
return clientId != null && !clientId.isBlank() && clientSecret != null && !clientSecret.isBlank();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,31 @@

import keycloakinitializer.realm.corn.CornRealm;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.representations.idm.RealmRepresentation;

import java.util.Optional;
public final class Main {

public class Main {
private Main() {
}

public static void main(String[] args) {
Keycloak admin = ExternalConfig.getAdmin();
CornRealm realm = new CornRealm();
Optional<RealmRepresentation> existingRealm = admin.realms().findAll().stream()

admin
.realms()
.findAll()
.stream()
.filter(r -> r.getRealm().equals(realm.getRealm()))
.findFirst();
if(existingRealm.isPresent()) {
if(!ExternalConfig.shouldOverrideExistingConfiguration()) {
System.out.println("Configured not to override existing configuration => Exiting");
return;
}
admin.realms().realm(existingRealm.get().getRealm()).remove();
}
admin.realms().create(realm);
.findFirst()
.ifPresentOrElse(
r -> {
if (!ExternalConfig.shouldOverrideExistingConfiguration()) {
throw new UnsupportedOperationException("Configured not to override existing configuration => Exiting");
}
admin.realms().realm(r.getRealm()).remove();
},
() -> admin.realms().create(realm)
);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,22 @@

import java.util.List;

public class CornClient extends ClientRepresentation {
final class CornClient extends ClientRepresentation {
private static final String CLIENT_ID = "Corn";
private static final String OPENID_CONNECT = "openid-connect";

public CornClient() {
setClientId("Corn");
CornClient() {
setClientId(CLIENT_ID);
setEnabled(true);
setRedirectUris(List.of(
"http://localhost/*",
"http://localhost:4200/*",
"http://localhost:80/*"
));
setDirectAccessGrantsEnabled(true);
setProtocol(OPENID_CONNECT);
setPublicClient(true);
setFullScopeAllowed(true);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,24 @@

import java.util.List;

public class CornRealm extends RealmRepresentation {
public final class CornRealm extends RealmRepresentation {
private static final String REALM_NAME = "Corn";
private static final String DEFAULT_SIGNATURE_ALGORITHM = "ES512";

public CornRealm() {
setRealm("Corn");
setRealm(REALM_NAME);
setEnabled(true);
setRegistrationAllowed(true);
setClients(List.of(new CornClient()));
setIdentityProviders(ExternalConfig.getIdentityProviders());
setRevokeRefreshToken(true);
setRememberMe(true);
setBruteForceProtected(true);
setDefaultSignatureAlgorithm(DEFAULT_SIGNATURE_ALGORITHM);

if(ExternalConfig.KCCFG_LOGIN_THEME_NAME != null) {
setLoginTheme(ExternalConfig.KCCFG_LOGIN_THEME_NAME);
}
}

public enum Role {
ADMIN, USER;
}

}
2 changes: 1 addition & 1 deletion corn-backend/Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

FROM alpine:3.19.1

RUN apk update --no-cache && apk add --no-cache gradle
RUN apk update --no-cache && apk upgrade --no-cache && apk add --no-cache gradle

WORKDIR /home/corn

Expand Down
4 changes: 2 additions & 2 deletions corn-backend/Dockerfile.prod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM alpine:3.19.1 AS build

RUN apk update --no-cache && apk add --no-cache gradle
RUN apk update --no-cache && apk upgrade --no-cache && apk add --no-cache gradle

WORKDIR /home/corn

Expand All @@ -16,7 +16,7 @@ RUN mv build/libs/corn-backend-0.0.1-SNAPSHOT.jar corn.jar

FROM alpine:3.19.1

RUN apk update --no-cache && apk add --no-cache openjdk17-jre
RUN apk update --no-cache && apk upgrade --no-cache && apk add --no-cache openjdk17-jre

WORKDIR /corn

Expand Down
27 changes: 22 additions & 5 deletions corn-backend/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
# Corn Backend

Backend for Corn collaboration plaform made in Java.
Application has been made following the best clean code patterns and tried to maintain high code test coverage.
It is a simple backend application that provides REST API for managing users and their roles.
It is secured with KeyCloak. Application brings high level of abstraction, security and is easy to extend.

## How to run it

1. You can run application using gradle:

```shell
./gradlew bootRun
```

2. To build a docker image and run it, use the `docker-compose.prod.yml` or `docker-compose.dev.yml` file in parent directory.

## Technology stack

* Java 17,
* Spring Boot 3.15,
* Spring Boot 3.1.5,
* Spring Data Jpa,
* Spring Security,
* Spring Boot Validation,
* Hibernate,
* Postgres,
* JavaMailSender,
* OAuth2,
* OAuth2 Resource Server,
* Mockito,
* JUnit,
* H2 Database,
* KeyCloak,
* Mapstruct,
* Lombok
* Lombok.
Loading

0 comments on commit 4eccbc4

Please sign in to comment.