Compare commits

..

5 Commits

Author SHA1 Message Date
Developer 02
25a4b0752b refactor: Entfernen der right_group-Eigenschaft aus der Representation-Entität
- Die Spalte `right_group` aus der `Representation`-Entität entfernt, um die Zuordnung von Benutzern oder Gruppen zu spezifischen Gruppen zu entfernen.
- Stattdessen wurde die `group`-Eigenschaft hinzugefügt, um flexible Zuordnungen zu ermöglichen.
- Ermöglicht nun `user-user`, `user-group`, `group-user` und `group-group` Repräsentationen.
2024-09-09 17:25:12 +02:00
Developer 02
75e708d02d chore: Entfernen der standardmäßig zugewiesenen AD Sync- und Internal-Boxen 2024-09-09 13:34:03 +02:00
Developer 02
dfe848100a feat: Entfernen der EcmFkId-Eigenschaft aus GroupCreateDto und dem Formular zur Gruppenerstellung
- `EcmFkId`-Eigenschaft aus dem `GroupCreateDto` im Backend entfernt.
- Anpassungen im Frontend vorgenommen, um das `EcmFkId`-Feld im Formular zur Gruppenerstellung zu entfernen.
- `EcmFkId` wird im Entity standardmäßig auf `-1` gesetzt.
2024-09-09 13:23:45 +02:00
Developer 02
e6416f0d7f feat(security): Nur Benutzer-ID und Passwort im Connection String verschlüsseln
- Verschlüsselung des gesamten Connection Strings durch Verschlüsselung der Benutzer-ID und des Passworts ersetzt.
- Die `appsettings`-Datei wurde aktualisiert, um nur noch die Benutzer-ID und das Passwort verschlüsselt zu speichern.
- `program.cs` angepasst, um den Connection String zu entschlüsseln und die Benutzer-ID sowie das Passwort separat zu entschlüsseln.
2024-09-09 12:55:22 +02:00
Developer 02
15bc34ba95 chore(config): Verbindungsschlüssel umbenennen 2024-09-09 11:50:38 +02:00
22 changed files with 107 additions and 120 deletions

View File

@@ -11,15 +11,6 @@
}
</mat-form-field>
</div>
<div [ngClass]="formFieldBSClass">
<mat-form-field>
<mat-label>ECM FK ID</mat-label>
<input matInput type="number" [formControl]="ecmFkId" (blur)="updateErrorMessage()" required />
@if (groupname.invalid) {
<mat-error>{{errorMessage()}}</mat-error>
}
</mat-form-field>
</div>
<div [ngClass]="formFieldBSClass">
<div [ngClass]="buttonBSClass">
<button mat-fab extended (click)="create()">
@@ -30,16 +21,10 @@
</div>
</div>
<div class="row">
<div [ngClass]="checkBoxBSClass(3)">
<mat-checkbox [disabled]="true">AD Sync</mat-checkbox>
</div>
<div [ngClass]="checkBoxBSClass(3)">
<mat-checkbox [(ngModel)]="checked" [disabled]="true">Internal</mat-checkbox>
</div>
<div [ngClass]="checkBoxBSClass(2)">
<div [ngClass]="formFieldBSClass">
<mat-checkbox [formControl]="active" [disabled]="true">Active</mat-checkbox>
</div>
<div class="col d-flex justify-content-center me-4 my-2">
<div [ngClass]="formFieldBSClass">
<button mat-fab extended (click)="delete()">
<mat-icon>delete</mat-icon>
Löschen

View File

