Compare commits

110 Commits

Author SHA1 Message Date
Developer 02
3832351dd1 add Jenkinsfile
Some checks failed
AppStd/WorkFlow/pipeline/head There was a failure building this commit
2025-05-22 16:03:12 +02:00
Developer 02
99237cbecc refactor(IISProfile): Verzeichnis aktualisiert, um die Namenskonventionen für digitale Daten anzuwenden 2025-03-25 15:38:33 +01:00
Developer 02
cb2edffe91 chore: Separate IIS-Konfiguration für .Net 7 und 8 hinzufügen 2025-03-24 16:12:39 +01:00
Developer 02
4d3768248e chore: IIS-Veröffentlichungsprofile getrennt nach Framework hinzufügen 2025-03-13 12:06:56 +01:00
Developer 02
fb38bc1fd4 chore(API): Hinzufügen von .net 7-Unterstützung für API 2025-03-13 10:12:55 +01:00
Developer 02
10b557374d chore: Hinzufügen von .net 7-Unterstützung für Domäne, Infrastruktur und Anwendung 2025-03-13 10:04:40 +01:00
Developer 02
f266e6728f chore: Upgrade auf DigitalData.Auth.Client 1.3.3 2025-03-13 09:29:07 +01:00
Developer 02
3373fceef3 chore(API): Hochgestuft auf 1.1.0 2025-03-11 17:17:08 +01:00
Developer 02
f7eaa0f7de refactor: IssuerSigningKeyResolver wurde aktualisiert, um die Konfiguration über serviceProvider anstelle eines separaten öffentlichen Schlüssels zu ermöglichen. 2025-03-11 16:22:54 +01:00
Developer 02
d5b1ee41a0 fix: Arranged auth Authentication Scheme 2025-03-11 10:48:04 +01:00
Developer 02
c3f5d90b6a refactor(PlaceholderAuthController): Update auf Login nur über Body und ohne Cookie 2025-03-11 10:32:28 +01:00
Developer 02
753eb18b71 updated(AuthController): Aktualisiert, um als Platzhalter für auth api in swagger zu funktionieren.
- umbenennen PlaceholderAuthController
2025-03-11 10:17:27 +01:00
Developer 02
17d8373739 feat: JwtBearerEvents hinzugefügt, um Token aus Cookie oder Query-String lesen zu können 2025-03-11 09:36:32 +01:00
Developer 02
d6ccc10244 fix: Aktualisiertes Präfix für den Namen der NLog-Protokolldatei als workFlow.API 2025-03-10 15:47:58 +01:00
Developer 02
3dccf82710 fix: Aktualisiertes Präfix für den Namen der NLog-Protokolldatei als workFlow.API 2025-03-10 15:36:42 +01:00
Developer 02
c7d8b67ccb refactor Aktualisierte AuthPublicKey-Konfiguration, die getrennt von AuthClaimPrams konfiguriert werden kann, um sie als JWT-Barriere-Konfiguration zu verwenden 2025-03-10 13:53:15 +01:00
Developer 02
b76043fa24 chore: Aktualisierung von DigitalData.Core.Application und UserManager.Application auf 2.0.0
- Aktualisiert auf Dienste als aktuelle Core.Application
2025-03-10 10:53:40 +01:00
Developer 02
e28f4560d6 Refactor: Inline AuthHubClient Optionen durch Konfigurationsabschnitt ersetzen 2025-03-10 09:48:10 +01:00
Developer 02
97d5156bbb feat(auth): Integration von AuthHubClient und JWT-basierter Authentifizierung
- Abhängigkeit `DigitalData.Auth.Client` hinzugefügt
- `AuthHubClient` mit konfigurierbarem öffentlichen Schlüssel für Authentifizierung integriert
- Cookie-basierte Authentifizierung durch JWT-Bearer-Authentifizierung ersetzt
- Token-Validierung so konfiguriert, dass dynamisch auflösbare Signaturschlüssel verwendet werden
2025-03-07 16:10:10 +01:00
Developer 02
40cf8f3f10 chore: Konfigurierte Paket-ID, Version, Firma, Produkt und Titel 2024-10-29 14:50:18 +01:00
Developer 02
a325d07c6b refactor: AuthController-Methoden optimieren und Login-Logik verbessern
- AuthController aktualisiert, um eine klarere Struktur zu implementieren.
- Login-Methode vereinfacht, um die Benutzerauthentifizierung direkt zu behandeln.
- Neue LoginById-Methode für den Benutzerlogin über ID eingeführt.
- Fehlerprotokollierung und -behandlung in den Login-Methoden verbessert.
- Überflüssigen Code entfernt und Lesbarkeit verbessert.
- TODO für weitere Integration mit UserManager hinzugefügt.
2024-10-29 14:24:13 +01:00
Developer 02
69abd3afa2 feat(DIExtensions.AddAPIKeyAuth): if options.Key is null return true in default isValidKey function 2024-10-29 12:44:53 +01:00
Developer 02
cbdd6ee295 feat(APIKeyAuthOptions): Schlüsselattribut wird löschbar gemacht.
- isValidKey-Eintrag wird löschbar gemacht.
 - wenn der Schlüssel null ist und der X-API-Schlüssel nicht existiert, wird die Anfrage authirezred.
2024-10-29 12:23:10 +01:00
Developer 02
2c1abaaf32 feat(DisableAPIKeyAuth): Optionen als bool zu appsettings hinzugefügt. Konfiguriert mit app 2024-10-29 11:43:25 +01:00
Developer 02
8038ff74dd feat(Swagger): OpenApiInfo über appsettings konfiguriert. 2024-10-29 10:44:20 +01:00
Developer 02
edcf3781b7 feat(APIKeyAuthAttribute): Zu allen Controllern hinzugefügt 2024-10-29 10:21:10 +01:00
Developer 02
6ea053be36 feat(APIKeyAuthHeaderOpFilter): Implementierung der SwaggerGen.IOperationFilter-Schnittstelle, um das API-Schlüsselfeld hinzuzufügen.
- Eigenschaft swagger-description hinzugefügt
2024-10-29 10:00:06 +01:00
Developer 02
67a62d7311 feat(APIKeyAuthOptions): Datenmodell zur Konfiguration der Autorisierung mit API-Schlüssel erstellt.
- DI-Erweiterung hinzugefügt
2024-10-29 09:29:14 +01:00
Developer 02
e17875dad7 feat(API): Methode zur Injektion von Abhängigkeiten hinzugefügt, um API-Schlüssel-Filter hinzuzufügen 2024-10-28 16:58:50 +01:00
Developer 02
b460de4e37 feat: Attribut zur API-Schlüssel-Authentifizierung hinzufügen
- ApiKeyAuthAttribute hinzugefügt und mit ApiKeyAuthFilter verknüpft.
- Dieses Attribut wird verwendet, um die API-Schlüssel-Überprüfung in Controllern anzuwenden.
2024-10-28 16:48:58 +01:00
Developer 02
1ca336abe0 feat: API-Schlüssel-Authentifizierungsfilter implementieren
- ApiKeyAuthFilter hinzugefügt, um API-Schlüssel aus den Anforderungs-Headern zu validieren.
- Der Filter überprüft das Vorhandensein eines API-Schlüssels und dessen Gültigkeit, bevor die Anforderung autorisiert wird.
- Der Standardname des API-Schlüssel-Headers ist auf "X-API-Key" festgelegt.
2024-10-28 16:45:22 +01:00
Developer 02
6e4a575864 feat(API): NLogger hinzugefügt 2024-10-28 16:27:32 +01:00
Developer 02
d664adf000 refactor: Unnötige Verzeichnissuch-Cachinge-Prozesse entfernen 2024-10-28 16:13:55 +01:00
Developer 02
41151593fd feat: JWT-Service mit Unterstützung für UserReadDto-Ansprüche hinzufügen
- ToClaimDictionary-Erweiterungsmethode hinzugefügt, um die Ansprüche von UserReadDto in ein Wörterbuch zu konvertieren.
- JWT-Service konfiguriert, um Tokens mit Ansprüchen aus UserReadDto zu generieren.
- JWT-Service in die Authentifizierungskonfiguration integriert.
2024-10-25 14:51:50 +02:00
Developer 02
730b218eb5 feat: Erweiterungsmethode hinzugefügt, um UserReadDto in Claims-Liste zu konvertieren 2024-10-25 14:41:06 +02:00
Developer 02
ee99d40fb1 refactor(ProfileControlsTFCreateDto): Entfernte id. 2024-10-25 12:24:07 +02:00
Developer 02
364036b9e4 feat(API): Authentifizierungs-Cookie aktualisiert.
- ExpireTimeSpan als 1 Stunde zugewiesen.
 - SlidingExpiration als wahre Stunde zugewiesen.
 - Cookie-Name als 'AuthSession' zugewiesen.
