From 4436cf5e525fd33cbbacfc5fc4d7acb89e408186 Mon Sep 17 00:00:00 2001 From: Michael Pointner Date: Sun, 29 Sep 2024 01:03:26 +0200 Subject: [PATCH 1/9] Use Modules --- beispielprojekt/build.gradle | 0 .../tgm}/controller/HelloWorldController.java | 2 +- build.gradle | 66 +++++++++++-------- server/build.gradle | 3 + .../src}/main/java/at/ac/tgm/Application.java | 0 .../java/at/ac/tgm/ServletInitializer.java | 0 .../main/resources/application.properties | 0 .../at/ac/tgm/ServerApplicationTests.java | 12 ++-- settings.gradle | 3 + 9 files changed, 50 insertions(+), 36 deletions(-) create mode 100644 beispielprojekt/build.gradle rename {src/main/java/at/ac/tgm/beispielprojekt => beispielprojekt/src/main/java/at/ac/tgm}/controller/HelloWorldController.java (90%) create mode 100644 server/build.gradle rename {src => server/src}/main/java/at/ac/tgm/Application.java (100%) rename {src => server/src}/main/java/at/ac/tgm/ServletInitializer.java (100%) rename {src => server/src}/main/resources/application.properties (100%) rename src/test/java/at/ac/tgm/DaBackendApplicationTests.java => server/src/test/java/at/ac/tgm/ServerApplicationTests.java (61%) diff --git a/beispielprojekt/build.gradle b/beispielprojekt/build.gradle new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/at/ac/tgm/beispielprojekt/controller/HelloWorldController.java b/beispielprojekt/src/main/java/at/ac/tgm/controller/HelloWorldController.java similarity index 90% rename from src/main/java/at/ac/tgm/beispielprojekt/controller/HelloWorldController.java rename to beispielprojekt/src/main/java/at/ac/tgm/controller/HelloWorldController.java index f0e493b..83a94a9 100644 --- a/src/main/java/at/ac/tgm/beispielprojekt/controller/HelloWorldController.java +++ b/beispielprojekt/src/main/java/at/ac/tgm/controller/HelloWorldController.java @@ -1,4 +1,4 @@ -package at.ac.tgm.beispielprojekt.controller; +package at.ac.tgm.controller; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; diff --git a/build.gradle b/build.gradle index d64d6c6..d658866 100644 --- a/build.gradle +++ b/build.gradle @@ -1,40 +1,48 @@ plugins { id 'java' id 'war' - id 'org.springframework.boot' version '3.3.3' id 'io.spring.dependency-management' version '1.1.6' + id 'org.springframework.boot' version '3.3.4' } -group = 'at.ac.tgm' -version = '0.0.1-SNAPSHOT' - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) +subprojects { + + apply plugin: 'java' + apply plugin: 'war' + apply plugin: 'org.springframework.boot' + apply plugin: 'io.spring.dependency-management' + + group = 'at.ac.tgm' + version = '0.0.1-SNAPSHOT' + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } } -} - -configurations { - compileOnly { - extendsFrom annotationProcessor + + configurations { + compileOnly { + extendsFrom annotationProcessor + } + } + + repositories { + mavenCentral() + } + + dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-web' + compileOnly 'org.projectlombok:lombok' + developmentOnly 'org.springframework.boot:spring-boot-devtools' + runtimeOnly 'com.h2database:h2' + annotationProcessor 'org.projectlombok:lombok' + providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } -} - -repositories { - mavenCentral() -} - -dependencies { - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.springframework.boot:spring-boot-starter-web' - compileOnly 'org.projectlombok:lombok' - developmentOnly 'org.springframework.boot:spring-boot-devtools' - runtimeOnly 'com.h2database:h2' - annotationProcessor 'org.projectlombok:lombok' - providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } tasks.named('test') { diff --git a/server/build.gradle b/server/build.gradle new file mode 100644 index 0000000..367e370 --- /dev/null +++ b/server/build.gradle @@ -0,0 +1,3 @@ +dependencies { + implementation project(':beispielprojekt') +} diff --git a/src/main/java/at/ac/tgm/Application.java b/server/src/main/java/at/ac/tgm/Application.java similarity index 100% rename from src/main/java/at/ac/tgm/Application.java rename to server/src/main/java/at/ac/tgm/Application.java diff --git a/src/main/java/at/ac/tgm/ServletInitializer.java b/server/src/main/java/at/ac/tgm/ServletInitializer.java similarity index 100% rename from src/main/java/at/ac/tgm/ServletInitializer.java rename to server/src/main/java/at/ac/tgm/ServletInitializer.java diff --git a/src/main/resources/application.properties b/server/src/main/resources/application.properties similarity index 100% rename from src/main/resources/application.properties rename to server/src/main/resources/application.properties diff --git a/src/test/java/at/ac/tgm/DaBackendApplicationTests.java b/server/src/test/java/at/ac/tgm/ServerApplicationTests.java similarity index 61% rename from src/test/java/at/ac/tgm/DaBackendApplicationTests.java rename to server/src/test/java/at/ac/tgm/ServerApplicationTests.java index 4fde07d..eb92c58 100644 --- a/src/test/java/at/ac/tgm/DaBackendApplicationTests.java +++ b/server/src/test/java/at/ac/tgm/ServerApplicationTests.java @@ -4,10 +4,10 @@ import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest -class DaBackendApplicationTests { - - @Test - void contextLoads() { - } - +class ServerApplicationTests { + + @Test + void contextLoads() { + } + } diff --git a/settings.gradle b/settings.gradle index ba23f5b..5e780af 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,4 @@ rootProject.name = 'DA-Backend' + +include 'server' +include 'beispielprojekt' From 47add450e82910b8151b25b09442c3a352e7eabd Mon Sep 17 00:00:00 2001 From: Michael Pointner Date: Thu, 10 Oct 2024 23:01:56 +0200 Subject: [PATCH 2/9] Implement AD LDAP Connection --- .gitignore | 2 + README.md | 34 +++++-- beispiel/build.gradle | 3 + .../tgm/controller/HelloWorldController.java | 46 +++++++++ beispielprojekt/build.gradle | 0 .../tgm/controller/HelloWorldController.java | 17 ---- build.gradle | 7 +- core/build.gradle | 24 +++++ .../java/at/ac/tgm/ad/entry/GroupEntry.java | 51 ++++++++++ .../java/at/ac/tgm/ad/entry/UserEntry.java | 96 +++++++++++++++++++ .../ac/tgm/ad/repository/GroupRepository.java | 12 +++ .../ac/tgm/ad/repository/UserRepository.java | 20 ++++ .../at/ac/tgm/ad/service/UserService.java | 63 ++++++++++++ .../java/at/ac/tgm/ad/util/EntryBase.java | 8 ++ .../src/main/java/at/ac/tgm/ad/util/Util.java | 14 +++ server/build.gradle | 9 +- .../src/main/java/at/ac/tgm/AdLdapConfig.java | 59 ++++++++++++ .../main/java/at/ac/tgm/SecurityConfig.java | 26 +++++ .../src/main/resources/application.properties | 4 + settings.gradle | 3 +- 20 files changed, 468 insertions(+), 30 deletions(-) create mode 100644 beispiel/build.gradle create mode 100644 beispiel/src/main/java/at/ac/tgm/controller/HelloWorldController.java delete mode 100644 beispielprojekt/build.gradle delete mode 100644 beispielprojekt/src/main/java/at/ac/tgm/controller/HelloWorldController.java create mode 100644 core/build.gradle create mode 100644 core/src/main/java/at/ac/tgm/ad/entry/GroupEntry.java create mode 100644 core/src/main/java/at/ac/tgm/ad/entry/UserEntry.java create mode 100644 core/src/main/java/at/ac/tgm/ad/repository/GroupRepository.java create mode 100644 core/src/main/java/at/ac/tgm/ad/repository/UserRepository.java create mode 100644 core/src/main/java/at/ac/tgm/ad/service/UserService.java create mode 100644 core/src/main/java/at/ac/tgm/ad/util/EntryBase.java create mode 100644 core/src/main/java/at/ac/tgm/ad/util/Util.java create mode 100644 server/src/main/java/at/ac/tgm/AdLdapConfig.java create mode 100644 server/src/main/java/at/ac/tgm/SecurityConfig.java diff --git a/.gitignore b/.gitignore index bd3712a..4ec735f 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,5 @@ out/ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* + +.env diff --git a/README.md b/README.md index 4c6e645..c2a7533 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,32 @@ Gemeinsames Spring Boot Backend für Diplomarbeitsprojekte Getestet mit: -- SDK Eclipse Temurin (AdoptOpenJDK HotSpot) 21.0.4 und Amazon Corretto 17.0.12 +- SDK Eclipse Temurin (AdoptOpenJDK HotSpot) 21.0.4 - Gradle 8.10.1 -## Enthaltene Spring Abhändigkeiten +Für die Active Directory LDAP Anbindung muss auf der Root eine `.env` Datei angelegt werden, welche Folgendes enthält: -- Spring Boot DevTools -- Lombok -- Spring Web -- Spring Data JPA -- H2 Database -- Validation \ No newline at end of file +``` +AD_USER=insertTGMEmailAdressHere +AD_PASSWORD=insertTGMPasswortHere +``` + +## Gradle Projektstruktur + +Die angedachte Struktur ist folgende: + +- Im **server** Modul wird + - der Tomcat gestartet + - im SecurityConfig die Pfade konfiguriert, bei denen ein Login notwendig ist. + - alle enthalten Projekte (und core) im `implementation project(':beispielprojekt')` importiert +- Das **beispiel** -Modul ist eine Vorlage für ein Modul, wo eine Diplomarbeit ihren spezifischen Code entwickeln soll. +- **core**: Alle Module importieren dies und hier soll auch der von mehreren Projekten genutzte Code wie z.B. Active Directory LDAP Anbindung reinkommen + +## Beispiel-API-Endpoints + +### Active Directory LDAP Anbindung + +- [Eingeloggter User](http://localhost:8080/beispielprojekt) +- [HIT-Schueler auflisten](http://localhost:8080/beispielprojekt/list/schueler) +- [Lehrer auflisten](http://localhost:8080/beispielprojekt/list/lehrer) +- [Person anhand des Nachnamen suchen](http://localhost:8080/beispielprojekt/find/Pointner) \ No newline at end of file diff --git a/beispiel/build.gradle b/beispiel/build.gradle new file mode 100644 index 0000000..378848a --- /dev/null +++ b/beispiel/build.gradle @@ -0,0 +1,3 @@ +dependencies { + implementation project(':core') +} \ No newline at end of file diff --git a/beispiel/src/main/java/at/ac/tgm/controller/HelloWorldController.java b/beispiel/src/main/java/at/ac/tgm/controller/HelloWorldController.java new file mode 100644 index 0000000..39d4b22 --- /dev/null +++ b/beispiel/src/main/java/at/ac/tgm/controller/HelloWorldController.java @@ -0,0 +1,46 @@ +package at.ac.tgm.controller; + +import at.ac.tgm.ad.entry.UserEntry; +import at.ac.tgm.ad.service.UserService; +import at.ac.tgm.ad.util.EntryBase; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.naming.Name; +import java.util.List; + +@RestController +@RequestMapping("/beispielprojekt") +public class HelloWorldController { + + @Autowired + private UserService userService; + + @GetMapping({"", "/"}) + public Authentication getAuthCurrentUser() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + return auth; + } + + @GetMapping("/list/lehrer") + public List listLehrer() { + return userService.listUserCNs(EntryBase.LEHRER); + } + + @GetMapping("/find/{surname}") + public ResponseEntity findUser(@PathVariable("surname") String surname) { + return ResponseEntity.of(userService.findBySurname(surname, true)); + } + + @GetMapping("/list/schueler") + public List entities() { + return userService.listUserDNs(EntryBase.SCHUELER_HIT); + } + +} diff --git a/beispielprojekt/build.gradle b/beispielprojekt/build.gradle deleted file mode 100644 index e69de29..0000000 diff --git a/beispielprojekt/src/main/java/at/ac/tgm/controller/HelloWorldController.java b/beispielprojekt/src/main/java/at/ac/tgm/controller/HelloWorldController.java deleted file mode 100644 index 83a94a9..0000000 --- a/beispielprojekt/src/main/java/at/ac/tgm/controller/HelloWorldController.java +++ /dev/null @@ -1,17 +0,0 @@ -package at.ac.tgm.controller; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/beispielprojekt") -public class HelloWorldController { - - @GetMapping - public ResponseEntity helloWorld() { - return ResponseEntity.ok("Hello World!"); - } - -} diff --git a/build.gradle b/build.gradle index d658866..7ee09fd 100644 --- a/build.gradle +++ b/build.gradle @@ -35,13 +35,14 @@ subprojects { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' - compileOnly 'org.projectlombok:lombok' + implementation 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'org.springframework.boot:spring-boot-starter-test' developmentOnly 'org.springframework.boot:spring-boot-devtools' + compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' - providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' - testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + } } diff --git a/core/build.gradle b/core/build.gradle new file mode 100644 index 0000000..6d34b0d --- /dev/null +++ b/core/build.gradle @@ -0,0 +1,24 @@ +plugins { + id 'java' +} + +group = 'at.ac.tgm' +version = 'unspecified' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + + implementation 'org.springframework.ldap:spring-ldap-core' + implementation 'org.springframework.security:spring-security-ldap' + implementation 'org.springframework.boot:spring-boot-starter-data-ldap' + implementation 'com.unboundid:unboundid-ldapsdk' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/core/src/main/java/at/ac/tgm/ad/entry/GroupEntry.java b/core/src/main/java/at/ac/tgm/ad/entry/GroupEntry.java new file mode 100644 index 0000000..c825f9b --- /dev/null +++ b/core/src/main/java/at/ac/tgm/ad/entry/GroupEntry.java @@ -0,0 +1,51 @@ +package at.ac.tgm.ad.entry; + +import at.ac.tgm.ad.util.EntryBase; +import lombok.*; +import org.springframework.ldap.odm.annotations.Attribute; +import org.springframework.ldap.odm.annotations.Entry; +import org.springframework.ldap.odm.annotations.Id; + +import javax.naming.Name; +import java.util.List; +import java.util.Set; + +@Entry( + base = EntryBase.GROUP, + objectClasses = { "group", "top" }) +@Getter @Setter @NoArgsConstructor @AllArgsConstructor @ToString +public class GroupEntry { + private @Id Name dn; + private @Attribute(name = "cn") String cn; + private @Attribute(name = "displayName") String displayName; + private @Attribute(name = "distinguishedName") String distinguishedName; + private @Attribute(name = "dSCorePropagationData") List dSCorePropagationData; + private @Attribute(name = "groupType") String groupType; + private @Attribute(name = "instanceType") String instanceType; + private @Attribute(name = "legacyExchangeDN") String legacyExchangeDN; + private @Attribute(name = "mail") String mail; + private @Attribute(name = "mailNickname") String mailNickname; + private @Attribute(name = "member") Set member; + private @Attribute(name = "memberOf") Name memberOf; + private @Attribute(name = "msExchGroupExternalMemberCount") String msExchGroupExternalMemberCount; + private @Attribute(name = "msExchGroupMemberCount") String msExchGroupMemberCount; + private @Attribute(name = "msExchPoliciesIncluded") List msExchPoliciesIncluded; + private @Attribute(name = "msExchRecipientDisplayType") String msExchRecipientDisplayType; + private @Attribute(name = "msExchRequireAuthToSendTo") String msExchRequireAuthToSendTo; + private @Attribute(name = "msExchUMDtmfMap") List msExchUMDtmfMap; + private @Attribute(name = "msExchVersion") String msExchVersion; + private @Attribute(name = "name") String name; + private @Attribute(name = "objectCategory") String objectCategory; + private @Attribute(name = "objectClass") List objectClass; + private @Attribute(name = "objectGUID") String objectGUID; + private @Attribute(name = "objectSid") String objectSid; + private @Attribute(name = "proxyAddresses") List proxyAddresses; + private @Attribute(name = "reportToOriginator") String reportToOriginator; + private @Attribute(name = "sAMAccountName") String sAMAccountName; + private @Attribute(name = "sAMAccountType") String sAMAccountType; + private @Attribute(name = "showInAddressBook") List showInAddressBook; + private @Attribute(name = "uSNChanged") String uSNChanged; + private @Attribute(name = "uSNCreated") String uSNCreated; + private @Attribute(name = "whenChanged") String whenChanged; + private @Attribute(name = "whenCreated") String whenCreated; +} diff --git a/core/src/main/java/at/ac/tgm/ad/entry/UserEntry.java b/core/src/main/java/at/ac/tgm/ad/entry/UserEntry.java new file mode 100644 index 0000000..20b32e9 --- /dev/null +++ b/core/src/main/java/at/ac/tgm/ad/entry/UserEntry.java @@ -0,0 +1,96 @@ +package at.ac.tgm.ad.entry; + +import at.ac.tgm.ad.util.EntryBase; +import lombok.*; +import org.springframework.ldap.odm.annotations.Attribute; +import org.springframework.ldap.odm.annotations.Entry; +import org.springframework.ldap.odm.annotations.Id; +import org.springframework.ldap.odm.annotations.Transient; + +import javax.naming.Name; +import java.util.List; +import java.util.Set; + +@Entry(base = EntryBase.LEHRER, objectClasses = {"user", "organizationalPerson", "person", "top"}) +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class UserEntry { + private @Id Name id; + private @Attribute(name = "memberOf") Set memberOf; + private @Transient Set groups; + private @Attribute(name = "cn") String cn; + private @Attribute(name = "sn") String sn; + private @Attribute(name = "accountExpires") String accountExpires; + private @Attribute(name = "badPasswordTime") String badPasswordTime; + private @Attribute(name = "badPwdCount") String badPwdCount; + private @Attribute(name = "codePage") String codePage; + private @Attribute(name = "countryCode") String countryCode; + private @Attribute(name = "displayName") String displayName; + private @Attribute(name = "distinguishedName") String distinguishedName; + private @Attribute(name = "dSCorePropagationData") List dSCorePropagationData; + private @Attribute(name = "employeeNumber") String employeeNumber; + private @Attribute(name = "employeeType") String employeeType; + private @Attribute(name = "extensionAttribute2") String extensionAttribute2; + private @Attribute(name = "extensionAttribute4") String extensionAttribute4; + private @Attribute(name = "extensionAttribute5") String extensionAttribute5; + private @Attribute(name = "givenName") String givenName; + private @Attribute(name = "homeDirectory") String homeDirectory; + private @Attribute(name = "homeDrive") String homeDrive; + private @Attribute(name = "homeMDB") String homeMDB; + private @Attribute(name = "info") String info; + private @Attribute(name = "instanceType") String instanceType; + private @Attribute(name = "l") String l; + private @Attribute(name = "lastLogoff") String lastLogoff; + private @Attribute(name = "lastLogon") String lastLogon; + private @Attribute(name = "lastLogonTimestamp") String lastLogonTimestamp; + private @Attribute(name = "legacyExchangeDN") String legacyExchangeDN; + private @Attribute(name = "logonCount") String logonCount; + private @Attribute(name = "mail") String mail; + private @Attribute(name = "mailNickname") String mailNickname; + private @Attribute(name = "mDBUseDefaults") String mDBUseDefaults; + private @Attribute(name = "mS-DS-ConsistencyGuid") String mSDSConsistencyGuid; + private @Attribute(name = "msDS-ExternalDirectoryObjectId") String msDSExternalDirectoryObjectId; + private @Attribute(name = "msExchArchiveQuota") String msExchArchiveQuota; + private @Attribute(name = "msExchArchiveWarnQuota") String msExchArchiveWarnQuota; + private @Attribute(name = "msExchCalendarLoggingQuota") String msExchCalendarLoggingQuota; + private @Attribute(name = "msExchDumpsterQuota") String msExchDumpsterQuota; + private @Attribute(name = "msExchDumpsterWarningQuota") String msExchDumpsterWarningQuota; + private @Attribute(name = "msExchELCMailboxFlags") String msExchELCMailboxFlags; + private @Attribute(name = "msExchHideFromAddressLists") String msExchHideFromAddressLists; + private @Attribute(name = "msExchHomeServerName") String msExchHomeServerName; + private @Attribute(name = "msExchMailboxGuid") String msExchMailboxGuid; + private @Attribute(name = "msExchMailboxSecurityDescriptor") String msExchMailboxSecurityDescriptor; + private @Attribute(name = "msExchMobileMailboxFlags") String msExchMobileMailboxFlags; + private @Attribute(name = "msExchPoliciesIncluded") String msExchPoliciesIncluded; + private @Attribute(name = "msExchRBACPolicyLink") String msExchRBACPolicyLink; + private @Attribute(name = "msExchRecipientDisplayType") String msExchRecipientDisplayType; + private @Attribute(name = "msExchRecipientTypeDetails") String msExchRecipientTypeDetails; + private @Attribute(name = "msExchSafeSendersHash") String msExchSafeSendersHash; + private @Attribute(name = "msExchSharingAnonymousIdentities") String msExchSharingAnonymousIdentities; + private @Attribute(name = "msExchTextMessagingState") List msExchTextMessagingState; + private @Attribute(name = "msExchUMDtmfMap") List msExchUMDtmfMap; + private @Attribute(name = "msExchUserAccountControl") String msExchUserAccountControl; + private @Attribute(name = "msExchUserCulture") String msExchUserCulture; + private @Attribute(name = "msExchVersion") String msExchVersion; + private @Attribute(name = "msExchWhenMailboxCreated") String msExchWhenMailboxCreated; + private @Attribute(name = "name") String name; + private @Attribute(name = "objectCategory") String objectCategory; + private @Attribute(name = "objectClass") List objectClass; + private @Attribute(name = "objectGUID") String objectGUID; + private @Attribute(name = "objectSid") String objectSid; + private @Attribute(name = "primaryGroupID") String primaryGroupID; + private @Attribute(name = "profilePath") String profilePath; + private @Attribute(name = "protocolSettings") String protocolSettings; + private @Attribute(name = "proxyAddresses") List proxyAddresses; + private @Attribute(name = "pwdLastSet") String pwdLastSet; + private @Attribute(name = "sAMAccountName") String sAMAccountName; + private @Attribute(name = "sAMAccountType") String sAMAccountType; + private @Attribute(name = "showInAddressBook") List showInAddressBook; + private @Attribute(name = "userAccountControl") String userAccountControl; + private @Attribute(name = "userPrincipalName") String userPrincipalName; + private @Attribute(name = "uSNChanged") String uSNChanged; + private @Attribute(name = "uSNCreated") String uSNCreated; +} diff --git a/core/src/main/java/at/ac/tgm/ad/repository/GroupRepository.java b/core/src/main/java/at/ac/tgm/ad/repository/GroupRepository.java new file mode 100644 index 0000000..b9b9349 --- /dev/null +++ b/core/src/main/java/at/ac/tgm/ad/repository/GroupRepository.java @@ -0,0 +1,12 @@ +package at.ac.tgm.ad.repository; + +import at.ac.tgm.ad.entry.GroupEntry; +import org.springframework.data.ldap.repository.LdapRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface GroupRepository extends LdapRepository { + Optional findByCn(String cn); +} diff --git a/core/src/main/java/at/ac/tgm/ad/repository/UserRepository.java b/core/src/main/java/at/ac/tgm/ad/repository/UserRepository.java new file mode 100644 index 0000000..c6a9602 --- /dev/null +++ b/core/src/main/java/at/ac/tgm/ad/repository/UserRepository.java @@ -0,0 +1,20 @@ +package at.ac.tgm.ad.repository; + +import at.ac.tgm.ad.entry.UserEntry; +import org.springframework.data.ldap.repository.LdapRepository; +import org.springframework.ldap.odm.annotations.Entry; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface UserRepository extends LdapRepository { + Optional findByCn(String cn); + Optional findBySn(String sn); + + /* + BUG: findAll() nicht nutzen, da das base-Attribute in der @Entry Annotation nicht beachtet wird und damit immer alle Nutzer auflistet werden: + https://github.com/spring-projects/spring-data-ldap/issues/446 + Stattdessen sollte findAll(LdapQuery ldapQuery) genutzt werden + */ +} \ No newline at end of file diff --git a/core/src/main/java/at/ac/tgm/ad/service/UserService.java b/core/src/main/java/at/ac/tgm/ad/service/UserService.java new file mode 100644 index 0000000..0926bd4 --- /dev/null +++ b/core/src/main/java/at/ac/tgm/ad/service/UserService.java @@ -0,0 +1,63 @@ +package at.ac.tgm.ad.service; + +import at.ac.tgm.ad.entry.GroupEntry; +import at.ac.tgm.ad.entry.UserEntry; +import at.ac.tgm.ad.repository.GroupRepository; +import at.ac.tgm.ad.repository.UserRepository; +import at.ac.tgm.ad.util.Util; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.ldap.core.AttributesMapper; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.odm.annotations.Entry; +import org.springframework.ldap.query.LdapQueryBuilder; +import org.springframework.stereotype.Service; + +import javax.naming.Name; +import javax.naming.ldap.LdapName; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +@Service +public class UserService { + @Autowired + private UserRepository userRepository; + @Autowired + private GroupRepository groupRepository; + @Autowired + private LdapTemplate ldapTemplate; + + public Optional findByCommonName(String cn, boolean loadGroups) { + Optional user = userRepository.findByCn(cn); + if (loadGroups) { + user.ifPresent(this::loadGroupMembers); + } + return user; + } + + public Optional findBySurname(String surname, boolean loadGroups) { + Optional user = userRepository.findBySn(surname); + if (loadGroups) { + user.ifPresent(this::loadGroupMembers); + } + return user; + } + + public List listUserCNs(String entryBase) { + return ldapTemplate.list(entryBase); + } + + public List listUserDNs(String entryBase) { + Entry entryAnnotation = UserEntry.class.getDeclaredAnnotation(Entry.class); + return ldapTemplate.search(LdapQueryBuilder.query().base(entryBase).where("objectclass").is(entryAnnotation.objectClasses()[0]), + (AttributesMapper) attrs -> new LdapName((String) attrs.get("distinguishedName").get())); + } + + private void loadGroupMembers(UserEntry lehrer) { + Set groups = + lehrer.getMemberOf().stream().map(member -> groupRepository.findByCn(Util.getCnFromName(member)).orElse(null)).filter(Objects::nonNull).collect(Collectors.toSet()); + lehrer.setGroups(groups); + } +} diff --git a/core/src/main/java/at/ac/tgm/ad/util/EntryBase.java b/core/src/main/java/at/ac/tgm/ad/util/EntryBase.java new file mode 100644 index 0000000..6b25374 --- /dev/null +++ b/core/src/main/java/at/ac/tgm/ad/util/EntryBase.java @@ -0,0 +1,8 @@ +package at.ac.tgm.ad.util; + +public class EntryBase { + public static final String GROUP = "OU=Groups"; + public static final String LEHRER = "OU=Lehrer,OU=People"; + public static final String SCHUELER = "OU=Schueler,OU=People"; + public static final String SCHUELER_HIT = "OU=HIT," + SCHUELER; +} diff --git a/core/src/main/java/at/ac/tgm/ad/util/Util.java b/core/src/main/java/at/ac/tgm/ad/util/Util.java new file mode 100644 index 0000000..d9ef788 --- /dev/null +++ b/core/src/main/java/at/ac/tgm/ad/util/Util.java @@ -0,0 +1,14 @@ +package at.ac.tgm.ad.util; + +import javax.naming.Name; + +public class Util { + public static String getCnFromName(Name name) { + String[] parts = name.get(name.size() - 1).split("="); + if (parts[0].equalsIgnoreCase("cn") && parts[1] != null) { + return parts[1]; + } else { + throw new IllegalArgumentException("Last part of the name is not a CN"); + } + } +} diff --git a/server/build.gradle b/server/build.gradle index 367e370..a347dbd 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -1,3 +1,10 @@ dependencies { - implementation project(':beispielprojekt') + implementation project(':core') + implementation project(':beispiel') + + implementation("me.paulschwarz:spring-dotenv:4.0.0") + implementation("org.springframework.ldap:spring-ldap-core") + implementation("org.springframework.security:spring-security-ldap") + implementation("org.springframework.boot:spring-boot-starter-data-ldap") + implementation("com.unboundid:unboundid-ldapsdk") } diff --git a/server/src/main/java/at/ac/tgm/AdLdapConfig.java b/server/src/main/java/at/ac/tgm/AdLdapConfig.java new file mode 100644 index 0000000..27b564e --- /dev/null +++ b/server/src/main/java/at/ac/tgm/AdLdapConfig.java @@ -0,0 +1,59 @@ +package at.ac.tgm; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.ldap.repository.config.EnableLdapRepositories; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider; + +import javax.naming.Name; +import java.io.IOException; +import java.util.Collections; + +@Configuration +@EnableLdapRepositories +public class AdLdapConfig { + + @Bean + ActiveDirectoryLdapAuthenticationProvider authenticationProvider() { + ActiveDirectoryLdapAuthenticationProvider authenticationProvider = new ActiveDirectoryLdapAuthenticationProvider("tgm.ac.at", "ldap://10.2.24.151/"); + authenticationProvider.setConvertSubErrorCodesToExceptions(true); + authenticationProvider.setUseAuthenticationRequestCredentials(true); + return authenticationProvider; + } + + @Bean + public AuthenticationManager authenticationManager(ActiveDirectoryLdapAuthenticationProvider adProvider) { + return new ProviderManager(Collections.singletonList(adProvider)); + } + + @Bean + public ObjectMapper registerObjectMapper(){ + ObjectMapper mapper = new ObjectMapper(); + SimpleModule module = new SimpleModule("MyNameSerializer"); + module.addSerializer(Name.class, new NameJsonSerializer()); + mapper.registerModule(module); + return mapper; + } + + class NameJsonSerializer extends StdSerializer { + public NameJsonSerializer() { + this(null); + } + + public NameJsonSerializer(Class t) { + super(t); + } + + @Override + public void serialize(Name value, JsonGenerator jgen, SerializerProvider provider) throws IOException { + jgen.writeString(value.toString()); + } + } +} diff --git a/server/src/main/java/at/ac/tgm/SecurityConfig.java b/server/src/main/java/at/ac/tgm/SecurityConfig.java new file mode 100644 index 0000000..2e7449b --- /dev/null +++ b/server/src/main/java/at/ac/tgm/SecurityConfig.java @@ -0,0 +1,26 @@ +package at.ac.tgm; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests((authorize) -> authorize + .anyRequest().authenticated() + ) + .formLogin(Customizer.withDefaults()) + .logout(Customizer.withDefaults()); + + return http.build(); + } + +} diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties index 4a4eed5..5e0970b 100644 --- a/server/src/main/resources/application.properties +++ b/server/src/main/resources/application.properties @@ -1 +1,5 @@ spring.application.name=DA-Backend +spring.ldap.urls=ldap://10.2.24.151/ +spring.ldap.base=OU=tgm,DC=tgm,DC=ac,DC=at +spring.ldap.username=${AD_USER} +spring.ldap.password=${AD_PASSWORD} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 5e780af..228b893 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,5 @@ rootProject.name = 'DA-Backend' include 'server' -include 'beispielprojekt' +include 'core' +include 'beispiel' From b6e779272537c13b1a3287cd31c09b5532dc9ff3 Mon Sep 17 00:00:00 2001 From: Michael Pointner Date: Thu, 10 Oct 2024 23:09:47 +0200 Subject: [PATCH 3/9] Add repository --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index 7ee09fd..79d4d80 100644 --- a/build.gradle +++ b/build.gradle @@ -5,6 +5,10 @@ plugins { id 'org.springframework.boot' version '3.3.4' } +repositories { + mavenCentral() +} + subprojects { apply plugin: 'java' From cca7f59648b3945981595dea1ca02eafcecca736 Mon Sep 17 00:00:00 2001 From: Michael Pointner Date: Thu, 10 Oct 2024 23:47:50 +0200 Subject: [PATCH 4/9] Fix bootWar --- build.gradle | 21 +++++++++------------ server/build.gradle | 14 +++++++++----- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/build.gradle b/build.gradle index 79d4d80..e5000b2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,24 +1,20 @@ plugins { id 'java' - id 'war' id 'io.spring.dependency-management' version '1.1.6' id 'org.springframework.boot' version '3.3.4' } -repositories { - mavenCentral() +allprojects{ + group = 'at.ac.tgm' + version = '0.0.1-SNAPSHOT' } subprojects { apply plugin: 'java' - apply plugin: 'war' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' - group = 'at.ac.tgm' - version = '0.0.1-SNAPSHOT' - java { toolchain { languageVersion = JavaLanguageVersion.of(21) @@ -40,6 +36,7 @@ subprojects { implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-tomcat' testImplementation 'org.springframework.boot:spring-boot-starter-test' developmentOnly 'org.springframework.boot:spring-boot-devtools' compileOnly 'org.projectlombok:lombok' @@ -48,8 +45,8 @@ subprojects { testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } -} - -tasks.named('test') { - useJUnitPlatform() -} + + tasks.named('test') { + useJUnitPlatform() + } +} \ No newline at end of file diff --git a/server/build.gradle b/server/build.gradle index a347dbd..033fe9e 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -1,10 +1,14 @@ +plugins { + id 'war' +} + dependencies { implementation project(':core') implementation project(':beispiel') - implementation("me.paulschwarz:spring-dotenv:4.0.0") - implementation("org.springframework.ldap:spring-ldap-core") - implementation("org.springframework.security:spring-security-ldap") - implementation("org.springframework.boot:spring-boot-starter-data-ldap") - implementation("com.unboundid:unboundid-ldapsdk") + implementation 'me.paulschwarz:spring-dotenv:4.0.0' + implementation 'org.springframework.ldap:spring-ldap-core' + implementation 'org.springframework.security:spring-security-ldap' + implementation 'org.springframework.boot:spring-boot-starter-data-ldap' + implementation 'com.unboundid:unboundid-ldapsdk' } From 40c4ae129908de6719718c34a74ac0684058eb6b Mon Sep 17 00:00:00 2001 From: Michael Pointner Date: Thu, 10 Oct 2024 23:58:52 +0200 Subject: [PATCH 5/9] Fix bootJar --- build.gradle | 4 ++++ server/build.gradle | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/build.gradle b/build.gradle index e5000b2..b84197d 100644 --- a/build.gradle +++ b/build.gradle @@ -49,4 +49,8 @@ subprojects { tasks.named('test') { useJUnitPlatform() } + + bootJar { + enabled = false + } } \ No newline at end of file diff --git a/server/build.gradle b/server/build.gradle index 033fe9e..e0401e9 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -12,3 +12,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-ldap' implementation 'com.unboundid:unboundid-ldapsdk' } + +bootJar { + enabled = true +} \ No newline at end of file From 0acb8e9adff2246b5bf887698d3e0f7a11586ae3 Mon Sep 17 00:00:00 2001 From: Michael Pointner Date: Fri, 11 Oct 2024 00:00:12 +0200 Subject: [PATCH 6/9] Fix bootJar 2 --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index b84197d..dd589f4 100644 --- a/build.gradle +++ b/build.gradle @@ -53,4 +53,8 @@ subprojects { bootJar { enabled = false } +} + +bootJar { + enabled = false } \ No newline at end of file From 7f307e33571ceb5d4cd2af545dfe57d1adb0563c Mon Sep 17 00:00:00 2001 From: Michael Pointner Date: Fri, 11 Oct 2024 00:16:51 +0200 Subject: [PATCH 7/9] Fix resolveProjectDependencies --- build.gradle | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index dd589f4..490cc81 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,6 @@ plugins { id 'java' + id 'war' id 'io.spring.dependency-management' version '1.1.6' id 'org.springframework.boot' version '3.3.4' } @@ -7,11 +8,9 @@ plugins { allprojects{ group = 'at.ac.tgm' version = '0.0.1-SNAPSHOT' -} - -subprojects { apply plugin: 'java' + apply plugin: 'war' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' @@ -53,8 +52,8 @@ subprojects { bootJar { enabled = false } -} - -bootJar { - enabled = false + + bootWar { + enabled = false + } } \ No newline at end of file From 572248bd495054770bb94a8ec75157c735511e0a Mon Sep 17 00:00:00 2001 From: Michael Pointner Date: Fri, 11 Oct 2024 00:22:20 +0200 Subject: [PATCH 8/9] Fix resolveProjectDependencies --- build.gradle | 9 ++------- server/build.gradle | 5 ++--- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index 490cc81..e3882dd 100644 --- a/build.gradle +++ b/build.gradle @@ -49,11 +49,6 @@ allprojects{ useJUnitPlatform() } - bootJar { - enabled = false - } - - bootWar { - enabled = false - } + bootJar.enabled = false + bootWar.enabled = false } \ No newline at end of file diff --git a/server/build.gradle b/server/build.gradle index e0401e9..552e03b 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -13,6 +13,5 @@ dependencies { implementation 'com.unboundid:unboundid-ldapsdk' } -bootJar { - enabled = true -} \ No newline at end of file +bootJar.enabled = true +bootWar.enabled = true \ No newline at end of file From 5e3194de44b591d5baacf8ba64a95e44dcf885c7 Mon Sep 17 00:00:00 2001 From: Michael Pointner Date: Fri, 11 Oct 2024 00:34:27 +0200 Subject: [PATCH 9/9] Extract url and domain from to code to application.properties --- server/src/main/java/at/ac/tgm/AdLdapConfig.java | 9 +++++++-- server/src/main/resources/application.properties | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/at/ac/tgm/AdLdapConfig.java b/server/src/main/java/at/ac/tgm/AdLdapConfig.java index 27b564e..1bc675c 100644 --- a/server/src/main/java/at/ac/tgm/AdLdapConfig.java +++ b/server/src/main/java/at/ac/tgm/AdLdapConfig.java @@ -15,14 +15,19 @@ import javax.naming.Name; import java.io.IOException; import java.util.Collections; +import org.springframework.beans.factory.annotation.Value; @Configuration @EnableLdapRepositories public class AdLdapConfig { + @Value("${spring.ldap.urls}") + private String url; + @Value("${spring.ldap.domain}") + private String domain; @Bean ActiveDirectoryLdapAuthenticationProvider authenticationProvider() { - ActiveDirectoryLdapAuthenticationProvider authenticationProvider = new ActiveDirectoryLdapAuthenticationProvider("tgm.ac.at", "ldap://10.2.24.151/"); + ActiveDirectoryLdapAuthenticationProvider authenticationProvider = new ActiveDirectoryLdapAuthenticationProvider(domain, url); authenticationProvider.setConvertSubErrorCodesToExceptions(true); authenticationProvider.setUseAuthenticationRequestCredentials(true); return authenticationProvider; @@ -42,7 +47,7 @@ public ObjectMapper registerObjectMapper(){ return mapper; } - class NameJsonSerializer extends StdSerializer { + static class NameJsonSerializer extends StdSerializer { public NameJsonSerializer() { this(null); } diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties index 5e0970b..f3ca97b 100644 --- a/server/src/main/resources/application.properties +++ b/server/src/main/resources/application.properties @@ -1,4 +1,5 @@ spring.application.name=DA-Backend +spring.ldap.domain=tgm.ac.at spring.ldap.urls=ldap://10.2.24.151/ spring.ldap.base=OU=tgm,DC=tgm,DC=ac,DC=at spring.ldap.username=${AD_USER}