@@ -25,21 +25,19 @@ import Swal from 'sweetalert2';
})
export class GroupFormComponent {
readonly groupname = new FormControl('', [Validators.required]);
readonly ecmFkId = new FormControl<number>(1, [Validators.required]);
readonly active = new FormControl<boolean>(true);
errorMessage = signal('');
public readonly formFieldBSClass: string = "col d-flex justify-content-center mx-1 my-2"
public readonly buttonBSClass: string = "d-flex justify-content-center mx-1 my-2"
public readonly checkBoxBSClass: (colCount: number | undefined) => string = (colCount: number = 2) => `col-${colCount} d-flex justify-content-left mx-1 my-2`
//public readonly checkBoxBSClass: (colCount: number | undefined) => string = (colCount: number = 2) => `col-${colCount} d-flex justify-content-left mx-1 my-2`
readonly checked = model(true);
constructor(private uService: UserService, private rService: RefreshService, private gService: GroupService) {
merge(
this.groupname.statusChanges, this.groupname.valueChanges,
this.ecmFkId.statusChanges, this.ecmFkId.valueChanges)
this.groupname.statusChanges, this.groupname.valueChanges)
.pipe(takeUntilDestroyed())
.subscribe(() => this.updateErrorMessage());
}
@@ -53,10 +51,9 @@ export class GroupFormComponent {
}
create() {
if (this.groupname.valid && this.ecmFkId.valid) {
if (this.groupname.valid) {
this.gService.create({
name: this.groupname.value!,
ecmFkId: (this.ecmFkId.value!),
adSync: false,
internal: true,
active: this.active.value!
@@ -76,7 +73,6 @@ export class GroupFormComponent {
delete() {
this.groupname.setValue('')
this.ecmFkId.setValue(1)
this.active.setValue(true)
}
}

View File

@@ -22,13 +22,13 @@ export class UserRepTableComponent extends BaseTableComponent<UserRep, UserRepSe
this.loading = false
}
override fetchData(userId?: number): void {
this.service.getAll(false, true, true, true, userId).subscribe({
override fetchData(userId?: number, groupId?: number): void {
this.service.getAll({ withUser: false, withRepGroup: true, withGroup: false, withRepUser: true, userId: userId, groupId: groupId }).subscribe({
next: (response: UserRep[]) => {
this.source = response;
this.loading = false;
},
error: (error: any) => {}
error: (error: any) => { }
});
}
}

View File

@@ -17,7 +17,6 @@ export interface User {
export interface Group {
id?: number;
ecmFkId: number;
name?: string;
adSync?: boolean;
internal?: boolean;
@@ -59,12 +58,12 @@ export interface UserRep {
id?: number,
userId: number,
repGroupId?: number,
rightGroupId: number,
groupId?: number,
addedWho: string,
repUserId?: number,
user?: User,
repGroup?: Group,
rightGroup?: Group,
group?: Group,
repUser?: User
}

View File

@@ -7,10 +7,13 @@
<mat-tab label="Benutzer">
<app-user-table #users [onSelectedRows]="userOnSelectedRows"></app-user-table>
</mat-tab>
<mat-tab label="Gruppe">
<app-group-table #rightGroups [columns]="groupColumns" [onSelectedRows]="groupOnSelectedRows"></app-group-table>
</mat-tab>
</mat-tab-group>
</div>
<!-- (1, 2): representations -->
<div class="col-2">
<div class="col-4">
<mat-tab-group>
<mat-tab label="Rep. Benutzer">
<app-user-table #repUsers [onSelectedRows]="repUserOnSelectedRows"></app-user-table>
@@ -20,14 +23,6 @@
</mat-tab>
</mat-tab-group>
</div>
<!-- (1, 3): right groups -->
<div class="col-2">
<mat-tab-group>
<mat-tab label="Rechte Gruppe">
<app-group-table #rightGroups] [columns]="groupRightColumns" [onSelectedRows]="rightGroupOnSelectedRows"></app-group-table>
</mat-tab>
</mat-tab-group>
</div>
<!-- (1, 4): assigned users -->
<div class="col-3">
<mat-tab-group>

View File

@@ -8,6 +8,7 @@ import Swal from 'sweetalert2';
import { MatTabsModule, MatTabGroup } from '@angular/material/tabs';
import { env } from '../../../environments/environment';
import { BasePageComponent } from '../base-page/base-page.component';
import { UserRep } from '../../models/user-management.api.models';
@Component({
standalone: true,
@@ -19,8 +20,8 @@ import { BasePageComponent } from '../base-page/base-page.component';
export class UserRepresentationComponent extends BasePageComponent implements AfterViewInit {
useRepLabel: string = "";
groupColumns: Array<GuiColumn>;
groupRepCols: Array<GuiColumn>;
groupRightColumns: Array<GuiColumn>;
slUserId: null | number = null;
slRepUserId: null | number = null;
slRepGroupId: null | number = null;
@@ -32,7 +33,7 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
constructor(private userRepService: UserRepService) {
super()
this.groupRepCols = env.columnNames.group.representative;
this.groupRightColumns = env.columnNames.group.right;
this.groupColumns = env.columnNames.group.right;
this.userRepService = userRepService;
}
@@ -43,7 +44,7 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
this.users.fetchData();
this.repUsers.fetchData();
this.repGroups.fetchData();
this.rightGroups.fetchData();
this.groups.fetchData();
})
this.transferService.add(() => {
this.repUsers.safelyUnselectAll();
@@ -53,26 +54,34 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
}
@ViewChild("users") users!: UserTableComponent;
@ViewChild("groups") groups!: GroupTableComponent;
@ViewChild("repUsers") repUsers!: UserTableComponent;
@ViewChild("repGroups") repGroups!: GroupTableComponent;
@ViewChild("rightGroups") rightGroups!: GroupTableComponent;
@ViewChild("userReps") userReps!: UserRepTableComponent;
userOnSelectedRows = (rows: GuiSelectedRow[]) => {
userGroupOnSelectedRows = (rows: GuiSelectedRow[], isUser: boolean = true) => {
if (rows.length > 0) {
this.users.safelyUnselectAll();
this.useRepLabel = `Vertretungen von ${rows[0].source?.username}`
this.userReps.fetchData(rows[0].source?.id)
this.slUserId = rows[0].source?.id
if (isUser) {
this.useRepLabel = `Vertretungen von ${rows[0].source?.username}`
this.userReps.fetchData(rows[0].source?.id)
this.slUserId = rows[0].source?.id
}
else {
this.useRepLabel = `Vertretungen von ${rows[0].source?.name}`
this.userReps.fetchData(undefined, rows[0].source?.id)
this.slUserId = rows[0].source?.id
}
}
}
rightGroupOnSelectedRows = (rows: GuiSelectedRow[]) => {
if (rows.length > 0) {
this.slRightGroupId = rows[0].source?.id
} else {
this.slRightGroupId = null;
}
userOnSelectedRows = (rows: GuiSelectedRow[]) => {
this.userGroupOnSelectedRows(rows, true);
}
groupOnSelectedRows = (rows: GuiSelectedRow[]) => {
this.userGroupOnSelectedRows(rows, false);
}
repUserOnSelectedRows = (rows: GuiSelectedRow[]) => {
@@ -91,17 +100,9 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
text: "Bitte wählen Sie die Vertretungen Benutzer!",
});
}
else if (!this.slRightGroupId) {
Swal.fire({
icon: "error",
title: "Oops...",
text: "Bitte wählen Sie die Rechte Gruppe!",
});
}
else {
var newUserRep = {
var newUserRep: UserRep = {
userId: this.slUserId,
rightGroupId: this.slRightGroupId,
repUserId: this.slRepUserId,
addedWho: 'DEFAULT'
}
@@ -145,17 +146,9 @@ export class UserRepresentationComponent extends BasePageComponent implements Af
text: "Bitte wählen Sie die Vertretungen Gruppe!",
});
}
else if (!this.slRightGroupId) {
Swal.fire({
icon: "error",
title: "Oops...",
text: "Bitte wählen Sie die rechte Gruppe!",
});
}
else {
var newUserRep = {
userId: this.slUserId,
rightGroupId: this.slRightGroupId,
repGroupId: this.slRepGroupId,
addedWho: 'DEFAULT'
}

View File

@@ -9,28 +9,42 @@ import { UrlService } from "./url.service";
providedIn: "root"
})
export class UserRepService extends ApiService<UserRep> {
constructor(http: HttpClient, urlService : UrlService) {
constructor(http: HttpClient, urlService: UrlService) {
super(http, urlService.apiRoute.userRep)
}
override getAll(withUser: boolean = false, withRepGroup: boolean = false, withRightGroup: boolean = false, withRepUser: boolean = false, userId?: number): Observable<UserRep[]> {
override getAll(options?: Options): Observable<UserRep[]> {
let params = new HttpParams();
if (withUser) {
params = params.set('withUser', withUser);
}
if (withRepGroup) {
params = params.set('withRepGroup', withRepGroup);
}
if (withRightGroup) {
params = params.set('withRightGroup', withRightGroup);
}
if (withRepUser) {
params = params.set('withRepUser', withRepUser);
}
if (userId) {
params = params.set('userId', userId)
if (options) {
if (options.withUser) {
params = params.set('withUser', options.withUser);
}
if (options.withRepGroup) {
params = params.set('withRepGroup', options.withRepGroup);
}
if (options.withGroup) {
params = params.set('withRightGroup', options.withGroup);
}
if (options.withRepUser) {
params = params.set('withRepUser', options.withRepUser);
}
if (options.userId) {
params = params.set('userId', options.userId)
}
if (options.groupId) {
params = params.set('groupId', options.groupId)
}
}
return this.http.get<UserRep[]>(`${this.baseUrl}`, { params: params, withCredentials: true });
}
}
interface Options {
withUser: boolean;
withRepGroup: boolean;
withGroup: boolean;
withRepUser: boolean;
userId?: number;
groupId?: number;
}

View File

@@ -189,10 +189,6 @@ export const env = {
header: "Repr. Gruppen",
field: (ur: any) => ur.repGroup?.name
},
{
header: "Rechte Gruppen",
field: (ur: any) => ur.rightGroup?.name
},
{
header: "Repr. Benutzer",
field: (ur: any) => ur.repUser?.username

View File

@@ -8,11 +8,13 @@ using System.Security.Claims;
using DigitalData.UserManager.Application;
using Microsoft.Extensions.Localization;
using DigitalData.Core.DTO;
using Microsoft.AspNetCore.Authorization;
namespace DigitalData.UserManager.API.Controllers
{
[Route("api/[controller]")]
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")]
[Authorize]
public class DirectoryController : ControllerBase
{
private readonly IUserService _userService;

View File

@@ -21,11 +21,11 @@ namespace DigitalData.UserManager.API.Controllers
}
[HttpGet]
public async Task<IActionResult> GetAll(bool withUser = false, bool withRepGroup = false, bool withRightGroup = false, bool withRepUser = false, int? userId = null)
public async Task<IActionResult> GetAll(bool withUser = false, bool withRepGroup = false, bool withGroup = false, bool withRepUser = false, int? userId = null, int? groupId = null)
{
try
{
return await _service.ReadAllAsync(withUser, withRepGroup, withRightGroup, withRepUser, userId).ThenAsync(Ok, IActionResult (m, n) =>
return await _service.ReadAllAsync(withUser, withRepGroup, withGroup, withRepUser, userId, groupId).ThenAsync(Ok, IActionResult (m, n) =>
{
_logger.LogNotice(n);
return NotFound();

View File

@@ -6,9 +6,9 @@ using Microsoft.AspNetCore.Authentication.Cookies;
using NLog.Web;
using NLog;
using DigitalData.Core.API;
using DigitalData.UserManager.API;
using DigitalData.UserManager.API.Controllers;
using DigitalData.UserManager.Application.Services;
using Microsoft.Data.SqlClient;
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger.Debug("init main");
@@ -72,7 +72,7 @@ try {
//builder.Services.AddAutoMapper(typeof(DirectoryMappingProfile).Assembly);
builder.Services.AddUserManager<UserManagerDbContext>();
builder.ConfigureBySection<DirectorySearchOptions>();
builder.Services.AddDirectorySearchService();
@@ -83,9 +83,13 @@ try {
cnn_str = new(() =>
{
var encryptor = app.Services.GetRequiredService<Encryptor>();
var eCnnStr = config.GetConnectionString("DD_ECM_Connection") ?? throw new InvalidOperationException("Connection string 'DD_ECM_Connection' is missing from the configuration.");
var cnnStr = encryptor.Decrypt(eCnnStr);
return cnnStr;
var eCnnStr = config.GetConnectionString("UM_DEF") ?? throw new InvalidOperationException("Connection string 'DD_ECM_Connection' is missing from the configuration.");
SqlConnectionStringBuilder cnnStrBuilder = new(eCnnStr);
cnnStrBuilder.UserID = encryptor.Decrypt(cnnStrBuilder.UserID);
cnnStrBuilder.Password = encryptor.Decrypt(cnnStrBuilder.Password);
var dCnnStr = cnnStrBuilder.ConnectionString;
return dCnnStr;
});
app.UseCors("DefaultCorsPolicy");

View File

@@ -6,7 +6,7 @@
}
},
"ConnectionStrings": {
"DD_ECM_Connection": "cIFSoeMqHel7SDkAj4MWjy1UHrNJgoHrLkBJ/I/1Y95MsV9vFQjJLn6Shm9qtAyymwSNrX9s+78mW2PX4KulSA/KAaRwNQteP6SHrX0nNOJptot8TcohuiT0m9K2M/GsJEnLyJ+3yb0nJHR5yzRaVvjl8ERhgntW47dFMni98YA="
"UM_DEF": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=g+2edXEbMbujCUjh7INZRQ==;Password=Bz/n9pu8EyzlVqicaMRQGQ==;Encrypt=false;TrustServerCertificate=True;"
},
"AllowedOrigins": [ "https://localhost:7103", "http://172.24.12.39:85", "http://localhost:85", "http://localhost:4200", "http://localhost:5500", "https://localhost:7202" ],
"RunAsWindowsService": false,

View File

@@ -6,6 +6,6 @@ namespace DigitalData.UserManager.Application.Contracts
{
public interface IUserRepService : IBaseService<UserRepCreateDto, UserRepReadDto, UserRepUpdateDto, UserRep>
{
Task<DataResult<IEnumerable<UserRepReadDto>>> ReadAllAsync(bool withUser = false, bool withRepGroup = false, bool withRightGroup = false, bool withRepUser = false, int? userId = null);
Task<DataResult<IEnumerable<UserRepReadDto>>> ReadAllAsync(bool withUser = false, bool withRepGroup = false, bool withGroup = false, bool withRepUser = false, int? userId = null, int? groupId = null);
}
}

View File

@@ -8,7 +8,6 @@ namespace DigitalData.UserManager.Application.DTOs.Group
bool? AdSync,
bool? Internal,
bool? Active,
string? Comment,
int EcmFkId
string? Comment
) : BaseCreateDto();
}

View File

@@ -5,8 +5,7 @@ namespace DigitalData.UserManager.Application.DTOs.UserRep
public record UserRepCreateDto(
int UserId,
int? RepGroupId,
int RightGroupId,
string AddedWho,
int? GroupId,
int RepUserId
) : BaseCreateDto();
}

View File

@@ -8,13 +8,13 @@ namespace DigitalData.UserManager.Application.DTOs.UserRep
int Id,
int UserId,
int? RepGroupId,
int RightGroupId,
int? GroupId,
string AddedWho,
string ChangedWho,
int? RepUserId,
UserReadDto? User,
GroupReadDto? RepGroup,
GroupReadDto? RightGroup,
GroupReadDto? Group,
UserReadDto? RepUser,
DateTime? AddedWhen,
DateTime? ChangedWhen

View File

@@ -5,7 +5,7 @@ namespace DigitalData.UserManager.Application.DTOs.UserRep
public record UserRepUpdateDto(
int UserId,
int? RepGroupId,
int RightGroupId,
int? GroupId,
int RepUserId
) : BaseUpdateDto();
}

View File

@@ -15,9 +15,9 @@ namespace DigitalData.UserManager.Application.Services
{
}
public async Task<DataResult<IEnumerable<UserRepReadDto>>> ReadAllAsync(bool withUser = false, bool withRepGroup = false, bool withRightGroup = false, bool withRepUser = false, int? userId = null)
public async Task<DataResult<IEnumerable<UserRepReadDto>>> ReadAllAsync(bool withUser = false, bool withRepGroup = false, bool withGroup = false, bool withRepUser = false, int? userId = null, int? groupId = null)
{
var urs = await _repository.ReadAllAsync(withUser, withRepGroup, withRightGroup, withRepUser, userId);
var urs = await _repository.ReadAllAsync(withUser, withRepGroup, withGroup, withRepUser, userId, groupId);
var urReadDTOs = _mapper.MapOrThrow<IEnumerable<UserRepReadDto>>(urs);
return Result.Success(urReadDTOs);
}

View File

@@ -26,9 +26,10 @@ namespace DigitalData.UserManager.Domain.Entities
[StringLength(200)]
public string? Comment { get; set; }
// TODO: this column should be assigned by triggers. despite this it is not null and this is problem for creation. talk with others
[Required]
[Column("ECM_FK_ID")]
[DefaultValue(0)]
public int EcmFkId { get; set; }
[DefaultValue(-1)]
public int EcmFkId { get; init; } = -1;
}
}

View File

@@ -14,9 +14,8 @@ namespace DigitalData.UserManager.Domain.Entities
[Column("REPR_GROUP")]
public int? RepGroupId { get; set; }
[Required]
[Column("RIGHT_GROUP")]
public int RightGroupId { get; set; }
[Column("GROUP_ID")]
public int? GroupId { get; set; } = null;
[Column("REPR_USER")]
public int? RepUserId { get; set; }
@@ -27,8 +26,8 @@ namespace DigitalData.UserManager.Domain.Entities
[ForeignKey("RepGroupId")]
public virtual Group? RepGroup { get; set; }
[ForeignKey("RightGroupId")]
public virtual Group? RightGroup { get; set; }
[ForeignKey("GroupId")]
public virtual Group? Group { get; set; }
[ForeignKey("RepUserId")]
public virtual User? RepUser { get; set; }

View File

@@ -5,6 +5,6 @@ namespace DigitalData.UserManager.Infrastructure.Contracts
{
public interface IUserRepRepository : ICRUDRepository<UserRep, int>
{
Task<IEnumerable<UserRep>> ReadAllAsync(bool withUser = false, bool withRepGroup = false, bool withRightGroup = false, bool withRepUser = false, int? userId = null);
Task<IEnumerable<UserRep>> ReadAllAsync(bool withUser = false, bool withRepGroup = false, bool withGroup = false, bool withRepUser = false, int? userId = null, int? groupId = null);
}
}

View File

@@ -12,9 +12,9 @@ namespace DigitalData.UserManager.Infrastructure.Repositories
{
}
public async Task<IEnumerable<UserRep>> ReadAllAsync(bool withUser = false, bool withRepGroup = false, bool withRightGroup = false, bool withRepUser = false, int? userId = null)
public async Task<IEnumerable<UserRep>> ReadAllAsync(bool withUser = false, bool withRepGroup = false, bool withGroup = false, bool withRepUser = false, int? userId = null, int? groupId = null)
{
var query = _dbSet.AsQueryable();
var query = _dbSet.AsNoTracking();
if (withUser)
query = query.Include(ur => ur.User);
@@ -22,8 +22,8 @@ namespace DigitalData.UserManager.Infrastructure.Repositories
if (withRepGroup)
query = query.Include(ur => ur.RepGroup);
if (withRightGroup)
query = query.Include(ur => ur.RightGroup);
if (withGroup)
query = query.Include(ur => ur.Group);
if (withRepUser)
query = query.Include(ur => ur.RepUser);
@@ -33,6 +33,11 @@ namespace DigitalData.UserManager.Infrastructure.Repositories
query = query.Where(ur => ur.UserId == userId);
}
if (groupId is not null)
{
query = query.Where(ur => ur.GroupId == groupId);
}
return await query.ToListAsync();
}
}