2024-10-25 12:23:34 +02:00
Developer 02
27f68df6d7 Chore: Optionen zur Aktivierung von Swagger über die appsettings.json in der Produktion hinzugefügt. 2024-10-25 11:30:40 +02:00
Developer 02
02a7120413 feat(Model): LoginDto in Login umbenennen 2024-10-25 11:13:48 +02:00
Developer 02
d61140c349 feat(API): Verzeichnis-Suchdienst hinzugefügt 2024-10-25 10:58:02 +02:00
Developer 02
ef91358c96 feat(API): Cookie-basierter Lokalisierer hinzugefügt. 2024-10-25 10:54:06 +02:00
Developer 02
f2ab2a9759 feat(auth): Verbesserung der Login-Logik mit erweiterter Validierung und Fehlerbehandlung
- Überprüfungen hinzugefügt, um sicherzustellen, dass entweder 'UserId' oder 'Username' angegeben ist, jedoch nicht beide.
- Fehlermeldungen verbessert, um eine bessere Klarheit zu gewährleisten.
- Benutzerabfrage-Logik in der Login-Methode refaktoriert, um vorhandene Benutzerdaten nach Möglichkeit zu nutzen.
- Konsistente Protokollierung von Hinweisen und Fehlern für eine bessere Nachverfolgbarkeit sichergestellt.
2024-10-25 10:24:27 +02:00
Developer 02
0495dc10de feat: Eigenschaften zu LogInDto für Validierung und Null-Prüfungen hinzugefügt 2024-10-25 09:43:22 +02:00
Developer 02
9c41e7bb18 feat(API.Models): DTO erstellt, um sich sowohl über id-password als auch über username password anzumelden 2024-10-25 09:36:47 +02:00
Developer 02
65ad9e6da0 feat: Cookie-basierte Authentifizierung zur Anwendung hinzufügen
- `CookieAuthenticationDefaults.AuthenticationScheme` zur Benutzerauthentifizierung integriert.
- Cookie-Einstellungen konfiguriert, um die Sicherheit zu erhöhen:
  - `HttpOnly`-Flag gesetzt, um den Zugriff von clientseitigen Skripten zu verhindern.
  - `SecurePolicy` so eingestellt, dass Cookies nur über HTTPS-Anfragen gesendet werden.
  - `SameSite` auf `Strict` gesetzt, um CSRF-Angriffe zu mindern.
- Benutzerdefinierte Anmelde-(`/api/auth/login`) und Abmeldepfade (`/api/auth/logout`) definiert.
2024-10-25 01:45:17 +02:00
Developer 02
0ef327a059 feat: ProfileObjStateController für verbesserte CRUD-Funktionalität aktualisiert
- `GetAsync`-Methode mit zusätzlichen Filteroptionen für Profil-, Benutzer- und Zustandsdetails erweitert.
- Verbesserte Autorisierungsprüfungen mit detaillierter Fehlerprotokollierung bei fehlenden oder ungültigen Benutzer-ID-Ansprüchen.
- Identitätsprüfung in den Create- und Delete-Methoden hinzugefügt, um unbefugten Zugriff zu verhindern.
- Fehlerbehandlung und Antwort verfeinert für robustere serverseitige Verarbeitung.
2024-10-25 01:43:14 +02:00
Developer 02
2a9e0a8f17 feat(ProfileControlsTFController): HttpDelete-Attribut zur Delete-Methode hinzugefügt 2024-10-25 01:39:32 +02:00
Developer 02
d6aac0b400 refactor: ProfileControlsTFController aktualisiert, um CRUD-Operationen zu verbessern
- Create-, Update- und Delete-Methoden verfeinert, um eine bessere Validierung der Benutzeridentität zu gewährleisten
- Autorisierungsprüfungen für benutzerbezogene Operationen basierend auf Claims hinzugefügt
- Verbesserte Fehlerbehandlung und Protokollierung für detaillierteres Feedback
- Fehlerbehandlungs-Basisklasse entfernt, Übergang zu direkten CRUD-Methoden
2024-10-25 01:38:16 +02:00
Developer 02
6d25f8d3bd refactor: ProfileControlsTFController mit benutzerdefinierter GetAsync-Methode erweitert
- Neue GetAsync-Methode hinzugefügt, um komplexe Filter mit optionalen Parametern zu unterstützen
- Verbesserte Fehlerprotokollierung und Validierung für die Extraktion der Benutzeridentität
- Benutzer-ID in den Service-Schichtenoperationen integriert
- Basismethode GetAll entfernt, um eine bessere Kontrolle über das Datenabrufen zu gewährleisten
2024-10-25 01:07:59 +02:00
Developer 02
eb45c6aefa feat(UserConroller): Added method to get authorized user. 2024-10-25 00:39:05 +02:00
Developer 02
79167a7f9d refactor(AuthController): Verwendeter Primär-Konstruktor 2024-10-24 22:11:41 +02:00
Developer 02
2a81f33340 feat: Erweiterungsmethoden zum Extrahieren von Benutzerdetails in ControllerExtensions hinzugefügt
- Methoden implementiert, um Benutzer-ID, Benutzernamen, Nachnamen, Vornamen und E-Mail aus den Claims zu extrahieren
- Null-Überprüfungen und Parsing-Logik bereitgestellt, um eine gültige Extraktion sicherzustellen
2024-10-24 22:10:02 +02:00
Developer 02
4e5a68fa89 feat: robuste Authentifizierungslogik in AuthController implementiert
- Login-Endpunkt mit claims-basierter Identität und Cookie-Authentifizierung hinzugefügt
- Gruppenvalidierungslogik mit Autorisierungsprüfungen integriert
- Fehlerbehandlung und Authentifizierungsfluss mit Logging versehen
- Logout-Funktionalität mit ordnungsgemäßem Cookie-Abmeldungsprozess bereitgestellt
2024-10-24 21:04:16 +02:00
Developer 02
2d2f35c972 refactor(API): Ersetzte CRUDControllerBase mit CRUDControllerBaseWithErrorHandling auf allen Controllern. 2024-10-24 20:50:42 +02:00
Developer 02
c606fe4480 feat(API): StateController initialisiert. 2024-10-24 20:46:37 +02:00
Developer 02
fa26fad600 feat(API): ProfileObjStateController initialisiert. 2024-10-24 20:45:42 +02:00
Developer 02
6da7f33437 feat(API): ProfileControlsTF initialisiert. 2024-10-24 20:43:37 +02:00
Developer 02
6fcddfc7b9 feat(API): ProfileController initialisiert. 2024-10-24 20:42:37 +02:00
Developer 02
ae59ffe73b feat(API): ConfigController initialisiert. 2024-10-24 20:40:27 +02:00
Developer 02
94a2d414d3 feat(API): Dienste Work Flow und User Manager hinzugefügt. 2024-10-24 20:25:26 +02:00
Developer 02
c7f1be7c58 feat(WFDBContext): Implementiert UserManagerDbContext. 2024-10-24 20:16:08 +02:00
Developer 02
70d7ed7415 feat(API): DB-Kontext zu den Diensten hinzugefügt 2024-10-24 20:02:16 +02:00
Developer 02
0351f8733d feat(API): Erforderliche Abhängigkeiten hinzugefügt. 2024-10-24 19:10:06 +02:00
Developer 02
26b57e5475 feat(API): Initalisiert. 2024-10-24 19:08:35 +02:00
Developer 02
2c66112d4d feat(ProfileObjStateService): ReadAsync-Methode als Schnittstellenimplementierung erstellt. 2024-10-24 18:57:39 +02:00
Developer 02
5327249f5e feat(ProfileControlsTFService): ReadAsync-Methode als Schnittstellenimplementierung erstellt. 2024-10-24 15:40:23 +02:00
Developer 02
f300b640a2 refactor: Ersetzt 'ProfileControlsTFRepository' durch 'IProfileControlsTFRepository'. 2024-10-24 15:27:10 +02:00
Developer 02
6a062045bb refactor: Ersetzt 'ProfileControlsTFRepository' durch 'IProfileControlsTFRepository'. 2024-10-24 15:26:39 +02:00
Developer 02
22f69589c9 refactor: Ersetzte 'usrId' durch 'userId'. 2024-10-24 15:22:32 +02:00
Developer 02
ca94368d0b feat(ProfileObjStateRepository): Zwei asynchrone Lesemethoden wurden zusammengeführt. 2024-10-24 15:15:41 +02:00
Developer 02
05701c10d2 feat(ProfileControlsTFRepository): Zwei asynchrone Lesemethoden wurden zusammengeführt. 2024-10-24 15:04:46 +02:00
Developer 02
1b21ccecf3 feat(DIExtensions): Methoden zur Injektion von Workflow-Diensten und mit TryAddScoped-Methode erstellt.
- Umwandlung von AddScoped in TryAddScoped in der Methode AddWorkFlowRepositories.
 - Methode mit dem Namen AddWorkFlow erstellt, um Dienste und Repositories zusammen hinzuzufügen
2024-10-23 17:42:19 +02:00
Developer 02
56344afdc8 feat(Anwendung): Crud-Dienste für Config, ProfileControlsTF, ProfileObjState, Profile und State erstellt.
- Implementierte zugehörige Schnittstellen.
2024-10-23 17:27:00 +02:00
Developer 02
dbbe07405b feat(Application): Crud-Service-Schnittstellen für Config, ProfileControlsTF, ProfileObjState, Profile und State erstellt.
- IUnique-Schnittstellen für ProfileControlsTFUpdateDto, ProfileObjStateUpdateDto und StateUpdateDto implementiert.
2024-10-23 17:04:36 +02:00
Developer 02
908dd6f170 feat(BaseUpdateDto): Implementierte IUniqe-Schnittstelle 2024-10-23 16:47:40 +02:00
Developer 02
dfb3dc3d08 feat: Profil von Auto-Mapper erstellt. 2024-10-23 16:42:19 +02:00
Developer 02
9f175bc4e9 feat(State): Erstellen, Lesen und Aktualisieren von DTOs unter Verwendung von Basis-DTOs zum Aktualisieren und Erstellen. 2024-10-23 16:32:58 +02:00
Developer 02
1453f9adb1 feat(ProfileObjState: Erstellen, Lesen und Aktualisieren von DTOs unter Verwendung von Basis-DTOs zum Aktualisieren und Erstellen. 2024-10-23 16:32:39 +02:00
Developer 02
21956cfc16 feat(ProfileControlsTF): Erstellen, Lesen und Aktualisieren von DTOs unter Verwendung von Basis-DTOs zum Aktualisieren und Erstellen. 2024-10-23 15:45:29 +02:00
Developer 02
1e6d247817 feat(Profile): Erstellen, Lesen und Aktualisieren von DTOs unter Verwendung von Basis-DTOs zum Aktualisieren und Erstellen. 2024-10-23 15:10:37 +02:00
Developer 02
adc33bfee1 feat(Config): Erstellen, Lesen und Aktualisieren von DTOs unter Verwendung von Basis-DTOs zum Aktualisieren und Erstellen. 2024-10-23 14:57:15 +02:00
Developer 02
5ce6958122 feat: BaseUpdateDto zur Verfolgung von Aktualisierungen hinzufügen
- BaseUpdateDto eingeführt, um Metadaten zur Aktualisierung zu kapseln.
- Eigenschaften umfassen ChangedWho und ChangedWhen, beide mit JsonIgnore markiert, um von der Serialisierung ausgeschlossen zu werden.
2024-10-23 14:36:23 +02:00
Developer 02
23e2267d00 feat: BaseCreateDto zur Verfolgung von Erstellung hinzufügen
- BaseCreateDto eingeführt, um Metadaten zur Erstellung zu kapseln.
- Eigenschaften umfassen AddedWho und AddedWhen, beide mit JsonIgnore markiert, um von der Serialisierung ausgeschlossen zu werden.
2024-10-23 14:34:52 +02:00
Developer 02
bc9ac273ea feat: Initalisiertes WorkFlow.Application Projekt mit Abhängigkeiten. 2024-10-23 14:19:55 +02:00
Developer 02
b0896c214f feat: Überladungen der Read-Methode für ProfileObjStateRepository hinzufügen
- Überladene ReadAsync-Methoden in ProfileObjStateRepository implementiert, um Filterung nach Benutzer und Status zu ermöglichen.
- Die Abfrageeffizienz durch Einbeziehung verwandter Entitäten und Anwendung bedingter Filter verbessert.
2024-10-23 14:12:04 +02:00
Developer 02
e1f0d611e5 ProfileObjStateRepository mit Read-Methode erweitern
- Neue Read-Methode zur ProfileObjStateRepository hinzugefügt, um flexiblere Abfragen zu ermöglichen.
- Die Methode unterstützt optionale Parameter zur Filterung nach Profil, Benutzer, Zustand und Objekt-ID.
- Verbesserte Datenabrufmöglichkeiten durch Einbeziehung verwandter Entitäten wie Profil und Zustand.
2024-10-23 14:00:55 +02:00
Developer 02
8ff6bbf93f refactor(ProfileObjState): Benutzer-Eigenschaft hinzugefügt, abhängig von Fremdschlüssel UsrId 2024-10-23 13:51:17 +02:00
Developer 02
650b23def9 refactor(ProfileControlsTFRepository): Überschriebene ReadOnly-Methode, um Profile und ProfileObjState Entitäten standardmäßig einzuschließen. 2024-10-23 13:40:12 +02:00
Developer 02
859f0631f0 refactor(repository): ReadAsync-Methode aktualisieren, um readonly-Parameter zu unterstützen 2024-10-23 13:36:50 +02:00
Developer 02
31bf58919d refactor(ProfileControlsTFRepository): Überschriebene ReadOnly-Methode, um Profile und User Entitäten standardmäßig einzuschließen. 2024-10-23 13:25:57 +02:00
Developer 02
480dcce051 feat(repository): Überladung der ReadAsync-Methode mit Username- und usrId-Filter in ProfileControlsTFRepository hinzugefügt
- Überladung von `ReadAsync` hinzugefügt, um die Filterung nach `usrId` und `username` zu unterstützen.
- `ReadAsync` aktualisiert, um die Einzelabfrage mit `FirstOrDefaultAsync` zu ermöglichen.
- Abfrageflexibilität verbessert, indem Benutzer- und Profilfilter in beiden asynchronen Methoden zugelassen werden.
2024-10-23 13:19:31 +02:00
Developer 02
e0877f5990 feat(repository): Async-Read-Methode und Username-Filter in ProfileControlsTFRepository hinzugefügt
- `ReadAsync`-Methode für asynchrone Abfrageausführung hinzugefügt.
- `username`-Filter zur `Read`-Methode hinzugefügt.
- Filterlogik in der `Read`-Methode aktualisiert, um die `username`-Bedingung einzuschließen.
2024-10-23 13:11:27 +02:00
Developer 02
845f7fe729 feat(repository): Abfragefunktionalität mit Filtern in ProfileControlsTFRepository hinzugefügt
- Methode `Read` mit optionalen Filtern für `Profile`, `User`, `profileId`, `usrId`, `objId` und `profileActive` in `ProfileControlsTFRepository` hinzugefügt.
- `AsNoTracking` für schreibgeschützte Abfragen eingeführt.
- `Include` für verwandte `Profile` und `User` Entitäten in der Abfrage hinzugefügt.
2024-10-23 13:02:11 +02:00
Developer 02
a7081d3f74 feat(WfState): Wf-Präfix aus Entität, Repository und DbSet entfernt 2024-10-23 11:49:20 +02:00
Developer 02
e1ec8c581c feat(ProfileObjState.cs): Mwf-Präfix entfernt 2024-10-23 11:47:14 +02:00
Developer 02
c76f9d1709 feat(ProfControlsTf.cs): Umbenennung der Domäne, des Repository und des dbset in ProfileControlsTF 2024-10-23 11:44:11 +02:00
Developer 02
4bcac51473 feat(ProfControlsTf.cs): Removed the Mwf prefix from property names. 2024-10-23 11:28:27 +02:00
Developer 02
1f4c7589d0 feat(Config.cs): Umbenennung von ConfTitle in Title und ConfString in String 2024-10-23 11:25:39 +02:00
Developer 02
8290699b2f feat(Infrastructure): erstellt DIExtensions.
- AddWorkFlowRepositories-Methode hinzugefügt, um Repositories über Schnittstellen einzubinden
2024-10-23 11:11:09 +02:00
Developer 02
f611847e2a feat(Contracts): implementiert alle Repository-Schnittstellen mit CRUDRepository 2024-10-23 11:07:37 +02:00
Developer 02
3c5df5bc6a feat(Contracts): IWfStateRepository als eine Implementierung von ICRUDRepository erstellt. 2024-10-23 10:45:01 +02:00
Developer 02
3f70bdc8f7 feat(Contracts): IProfileObjStateRepository als eine Implementierung von ICRUDRepository erstellt. 2024-10-23 10:44:05 +02:00
Developer 02
f0b182fb94 feat(Contracts): IProfileRepository als eine Implementierung von ICRUDRepository erstellt 2024-10-23 10:42:46 +02:00
Developer 02
3d35c1ab21 feat(Contracts): IProfControlsTfRepository als eine Implementierung von ICRUDRepository erstellt 2024-10-23 10:41:25 +02:00
Developer 02
16f694eb67 feat(Contracts): IConfigRepository als eine Implementierung von ICRUDRepository erstellt 2024-10-23 10:39:52 +02:00
Developer 02
5dab28d99d feat(Entitäten): IUnique-Schnittstelle, die auf jede Entität angewendet wird. 2024-10-23 10:35:33 +02:00
Developer 02
1df7858423 refactor(ProfileObjState): Umbenennen der Eigenschaft 'Profile' in 'MwfProfile' 2024-10-23 10:31:34 +02:00
Developer 02
cb5a6afde0 feat(ProfileObjState): State Entität mit StateId-foreign-key hinzugefügt. 2024-10-23 10:26:50 +02:00
Developer 02
d4d6c29225 feat(ProfileObjState): Profil Entität mit MwfProfileId-foreign-key hinzugefügt. 2024-10-23 10:26:15 +02:00
Developer 02
dd15b520c1 feat(ProfControlsTf): User Entität mit UsrId-foreign-key hinzugefügt.
- UserManager.Domain baget-package hinzugefügt
2024-10-23 10:20:59 +02:00
76 changed files with 1808 additions and 31 deletions

View File

@@ -0,0 +1,14 @@
using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Filters;
namespace WorkFlow.API.Attributes
{
//TODO: move APIKeyAuthAttribute to Core.API
public class APIKeyAuthAttribute : ServiceFilterAttribute
{
public APIKeyAuthAttribute()
: base(typeof(APIKeyAuthFilter))
{
}
}
}

View File

@@ -0,0 +1,20 @@
using DigitalData.Core.API;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Attributes;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.Config;
using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ConfigController : CRUDControllerBaseWithErrorHandling<IConfigService, ConfigCreateDto, ConfigDto, ConfigUpdateDto, Config, int>
{
public ConfigController(ILogger<ConfigController> logger, IConfigService service) : base(logger, service)
{
}
}

View File

@@ -0,0 +1,96 @@
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using WorkFlow.API.Attributes;
namespace WorkFlow.API.Controllers
{
[APIKeyAuth]
public static class ControllerExtensions
{
public static bool TryGetUserId(this ControllerBase controller, out int? id)
{
var value = controller.User.FindFirstValue(ClaimTypes.NameIdentifier);
if (value is null)
{
id = default;
return false;
}
if(int.TryParse(value, out int id_int))
{
id = id_int;
return true;
}
else
{
id = null;
return false;
}
}
public static bool TryGetUsername(this ControllerBase controller, out string username)
{
var value = controller.User.FindFirstValue(ClaimTypes.Name);
if (value is null)
{
username = string.Empty;
return false;
}
else
{
username = value;
return true;
}
}
public static bool TryGetName(this ControllerBase controller, out string name)
{
var value = controller.User.FindFirstValue(ClaimTypes.Surname);
if (value is null)
{
name = string.Empty;
return false;
}
else
{
name = value;
return true;
}
}
public static bool TryGetPrename(this ControllerBase controller, out string prename)
{
var value = controller.User.FindFirstValue(ClaimTypes.GivenName);
if (value is null)
{
prename = string.Empty;
return false;
}
else
{
prename = value;
return true;
}
}
public static bool TryGetEmail(this ControllerBase controller, out string email)
{
var value = controller.User.FindFirstValue(ClaimTypes.Email);
if (value is null)
{
email = string.Empty;
return false;
}
else
{
email = value;
return true;
}
}
}
}

View File

@@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using WorkFlow.API.Models;
using WorkFlow.API.Attributes;
namespace WorkFlow.API.Controllers;
//TODO: implement up-to-date AuthController in UserManager
[APIKeyAuth]
[Route("api/Auth")]
[ApiController]
[Tags("Auth")]
public class PlaceholderAuthController : ControllerBase
{
[HttpPost]
public IActionResult CreateTokenViaBody([FromBody] Login login)
{
throw new NotImplementedException();
}
[HttpGet("check")]
[Authorize]
public IActionResult Check() => Ok();
}

View File

@@ -0,0 +1,20 @@
using DigitalData.Core.API;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Attributes;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.Profile;
using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileController : CRUDControllerBaseWithErrorHandling<IProfileService, ProfileCreateDto, ProfileDto, ProfileUpdateDto, Profile, int>
{
public ProfileController(ILogger<ProfileController> logger, IProfileService service) : base(logger, service)
{
}
}

View File

@@ -0,0 +1,126 @@
using DigitalData.Core.API;
using DigitalData.Core.DTO;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Attributes;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.ProfileControlsTF;
using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileControlsTFController : CRUDControllerBase<IProfileControlsTFService, ProfileControlsTFCreateDto, ProfileControlsTFDto, ProfileControlsTFUpdateDto, ProfileControlsTF, int>
{
private readonly ILogger<ProfileControlsTFController> logger;
public ProfileControlsTFController(ILogger<ProfileControlsTFController> logger, IProfileControlsTFService service) : base(logger, service)
{
this.logger = logger;
}
[NonAction]
public override Task<IActionResult> GetAll() => base.GetAll();
[NonAction]
public override Task<IActionResult> Update(ProfileControlsTFUpdateDto updateDto) => base.Update(updateDto);
[HttpGet]
public async Task<IActionResult> GetAsync(
bool withProfile = true, bool withUser = false,
int? profileId = null, int? objId = null, bool? profileActive = null)
{
try
{
if (!this.TryGetUserId(out int? id))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (id is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
return await _service.ReadAsync(
withProfile: withProfile, withUser: withUser,
userId: id,
profileId: profileId, objId: objId, profileActive: profileActive)
.ThenAsync(
Success: pctf => pctf.Any() ? Ok(pctf) : NotFound(),
Fail: IActionResult (msg, ntc) =>
{
logger.LogNotice(ntc);
return NotFound();
});
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
[HttpPost]
public override async Task<IActionResult> Create([FromBody] ProfileControlsTFCreateDto createDto)
{
try
{
if (!this.TryGetUserId(out int? id))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (id is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
if (createDto.UserId != id)
return Unauthorized();
return await base.Create(createDto);
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
[HttpDelete]
public override async Task<IActionResult> Delete([FromRoute] int id)
{
try
{
if (!this.TryGetUserId(out int? userId))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (userId is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
return await _service.ReadByIdAsync(id).ThenAsync(
SuccessAsync: async pctf => pctf.UserId == userId ? await base.Delete(id) : Unauthorized(),
Fail: IActionResult (msg, ntc) =>
{
_logger.LogNotice(ntc);
return ntc.HasFlag(Flag.NotFound) ? NotFound() : StatusCode(StatusCodes.Status500InternalServerError);
});
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
}

View File

@@ -0,0 +1,127 @@
using DigitalData.Core.API;
using DigitalData.Core.DTO;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Attributes;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.ProfileObjState;
using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers
{
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileObjStateController : CRUDControllerBaseWithErrorHandling<IProfileObjStateService, ProfileObjStateCreateDto, ProfileObjStateDto, ProfileObjStateUpdateDto, ProfileObjState, int>
{
private readonly ILogger<ProfileObjStateController> logger;
public ProfileObjStateController(ILogger<ProfileObjStateController> logger, IProfileObjStateService service) : base(logger, service)
{
this.logger = logger;
}
[NonAction]
public override Task<IActionResult> GetAll() => base.GetAll();
[NonAction]
public override Task<IActionResult> Update(ProfileObjStateUpdateDto updateDto) => base.Update(updateDto);
[HttpGet]
public async Task<IActionResult> GetAsync(
bool withProfile = true, bool withUser = true, bool withState = true,
int? profileId = null, int? objId = null, bool? profileActive = null)
{
try
{
if (!this.TryGetUserId(out int? id))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (id is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
return await _service.ReadAsync(
withProfile: withProfile, withUser: withUser, withState,
userId: id,
profileId: profileId, objId: objId, profileActive: profileActive)
.ThenAsync(
Success: pctf => pctf.Any() ? Ok(pctf) : NotFound(),
Fail: IActionResult (msg, ntc) =>
{
logger.LogNotice(ntc);
return NotFound();
});
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
[HttpPost]
public override async Task<IActionResult> Create([FromBody] ProfileObjStateCreateDto createDto)
{
try
{
if (!this.TryGetUserId(out int? id))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (id is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
if (createDto.UserId != id)
return Unauthorized();
return await base.Create(createDto);
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
[HttpDelete]
public override async Task<IActionResult> Delete([FromRoute] int id)
{
try
{
if (!this.TryGetUserId(out int? userId))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (userId is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
return await _service.ReadByIdAsync(id).ThenAsync(
SuccessAsync: async pctf => pctf.UserId == userId ? await base.Delete(id) : Unauthorized(),
Fail: IActionResult (msg, ntc) =>
{
_logger.LogNotice(ntc);
return ntc.HasFlag(Flag.NotFound) ? NotFound() : StatusCode(StatusCodes.Status500InternalServerError);
});
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
}
}

View File

@@ -0,0 +1,20 @@
using DigitalData.Core.API;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Attributes;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.State;
using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class StateController : CRUDControllerBaseWithErrorHandling<IStateService, StateCreateDto, StateDto, StateUpdateDto, State, int>
{
public StateController(ILogger<StateController> logger, IStateService service) : base(logger, service)
{
}
}

View File

@@ -0,0 +1,54 @@
using DigitalData.Core.DTO;
using DigitalData.UserManager.Application.Contracts;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Attributes;
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class UserController : ControllerBase
{
private readonly ILogger<UserController> logger;
private readonly IUserService userService;
public UserController(ILogger<UserController> logger, IUserService userService)
{
this.logger = logger;
this.userService = userService;
}
[HttpGet]
public async Task<IActionResult> GetAsync()
{
try
{
if (!this.TryGetUserId(out int? id))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if(id is int id_int)
return await userService.ReadByIdAsync(id_int).ThenAsync(
Success: Ok,
Fail: IActionResult (msg, ntc) =>
{
logger.LogNotice(ntc);
return NotFound();
});
else
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
}

View File

@@ -0,0 +1,22 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using WorkFlow.API.Filters;
using WorkFlow.API.Models;
namespace WorkFlow.API.Extensions
{
public static class DIExtensions
{
public static IServiceCollection AddAPIKeyAuth(this IServiceCollection services, Func<string?, bool> isValidKey, string headerName = "X-API-Key")
=> services.AddSingleton<APIKeyAuthFilter>(provider => new(isValidKey: isValidKey, headerName: headerName));
public static IServiceCollection AddAPIKeyAuth(this IServiceCollection services, APIKeyAuthOptions options, bool configureOptions = true)
{
if(configureOptions)
services.TryAddSingleton(Options.Create(options));
return services.AddAPIKeyAuth(isValidKey: key => options.Key is null || options.Key == key, headerName: options.HeaderName);
}
}
}

View File

@@ -0,0 +1,42 @@
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using WorkFlow.API.Models;
namespace WorkFlow.API.Filters;
public class APIKeyAuthHeaderOpFilter : IOperationFilter
{
private readonly APIKeyAuthOptions apiKeyAuthOptions;
private readonly IWebHostEnvironment environment;
public APIKeyAuthHeaderOpFilter(IOptions<APIKeyAuthOptions> options, IWebHostEnvironment environment)
{
this.environment = environment;
apiKeyAuthOptions = options.Value;
}
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var param = new OpenApiParameter
{
Name = apiKeyAuthOptions.HeaderName,
In = ParameterLocation.Header,
Required = true,
AllowEmptyValue = false,
Schema = new OpenApiSchema
{
Type = "string"
}
};
if(environment.IsDevelopment())
param.Schema.Default = new OpenApiString(apiKeyAuthOptions.Key);
if (apiKeyAuthOptions.SwaggerDescription is not null)
param.Description = apiKeyAuthOptions.SwaggerDescription;
operation.Parameters.Add(param);
}
}

View File

@@ -0,0 +1,22 @@
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;
namespace WorkFlow.API.Filters;
public class APIKeyAuthFilter : IAuthorizationFilter
{
private readonly Func<string?, bool> isValidKey;
private readonly string headerName;
public APIKeyAuthFilter(Func<string?, bool> isValidKey, string headerName = "X-API-Key")
{
this.isValidKey = isValidKey;
this.headerName = headerName;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
if (!isValidKey(context.HttpContext.Request.Headers[headerName]))
context.Result = new UnauthorizedResult();
}
}

10
WorkFlow.API/Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,10 @@
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'dotnet build'
}
}
}
}

View File

@@ -0,0 +1,18 @@
namespace WorkFlow.API;
public class LazyServiceProvider : IServiceProvider
{
private Lazy<IServiceProvider>? _serviceProvider;
public Func<IServiceProvider> Factory
{
set => _serviceProvider = new(value);
}
public object? GetService(Type serviceType)
{
if (_serviceProvider is null)
throw new InvalidOperationException("GetService cannot be called before _serviceProvider is set.");
return _serviceProvider.Value.GetService(serviceType);
}
}

View File

@@ -0,0 +1,11 @@
namespace WorkFlow.API.Models
{
public class APIKeyAuthOptions
{
public string? Key { get; init; } = null;
public string HeaderName { get; init; } = "X-API-Key";
public string? SwaggerDescription { get; init; } = null;
}
}

View File

@@ -0,0 +1,12 @@
namespace WorkFlow.API.Models;
public class AuthTokenKeys
{
public string Cookie { get; init; } = "AuthToken";
public string QueryString { get; init; } = "AuthToken";
public string Issuer { get; init; } = "auth.digitaldata.works";
public string Audience { get; init; } = "work-flow.digitaldata.works";
}

View File

@@ -0,0 +1,4 @@
namespace WorkFlow.API.Models
{
public record Login(int? UserId, string? Username, string Password);
}

View File

@@ -0,0 +1,19 @@
using DigitalData.UserManager.Application.DTOs.User;
using System.Security.Claims;
namespace WorkFlow.API.Models
{
public static class ModelExtensions
{
public static List<Claim> ToClaimList(this UserReadDto user) => new()
{
new (ClaimTypes.NameIdentifier, user.Id.ToString()),
new (ClaimTypes.Name, user.Username),
new (ClaimTypes.Surname, user.Name ?? ""),
new (ClaimTypes.GivenName, user.Prename ?? ""),
new (ClaimTypes.Email, user.Email ?? "")
};
public static Dictionary<string, object> ToClaimDictionary(this UserReadDto user) => user.ToClaimList().ToDictionary(claim => claim.Type, claim => (object) claim.Value);
}
}

161
WorkFlow.API/Program.cs Normal file
View File

@@ -0,0 +1,161 @@
using WorkFlow.Application;
using DigitalData.UserManager.Application;
using Microsoft.EntityFrameworkCore;
using WorkFlow.Infrastructure;
using DigitalData.Core.API;
using DigitalData.Core.Application;
using DigitalData.UserManager.Application.DTOs.User;
using Microsoft.IdentityModel.Tokens;
using WorkFlow.API.Models;
using NLog;
using NLog.Web;
using WorkFlow.API.Extensions;
using WorkFlow.API.Filters;
using Microsoft.OpenApi.Models;
using DigitalData.Auth.Client;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using WorkFlow.API;
using Microsoft.Extensions.Options;
using DigitalData.Core.Abstractions.Security;
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger.Info("Logging initialized.");
try
{
var builder = WebApplication.CreateBuilder(args);
var config = builder.Configuration;
// Add NLogger
builder.Logging.ClearProviders();
builder.Host.UseNLog();
// Add services to the container
var cnn_str = config.GetConnectionString("Default") ?? throw new("Default connection string not found.");
builder.Services.AddDbContext<WFDBContext>(options => options.UseSqlServer(cnn_str).EnableDetailedErrors());
builder.Services.AddWorkFlow().AddUserManager<WFDBContext>();
builder.Services.AddCookieBasedLocalizer();
builder.Services.AddDirectorySearchService(config.GetSection("DirectorySearchOptions"));
builder.Services.AddJWTService<UserReadDto>(user => new SecurityTokenDescriptor()
{
Claims = user.ToClaimList().ToDictionary(claim => claim.Type, claim => claim.Value as object)
});
bool disableAPIKeyAuth = config.GetValue<bool>("DisableAPIKeyAuth") && builder.IsDevOrDiP();
if (disableAPIKeyAuth)
builder.Services.AddAPIKeyAuth(new APIKeyAuthOptions());
else
if (config.GetSection("APIKeyAuth").Get<APIKeyAuthOptions>() is APIKeyAuthOptions options)
builder.Services.AddAPIKeyAuth(options);
else
throw new("The API Key Authorization configuration is not available in the app settings, even though the app is not in development or DiP mode and API Key Authorization is not disabled.");
var lazyProvider = new LazyServiceProvider();
builder.Services.AddAuthHubClient(config.GetSection("AuthClientParams"));
builder.Services.AddControllers();
var authTokenKeys = config.GetSection(nameof(AuthTokenKeys)).Get<AuthTokenKeys>() ?? new();
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(opt =>
{
opt.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKeyResolver = (token, securityToken, identifier, parameters) =>
{
var clientParams = lazyProvider.GetRequiredService<IOptions<ClientParams>>()?.Value;
var publicKey = clientParams!.PublicKeys.Get(authTokenKeys.Issuer, authTokenKeys.Audience);
return new List<SecurityKey>() { publicKey.SecurityKey };
},
ValidateIssuer = true,
ValidIssuer = authTokenKeys.Issuer,
ValidateAudience = true,
ValidAudience = authTokenKeys.Audience,
};
opt.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// if there is no token read related cookie or query string
if (context.Token is null) // if there is no token
{
if (context.Request.Cookies.TryGetValue(authTokenKeys.Cookie, out var cookieToken) && cookieToken is not null)
context.Token = cookieToken;
else if (context.Request.Query.TryGetValue(authTokenKeys.QueryString, out var queryStrToken))
context.Token = queryStrToken;
}
return Task.CompletedTask;
}
};
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(setupAct =>
{
setupAct.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "Bearer"
});
setupAct.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
if (!disableAPIKeyAuth)
setupAct.OperationFilter<APIKeyAuthHeaderOpFilter>();
if (config.GetSection("OpenApiInfo").Get<OpenApiInfo>() is OpenApiInfo openApiInfo)
setupAct.SwaggerDoc(openApiInfo?.Version ?? "v1", openApiInfo);
});
var app = builder.Build();
lazyProvider.Factory = () => app.Services;
// Configure the HTTP request pipeline.
if (app.IsDevOrDiP() && app.Configuration.GetValue<bool>("EnableSwagger"))
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseCookieBasedLocalizer("de-DE");
app.MapControllers();
app.Run();
}
catch (Exception ex)
{
logger.Error(ex, "Stopped program because of exception.");
throw;
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<WebPublishMethod>Package</WebPublishMethod>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<ExcludeApp_Data>false</ExcludeApp_Data>
<ProjectGuid>4fb33592-ef0d-47c3-9cde-03b2ef12be00</ProjectGuid>
<DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\workFLOW\API\net7\$(Version)\workFLOW.API.zip</DesktopBuildPackageLocation>
<PackageAsSingleFile>true</PackageAsSingleFile>
<DeployIisAppPath>WorkFlow.API</DeployIisAppPath>
<_TargetId>IISWebDeployPackage</_TargetId>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<WebPublishMethod>Package</WebPublishMethod>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<ExcludeApp_Data>false</ExcludeApp_Data>
<ProjectGuid>4fb33592-ef0d-47c3-9cde-03b2ef12be00</ProjectGuid>
<DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\workFLOW\API\net8\$(Version)\workFLOW.API.zip</DesktopBuildPackageLocation>
<PackageAsSingleFile>true</PackageAsSingleFile>
<DeployIisAppPath>WorkFlow.API</DeployIisAppPath>
<_TargetId>IISWebDeployPackage</_TargetId>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,41 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:56180",
"sslPort": 44397
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5130",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7120;http://localhost:5130",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

8
WorkFlow.API/WFKey.cs Normal file
View File

@@ -0,0 +1,8 @@
namespace WorkFlow.API
{
public static class WFKey
{
public static readonly string WrongPassword = nameof(WrongPassword);
public static readonly string UserNotFoundOrWrongPassword = nameof(UserNotFoundOrWrongPassword);
}
}

View File

@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<PackageId>WorkFlow.API</PackageId>
<Version>1.1.0</Version>
<Company>Digital Data GmbH</Company>
<Product>WorkFlow.API</Product>
<Title>WorkFlow.API</Title>
<AssemblyVersion>1.1.0</AssemblyVersion>
<FileVersion>1.1.0</FileVersion>
</PropertyGroup>
<ItemGroup>
<_WebToolingArtifacts Remove="Properties\PublishProfiles\IISProfile - Copy.pubxml" />
<_WebToolingArtifacts Remove="Properties\PublishProfiles\IISProfileNet7.pubxml" />
<_WebToolingArtifacts Remove="Properties\PublishProfiles\IISProfileNet8.pubxml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="DigitalData.Auth.Client" Version="1.3.3" />
<PackageReference Include="DigitalData.Core.API" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.20" />
<PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.14" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WorkFlow.Application\WorkFlow.Application.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,91 @@
{
"DiPMode": true,
"EnableSwagger": true,
"DisableAPIKeyAuth": false,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"NLog": {
"throwConfigExceptions": true,
"variables": {
"logDirectory": "E:\\LogFiles\\Digital Data\\workFlow.API",
"logFileNamePrefix": "${shortdate}-workFlow.API"
},
"targets": {
"infoLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Info.log",
"maxArchiveDays": 30
},
"errorLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Error.log",
"maxArchiveDays": 30
},
"criticalLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Critical.log",
"maxArchiveDays": 30
}
},
// Trace, Debug, Info, Warn, Error and *Fatal*
"rules": [
{
"logger": "*",
"minLevel": "Info",
"maxLevel": "Warn",
"writeTo": "infoLogs"
},
{
"logger": "*",
"level": "Error",
"writeTo": "errorLogs"
},
{
"logger": "*",
"level": "Fatal",
"writeTo": "criticalLogs"
}
]
},
"AllowedHosts": "*",
"ConnectionStrings": {
"Default": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"
},
"DirectorySearchOptions": {
"ServerName": "DD-VMP01-DC01",
"Root": "DC=dd-gan,DC=local,DC=digitaldata,DC=works",
"UserCacheExpirationDays": 1,
"CustomSearchFilters": {
"User": "(&(objectClass=user)(sAMAccountName=*))",
"Group": "(&(objectClass=group) (samAccountName=*))"
}
},
"APIKeyAuth": {
"Key": "ULbcOUiAXAoCXPviyCGtObZUGnrCHNgDmtNbQNpq5MOhB0EFQn18dObdQ93INNy8xIcnOPMJfEHqOotllELVrJ2R5AjqOfQszT2j00w215GanD3UiJGwFhwmdoNFsmNj",
"HeaderName": "X-API-Key",
"SwaggerDescription": "Required header for API key authentication. Enter a valid API key."
},
"OpenApiInfo": {
"Title": "WorkFlow API",
"Contact": {
"Email": "info-flow@digitaldata.works",
"Name": "Digital Data GmbH",
"Url": "https://digitaldata.works/"
}
},
"AuthClientParams": {
"Url": "https://localhost:7192/auth-hub",
"PublicKeys": [
{
"Issuer": "auth.digitaldata.works",
"Audience": "work-flow.digitaldata.works",
"Content": "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3QCd7dH/xOUITFZbitMa/xnh8a0LyL6ZBvSRAwkI9ceplTRSHJXoM1oB+xtjWE1kOuHVLe941Tm03szS4+/rHIm0Ejva/KKlv7sPFAHE/pWuoPS303vOHgI4HAFcuwywA8CghUWzaaK5LU/Hl8srWwxBHv5hKIUjJFJygeAIENvFOZ1gFbB3MPEC99PiPOwAmfl4tMQUmSsFyspl/RWVi7bTv26ZE+m3KPcWppmvmYjXlSitxRaySxnfFvpca/qWfd/uUUg2KWKtpAwWVkqr0qD9v3TyKSgHoGDsrFpwSx8qufUJSinmZ1u/0iKl6TXeHubYS4C4SUSVjOWXymI2ZQIDAQAB-----END PUBLIC KEY-----"
}
],
"RetryDelay": "00:00:05"
}
}

View File

@@ -0,0 +1,10 @@
using DigitalData.Core.Abstractions.Application;
using WorkFlow.Application.DTO.Config;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application.Contracts
{
public interface IConfigService : ICRUDService<ConfigCreateDto, ConfigDto, Config, int>
{
}
}

View File

@@ -0,0 +1,15 @@
using DigitalData.Core.Abstractions.Application;
using DigitalData.Core.DTO;
using WorkFlow.Application.DTO.ProfileControlsTF;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application.Contracts
{
public interface IProfileControlsTFService : ICRUDService<ProfileControlsTFCreateDto, ProfileControlsTFDto, ProfileControlsTF, int>
{
Task<DataResult<IEnumerable<ProfileControlsTFDto>>> ReadAsync(
bool withProfile = true, bool withUser = false,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null);
}
}

View File

@@ -0,0 +1,15 @@
using DigitalData.Core.Abstractions.Application;
using DigitalData.Core.DTO;
using WorkFlow.Application.DTO.ProfileObjState;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application.Contracts
{
public interface IProfileObjStateService : ICRUDService<ProfileObjStateCreateDto, ProfileObjStateDto, ProfileObjState, int>
{
Task<DataResult<IEnumerable<ProfileObjStateDto>>> ReadAsync(
bool withProfile = true, bool withUser = true, bool withState = true,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null);
}
}

View File

@@ -0,0 +1,10 @@
using DigitalData.Core.Abstractions.Application;
using WorkFlow.Application.DTO.Profile;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application.Contracts
{
public interface IProfileService : ICRUDService<ProfileCreateDto, ProfileDto, Profile, int>
{
}
}

View File

@@ -0,0 +1,10 @@
using DigitalData.Core.Abstractions.Application;
using WorkFlow.Application.DTO.State;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application.Contracts
{
public interface IStateService : ICRUDService<StateCreateDto, StateDto, State, int>
{
}
}

View File

@@ -0,0 +1,25 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.Services;
using WorkFlow.Infrastructure;
namespace WorkFlow.Application
{
public static class DIExtensions
{
public static IServiceCollection AddWorkFlowServices(this IServiceCollection services)
{
services.AddAutoMapper(typeof(MappingProfile).Assembly);
services.TryAddScoped<IConfigService, ConfigService>();
services.TryAddScoped<IProfileControlsTFService, ProfileControlsTFService>();
services.TryAddScoped<IProfileObjStateService, ProfileObjStateService>();
services.TryAddScoped<IProfileService, ProfileService>();
services.TryAddScoped<IStateService, StateService>();
return services;
}
public static IServiceCollection AddWorkFlow(this IServiceCollection services) => services.AddWorkFlowRepositories().AddWorkFlowServices();
}
}

View File

@@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace WorkFlow.Application.DTO
{
public record BaseCreateDto
{
[JsonIgnore]
public required string AddedWho { get; set; } = "UNKNOWN";
[JsonIgnore]
public required DateTime AddedWhen = DateTime.Now;
}
}

View File

@@ -0,0 +1,16 @@
using DigitalData.Core.Abstractions;
using System.Text.Json.Serialization;
namespace WorkFlow.Application.DTO
{
public record BaseUpdateDto : IUnique<int>
{
public required int Id { get; init; }
[JsonIgnore]
public required string ChangedWho { get; set; } = "UNKNOWN";
[JsonIgnore]
public required DateTime ChangedWhen = DateTime.Now;
}
}

View File

@@ -0,0 +1,4 @@
namespace WorkFlow.Application.DTO.Config
{
public record ConfigCreateDto(string Title, string String);
}

View File

@@ -0,0 +1,10 @@
namespace WorkFlow.Application.DTO.Config
{
public record ConfigDto(int Id,
string Title,
string String,
string AddedWho,
DateTime AddedWhen,
string? ChangedWho = null,
DateTime? ChangedWhen = null);
}

View File

@@ -0,0 +1,4 @@
namespace WorkFlow.Application.DTO.Config
{
public record ConfigUpdateDto(string Title, string String) : BaseUpdateDto;
}

View File

@@ -0,0 +1,4 @@
namespace WorkFlow.Application.DTO.Profile
{
public record ProfileCreateDto(string IntlName, int ExtId1, bool Active, byte TypeId) : BaseCreateDto;
}

View File

@@ -0,0 +1,12 @@
namespace WorkFlow.Application.DTO.Profile
{
public record ProfileDto(int Id,
string IntlName,
int ExtId1,
bool Active,
byte TypeId,
string AddedWho,
DateTime AddedWhen,
string? ChangedWho = null,
DateTime? ChangedWhen = null);
}

View File

@@ -0,0 +1,4 @@
namespace WorkFlow.Application.DTO.Profile
{
public record ProfileUpdateDto(string IntlName, int ExtId1, bool Active, byte TypeId) : BaseUpdateDto;
}

View File

@@ -0,0 +1,14 @@
namespace WorkFlow.Application.DTO.ProfileControlsTF
{
public record ProfileControlsTFCreateDto(
int ProfileId,
int UserId,
long ObjId,
string ObjType,
string AttrName,
string CtrlType,
string CtrlCaption,
bool Mandatory,
bool ReadOnly,
string? ChoiceList = null) : BaseCreateDto;
}

View File

@@ -0,0 +1,21 @@
using DigitalData.UserManager.Application.DTOs.User;
using WorkFlow.Application.DTO.Profile;
namespace WorkFlow.Application.DTO.ProfileControlsTF
{
public record ProfileControlsTFDto(int Id,
int ProfileId,
int UserId,
long ObjId,
string ObjType,
string AttrName,
string CtrlType,
string CtrlCaption,
bool Mandatory,
bool ReadOnly,
string AddedWho,
DateTime AddedWhen,
string? ChoiceList = null,
ProfileDto? Profile = null,
UserReadDto? User = null);
}

View File

@@ -0,0 +1,9 @@
using DigitalData.Core.Abstractions;
namespace WorkFlow.Application.DTO.ProfileControlsTF
{
/// <summary>
/// This Data Transfer Object (DTO) serves as a placeholder and does not support updates.
/// </summary>
public record ProfileControlsTFUpdateDto(int Id) : IUnique<int>;
}

View File

@@ -0,0 +1,11 @@
namespace WorkFlow.Application.DTO.ProfileObjState
{
public record ProfileObjStateCreateDto(
int ProfileId,
int UserId,
long ObjId,
int StateId,
string? State2 = null,
string? State3 = null,
string? State4 = null) : BaseCreateDto;
}

View File

@@ -0,0 +1,20 @@
using DigitalData.UserManager.Application.DTOs.User;
using WorkFlow.Application.DTO.Profile;
using WorkFlow.Application.DTO.State;
namespace WorkFlow.Application.DTO.ProfileObjState
{
public record ProfileObjStateDto(int Id,
int ProfileId,
int UserId,
long ObjId,
int StateId,
string AddedWho,
DateTime AddedWhen,
string? State2 = null,
string? State3 = null,
string? State4 = null,
ProfileDto? Profile = null,
UserReadDto? User = null,
StateDto? State = null);
}

View File

@@ -0,0 +1,9 @@
using DigitalData.Core.Abstractions;
namespace WorkFlow.Application.DTO.ProfileObjState
{
/// <summary>
/// This Data Transfer Object (DTO) serves as a placeholder and does not support updates.
/// </summary>
public record ProfileObjStateUpdateDto(int Id) : IUnique<int>;
}

View File

@@ -0,0 +1,4 @@
namespace WorkFlow.Application.DTO.State
{
public record StateCreateDto(string IntlState) : BaseCreateDto;
}

View File

@@ -0,0 +1,4 @@
namespace WorkFlow.Application.DTO.State
{
public record StateDto(int Id, string IntlState, string AddedWho, DateTime AddedWhen);
}

View File

@@ -0,0 +1,9 @@
using DigitalData.Core.Abstractions;
namespace WorkFlow.Application.DTO.State
{
/// <summary>
/// This Data Transfer Object (DTO) serves as a placeholder and does not support updates.
/// </summary>
public record StateUpdateDto(int Id) : IUnique<int>;
}

View File

@@ -0,0 +1,37 @@
using AutoMapper;
using WorkFlow.Application.DTO.Config;
using WorkFlow.Application.DTO.Profile;
using WorkFlow.Application.DTO.ProfileControlsTF;
using WorkFlow.Application.DTO.ProfileObjState;
using WorkFlow.Application.DTO.State;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application
{
public class MappingProfile : AutoMapper.Profile
{
public MappingProfile()
{
// Mapping entity to DTO
CreateMap<Config, ConfigDto>();
CreateMap<Domain.Entities.Profile, ProfileDto>();
CreateMap<ProfileControlsTF, ProfileControlsTFDto>();
CreateMap<ProfileObjState, ProfileObjStateDto>();
CreateMap<State, StateDto>();
// Mapping create-DTO to entity
CreateMap<ConfigCreateDto, Config>();
CreateMap<ProfileCreateDto, Domain.Entities.Profile>();
CreateMap<ProfileControlsTFCreateDto, ProfileControlsTF>();
CreateMap<ProfileObjStateCreateDto, ProfileObjState>();
CreateMap<StateCreateDto, State>();
// Mapping update-DTO to entity
CreateMap<ConfigUpdateDto, Config>();
CreateMap<ProfileUpdateDto, Domain.Entities.Profile>();
CreateMap<ProfileControlsTFUpdateDto, ProfileControlsTF>();
CreateMap<ProfileObjStateUpdateDto, ProfileObjState>();
CreateMap<StateUpdateDto, State>();
}
}
}

View File

@@ -0,0 +1,17 @@
using AutoMapper;
using DigitalData.Core.Abstractions.Application;
using DigitalData.Core.Application;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.Config;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Application.Services;
public class ConfigService : CRUDService<IConfigRepository, ConfigCreateDto, ConfigDto, Config, int>,
IConfigService, ICRUDService<ConfigCreateDto, ConfigDto, Config, int>
{
public ConfigService(IConfigRepository repository, IMapper mapper) : base(repository, mapper)
{
}
}

View File

@@ -0,0 +1,35 @@
using AutoMapper;
using DigitalData.Core.Abstractions.Application;
using DigitalData.Core.Application;
using DigitalData.Core.DTO;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.ProfileControlsTF;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Application.Services
{
public class ProfileControlsTFService : CRUDService<IProfileControlsTFRepository, ProfileControlsTFCreateDto, ProfileControlsTFDto, ProfileControlsTF, int>,
IProfileControlsTFService, ICRUDService<ProfileControlsTFCreateDto, ProfileControlsTFDto, ProfileControlsTF, int>
{
public ProfileControlsTFService(IProfileControlsTFRepository repository, IMapper mapper) : base(repository, mapper)
{
}
public async Task<DataResult<IEnumerable<ProfileControlsTFDto>>> ReadAsync(
bool withProfile = true, bool withUser = false,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null)
{
var pctf_list = await _repository.ReadAsync(
isReadonly: true,
withProfile: withProfile, withUser: withUser,
userId: userId, username: username,
profileId: profileId, objId: objId, profileActive: profileActive);
var pctf_dto_list = _mapper.Map<IEnumerable<ProfileControlsTFDto>>(pctf_list);
return Result.Success(pctf_dto_list);
}
}
}

View File

@@ -0,0 +1,34 @@
using AutoMapper;
using DigitalData.Core.Abstractions.Application;
using DigitalData.Core.Application;
using DigitalData.Core.DTO;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.ProfileObjState;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Application.Services;
public class ProfileObjStateService : CRUDService<IProfileObjStateRepository, ProfileObjStateCreateDto, ProfileObjStateDto, ProfileObjState, int>,
IProfileObjStateService, ICRUDService<ProfileObjStateCreateDto, ProfileObjStateDto, ProfileObjState, int>
{
public ProfileObjStateService(IProfileObjStateRepository repository, IMapper mapper) : base(repository, mapper)
{
}
public async Task<DataResult<IEnumerable<ProfileObjStateDto>>> ReadAsync(
bool withProfile = true, bool withUser = true, bool withState = true,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null)
{
var pos_list = await _repository.ReadAsync(
isReadonly: true,
withProfile: withProfile, withUser: withUser, withState: withState,
userId: userId, username: username,
profileId: profileId, objId: objId, profileActive: profileActive);
var post_dto_list = _mapper.Map<IEnumerable<ProfileObjStateDto>>(pos_list);
return Result.Success(post_dto_list);
}
}

View File

@@ -0,0 +1,16 @@
using DigitalData.Core.Abstractions.Application;
using DigitalData.Core.Application;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.Profile;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Application.Services;
public class ProfileService : CRUDService<IProfileRepository, ProfileCreateDto, ProfileDto, Profile, int>,
IProfileService, ICRUDService<ProfileCreateDto, ProfileDto, Profile, int>
{
public ProfileService(IProfileRepository repository, AutoMapper.IMapper mapper) : base(repository, mapper)
{
}
}

View File

@@ -0,0 +1,17 @@
using AutoMapper;
using DigitalData.Core.Abstractions.Application;
using DigitalData.Core.Application;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.State;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Application.Services;
public class StateService : CRUDService<IStateRepository, StateCreateDto, StateDto, State, int>,
IStateService, ICRUDService<StateCreateDto, StateDto, State, int>
{
public StateService(IStateRepository repository, IMapper mapper) : base(repository, mapper)
{
}
}

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DigitalData.Core.Application" Version="3.2.0" />
<PackageReference Include="UserManager.Application" Version="3.1.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WorkFlow.Domain\WorkFlow.Domain.csproj" />
<ProjectReference Include="..\WorkFlow.Infrastructure\WorkFlow.Infrastructure.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,10 +1,11 @@
using System.ComponentModel.DataAnnotations;
using DigitalData.Core.Abstractions;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities
{
[Table("TBMWF_CONFIG", Schema = "dbo")]
public class Config
public class Config : IUnique<int>
{
[Key]
[Column("GUID")]
@@ -12,11 +13,11 @@ namespace WorkFlow.Domain.Entities
[Required]
[Column("CONF_TITLE", TypeName = "varchar(100)")]
public required string ConfTitle { get; init; }
public required string Title { get; init; }
[Required]
[Column("CONF_STRING", TypeName = "varchar(900)")]
public required string ConfString { get; init; }
public required string String { get; init; }
[Required]
[Column("ADDED_WHO", TypeName = "varchar(30)")]

View File

@@ -1,10 +1,11 @@
using System.ComponentModel.DataAnnotations;
using DigitalData.Core.Abstractions;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities
{
[Table("TBMWF_PROFILE", Schema = "dbo")]
public class Profile
public class Profile : IUnique<int>
{
[Key]
[Column("GUID")]

View File

@@ -1,10 +1,12 @@
using System.ComponentModel.DataAnnotations;
using DigitalData.Core.Abstractions;
using DigitalData.UserManager.Domain.Entities;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities
{
[Table("TBMWF_PROF_CONTROLS_TF", Schema = "dbo")]
public class ProfControlsTf
public class ProfileControlsTF : IUnique<int>
{
[Key]
[Column("GUID")]
@@ -12,11 +14,11 @@ namespace WorkFlow.Domain.Entities
[Required]
[Column("MWF_PROFILE_ID")]
public required int MwfProfileId { get; init; }
public required int ProfileId { get; init; }
[Required]
[Column("USR_ID")]
public required int UsrId { get; init; }
public required int UserId { get; init; }
[Required]
[Column("OBJ_ID")]
@@ -57,7 +59,10 @@ namespace WorkFlow.Domain.Entities
[Column("ADDED_WHEN", TypeName = "datetime")]
public required DateTime AddedWhen { get; init; }
[ForeignKey("MwfProfileId")]
public Profile? MwfProfile { get; init; } = default;
[ForeignKey("ProfileId")]
public Profile? Profile { get; init; } = default;
[ForeignKey("UserId")]
public User? User { get; set; } = default;
}
}

View File

@@ -1,10 +1,12 @@
using System.ComponentModel.DataAnnotations;
using DigitalData.Core.Abstractions;
using DigitalData.UserManager.Domain.Entities;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities
{
[Table("TBMWF_PROFILE_OBJ_STATE", Schema = "dbo")]
public class ProfileObjState
public class ProfileObjState : IUnique<int>
{
[Key]
[Column("GUID")]
@@ -12,11 +14,11 @@ namespace WorkFlow.Domain.Entities
[Required]
[Column("MWF_PROFILE_ID")]
public required int MwfProfileId { get; init; }
public required int ProfileId { get; init; }
[Required]
[Column("USR_ID")]
public required int UsrId { get; init; }
public required int UserId { get; init; }
[Required]
[Column("OBJ_ID")]
@@ -42,5 +44,14 @@ namespace WorkFlow.Domain.Entities
[Required]
[Column("ADDED_WHEN", TypeName = "datetime")]
public required DateTime AddedWhen { get; init; }
[ForeignKey("ProfileId")]
public Profile? Profile { get; init; } = null;
[ForeignKey("UserId")]
public User? User { get; init; } = null;
[ForeignKey("StateId")]
public State? State { get; init; } = null;
}
}

View File

@@ -1,10 +1,11 @@
using System.ComponentModel.DataAnnotations;
using DigitalData.Core.Abstractions;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities
{
[Table("TBMWF_WF_STATE", Schema = "dbo")]
public class WfState
public class State : IUnique<int>
{
[Key]
[Column("GUID")]

View File

@@ -1,9 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="UserManager.Domain" Version="3.0.1" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,9 @@
using DigitalData.Core.Abstractions.Infrastructure;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Infrastructure.Contracts
{
public interface IConfigRepository : ICRUDRepository<Config, int>
{
}
}

View File

@@ -0,0 +1,14 @@
using DigitalData.Core.Abstractions.Infrastructure;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Infrastructure.Contracts
{
public interface IProfileControlsTFRepository : ICRUDRepository<ProfileControlsTF, int>
{
Task<IEnumerable<ProfileControlsTF>> ReadAsync(
bool isReadonly = true,
bool withProfile = true, bool withUser = false,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null);
}
}

View File

@@ -0,0 +1,14 @@
using DigitalData.Core.Abstractions.Infrastructure;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Infrastructure.Contracts
{
public interface IProfileObjStateRepository : ICRUDRepository<ProfileObjState, int>
{
Task<IEnumerable<ProfileObjState>> ReadAsync(
bool isReadonly = true,
bool withProfile = true, bool withUser = true, bool withState = true,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null);
}
}

View File

@@ -0,0 +1,9 @@
using DigitalData.Core.Abstractions.Infrastructure;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Infrastructure.Contracts
{
public interface IProfileRepository : ICRUDRepository<Profile, int>
{
}
}

View File

@@ -0,0 +1,9 @@
using DigitalData.Core.Abstractions.Infrastructure;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Infrastructure.Contracts
{
public interface IStateRepository : ICRUDRepository<State, int>
{
}
}

View File

@@ -0,0 +1,21 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using WorkFlow.Infrastructure.Contracts;
using WorkFlow.Infrastructure.Repositories;
namespace WorkFlow.Infrastructure
{
public static class DIExtensions
{
public static IServiceCollection AddWorkFlowRepositories(this IServiceCollection services)
{
services.TryAddScoped<IConfigRepository, ConfigRepository>();
services.TryAddScoped<IProfileControlsTFRepository, ProfileControlsTFRepository>();
services.TryAddScoped<IProfileObjStateRepository, ProfileObjStateRepository>();
services.TryAddScoped<IProfileRepository, ProfileRepository>();
services.TryAddScoped<IStateRepository, StateRepository>();
return services;
}
}
}

View File

@@ -0,0 +1,15 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.Core.Infrastructure;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Infrastructure.Repositories
{
//TODO: Make the db context type generic so that it can be used by other projects with different db contexts.
public class ConfigRepository : CRUDRepository<Config, int, WFDBContext>, IConfigRepository, ICRUDRepository<Config, int>
{
public ConfigRepository(WFDBContext dbContext) : base(dbContext, dbContext.Configs)
{
}
}
}

View File

@@ -0,0 +1,57 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.Core.Infrastructure;
using Microsoft.EntityFrameworkCore;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Infrastructure.Repositories
{
public class ProfileControlsTFRepository : CRUDRepository<ProfileControlsTF, int, WFDBContext>, IProfileControlsTFRepository, ICRUDRepository<ProfileControlsTF, int>
{
public ProfileControlsTFRepository(WFDBContext dbContext) : base(dbContext, dbContext.ProfileControlsTFs)
{
}
protected override IQueryable<ProfileControlsTF> ReadOnly() => base.ReadOnly().Include(pctf => pctf.Profile).Include(pctf => pctf.User);
protected IQueryable<ProfileControlsTF> Read(bool isReadonly = false, bool withProfile = true, bool withUser = true, int? profileId = null, int? userId = null, string? username = null, int? objId = null, bool? profileActive = null)
{
var query = isReadonly ? _dbSet.AsNoTracking() : _dbSet.AsQueryable();
if (withProfile)
query = query.Include(pctf => pctf.Profile);
if (withUser)
query = query.Include(pctf => pctf.User);
if (profileId is not null)
query = query.Where(pctf => pctf.ProfileId == profileId);
if (userId is not null)
query = query.Where(pctf => pctf.UserId == userId);
if (username is null)
query = query.Where(pctf => pctf.User!.Username == username);
if (objId is not null)
query = query.Where(pctf => pctf.ObjId == objId);
if (profileActive is not null)
query = query.Where(pctf => pctf.Profile!.Active == profileActive);
return query;
}
public async Task<IEnumerable<ProfileControlsTF>> ReadAsync(
bool isReadonly = true,
bool withProfile = true, bool withUser = true,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null)
=> await Read(
isReadonly: isReadonly,
withProfile: withProfile, withUser: withUser,
userId: userId, username: username,
profileId: profileId, objId: objId, profileActive: profileActive)
.ToListAsync();
}
}

View File

@@ -0,0 +1,62 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.Core.Infrastructure;
using Microsoft.EntityFrameworkCore;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Infrastructure.Repositories;
public class ProfileObjStateRepository : CRUDRepository<ProfileObjState, int, WFDBContext>, IProfileObjStateRepository, ICRUDRepository<ProfileObjState, int>
{
public ProfileObjStateRepository(WFDBContext dbContext) : base(dbContext, dbContext.ProfileObjStates)
{
}
protected override IQueryable<ProfileObjState> ReadOnly() => base.ReadOnly().Include(pos => pos.Profile).Include(pos => pos.State);
protected IQueryable<ProfileObjState> Read(bool isReadonly = false, bool withProfile = true, bool withUser = true, bool withState = true, int? profileId = null, int? userId = null, string? username = null, int? stateId = null, int? objId = null, bool? profileActive = null)
{
var query = isReadonly ? _dbSet.AsNoTracking() : _dbSet.AsQueryable();
if (withProfile)
query = query.Include(pctf => pctf.Profile);
if (withUser)
query = query.Include(pctf => pctf.User);
if (withState)
query = query.Include(pctf => pctf.State);
if (profileId is not null)
query = query.Where(pctf => pctf.ProfileId == profileId);
if (userId is not null)
query = query.Where(pctf => pctf.UserId == userId);
if (username is null)
query = query.Where(pctf => pctf.User!.Username == username);
if (stateId is null)
query = query.Where(pctf => pctf.State!.Id == stateId);
if (objId is not null)
query = query.Where(pctf => pctf.ObjId == objId);
if (profileActive is not null)
query = query.Where(pctf => pctf.Profile!.Active == profileActive);
return query;
}
public async Task<IEnumerable<ProfileObjState>> ReadAsync(
bool isReadonly = true,
bool withProfile = true, bool withUser = true, bool withState = true,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null)
=> await Read(
isReadonly: isReadonly,
withProfile: withProfile, withUser: withUser, withState: withState,
userId: userId, username: username,
profileId: profileId, objId: objId, profileActive: profileActive)
.ToListAsync();
}

View File

@@ -0,0 +1,13 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.Core.Infrastructure;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Infrastructure.Repositories;
public class ProfileRepository : CRUDRepository<Profile, int, WFDBContext>, IProfileRepository, ICRUDRepository<Profile, int>
{
public ProfileRepository(WFDBContext dbContext) : base(dbContext, dbContext.Profiles)
{
}
}

View File

@@ -0,0 +1,13 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.Core.Infrastructure;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Infrastructure.Repositories;
public class StateRepository : CRUDRepository<State, int, WFDBContext>, IStateRepository, ICRUDRepository<State, int>
{
public StateRepository(WFDBContext dbContext) : base(dbContext, dbContext.States)
{
}
}

View File

@@ -1,18 +1,46 @@
using Microsoft.EntityFrameworkCore;
using DigitalData.UserManager.Domain.Entities;
using DigitalData.UserManager.Infrastructure;
using DigitalData.UserManager.Infrastructure.Contracts;
using Microsoft.EntityFrameworkCore;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Infrastructure
namespace WorkFlow.Infrastructure;
public class WFDBContext : DbContext, IUserManagerDbContext
{
public class WFDBContext(DbContextOptions options) : DbContext(options)
public DbSet<Config> Configs { get; set; }
public DbSet<ProfileControlsTF> ProfileControlsTFs { get; set; }
public DbSet<Profile> Profiles { get; set; }
public DbSet<ProfileObjState> ProfileObjStates { get; set; }
public DbSet<State> States { get; set; }
public DbSet<GroupOfUser> GroupOfUsers { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<ModuleOfUser> ModuleOfUsers { get; set; }
public DbSet<Module> Modules { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UserRep> UserReps { get; set; }
public DbSet<ClientUser> ClientUsers { get; set; }
public WFDBContext(DbContextOptions options) : base(options)
{
public DbSet<Config> Configs { get; set; }
}
public DbSet<ProfControlsTf> ProfControlsTf { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//configure model builder for user manager tables
modelBuilder.ConfigureUserManager();
public DbSet<Profile> Profiles { get; set; }
public DbSet<ProfileObjState> ProfileObjStates { get; set; }
public DbSet<WfState> WfStates { get; set; }
base.OnModelCreating(modelBuilder);
}
}

View File

@@ -1,13 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DigitalData.Core.Infrastructure" Version="2.0.0" />
<PackageReference Include="UserManager.Infrastructure" Version="3.0.1" />
</ItemGroup>
<ItemGroup>

View File

@@ -5,7 +5,11 @@ VisualStudioVersion = 17.9.34622.214
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkFlow.Domain", "WorkFlow.Domain\WorkFlow.Domain.csproj", "{71E9264E-A2F0-4E5A-B010-8E4618C0C6AC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkFlow.Infrastructure", "WorkFlow.Infrastructure\WorkFlow.Infrastructure.csproj", "{62526D0D-3365-4113-854A-3656191D7C63}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkFlow.Infrastructure", "WorkFlow.Infrastructure\WorkFlow.Infrastructure.csproj", "{62526D0D-3365-4113-854A-3656191D7C63}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkFlow.Application", "WorkFlow.Application\WorkFlow.Application.csproj", "{5700B5DD-D17E-4E17-ADE5-48C95A0CC364}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkFlow.API", "WorkFlow.API\WorkFlow.API.csproj", "{4FB33592-EF0D-47C3-9CDE-03B2EF12BE00}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -21,6 +25,14 @@ Global
{62526D0D-3365-4113-854A-3656191D7C63}.Debug|Any CPU.Build.0 = Debug|Any CPU
{62526D0D-3365-4113-854A-3656191D7C63}.Release|Any CPU.ActiveCfg = Release|Any CPU
{62526D0D-3365-4113-854A-3656191D7C63}.Release|Any CPU.Build.0 = Release|Any CPU
{5700B5DD-D17E-4E17-ADE5-48C95A0CC364}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5700B5DD-D17E-4E17-ADE5-48C95A0CC364}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5700B5DD-D17E-4E17-ADE5-48C95A0CC364}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5700B5DD-D17E-4E17-ADE5-48C95A0CC364}.Release|Any CPU.Build.0 = Release|Any CPU
{4FB33592-EF0D-47C3-9CDE-03B2EF12BE00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FB33592-EF0D-47C3-9CDE-03B2EF12BE00}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FB33592-EF0D-47C3-9CDE-03B2EF12BE00}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FB33592-EF0D-47C3-9CDE-03B2EF12BE00}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE