mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 13:20:08 +03:00
Compare commits
20
Commits
pdfiumissue
...
org
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
877caee396 | ||
|
|
0dd0e0c71e | ||
|
|
d1c8802890 | ||
|
|
31c397d016 | ||
|
|
e418e06ace | ||
|
|
9e78057a3e | ||
|
|
4437e1cf30 | ||
|
|
3f81adaf56 | ||
|
|
cac817ed56 | ||
|
|
3da9ae46b6 | ||
|
|
771ce3acfe | ||
|
|
a9def611f6 | ||
|
|
b049638f49 | ||
|
|
5761bfbffa | ||
|
|
3c6786f719 | ||
|
|
7af18cb10c | ||
|
|
42f5e68f48 | ||
|
|
92e7538182 | ||
|
|
5e5f2dda83 | ||
|
|
0966b919cb |
@@ -10,21 +10,25 @@ import lombok.RequiredArgsConstructor;
|
||||
@RequiredArgsConstructor
|
||||
public enum Role {
|
||||
|
||||
// Unlimited access
|
||||
// System-wide administrator - can manage all organizations
|
||||
SYSTEM_ADMIN(
|
||||
"ROLE_SYSTEM_ADMIN",
|
||||
Integer.MAX_VALUE,
|
||||
Integer.MAX_VALUE,
|
||||
"adminUserSettings.systemAdmin"),
|
||||
|
||||
// Organization administrator - can manage their organization and all its teams
|
||||
ORG_ADMIN("ROLE_ORG_ADMIN", Integer.MAX_VALUE, Integer.MAX_VALUE, "adminUserSettings.orgAdmin"),
|
||||
|
||||
// Team leader - can manage users in their specific team
|
||||
TEAM_LEAD("ROLE_TEAM_LEAD", Integer.MAX_VALUE, Integer.MAX_VALUE, "adminUserSettings.teamLead"),
|
||||
|
||||
// Legacy admin role - equivalent to SYSTEM_ADMIN for backward compatibility
|
||||
ADMIN("ROLE_ADMIN", Integer.MAX_VALUE, Integer.MAX_VALUE, "adminUserSettings.admin"),
|
||||
|
||||
// Unlimited access
|
||||
// Regular user with unlimited access within their team/org
|
||||
USER("ROLE_USER", Integer.MAX_VALUE, Integer.MAX_VALUE, "adminUserSettings.user"),
|
||||
|
||||
// 40 API calls Per Day, 40 web calls
|
||||
LIMITED_API_USER("ROLE_LIMITED_API_USER", 40, 40, "adminUserSettings.apiUser"),
|
||||
|
||||
// 20 API calls Per Day, 20 web calls
|
||||
EXTRA_LIMITED_API_USER("ROLE_EXTRA_LIMITED_API_USER", 20, 20, "adminUserSettings.extraApiUser"),
|
||||
|
||||
// 0 API calls per day and 20 web calls
|
||||
WEB_ONLY_USER("ROLE_WEB_ONLY_USER", 0, 20, "adminUserSettings.webOnlyUser"),
|
||||
|
||||
INTERNAL_API_USER(
|
||||
"STIRLING-PDF-BACKEND-API-USER",
|
||||
Integer.MAX_VALUE,
|
||||
@@ -63,4 +67,35 @@ public enum Role {
|
||||
}
|
||||
throw new IllegalArgumentException("No Role defined for id: " + roleId);
|
||||
}
|
||||
|
||||
/** Checks if this role can manage users across all organizations */
|
||||
public boolean isSystemAdmin() {
|
||||
return this == SYSTEM_ADMIN || this == ADMIN; // ADMIN for backward compatibility
|
||||
}
|
||||
|
||||
/** Checks if this role can manage an organization and all its teams */
|
||||
public boolean isOrgAdmin() {
|
||||
return isSystemAdmin() || this == ORG_ADMIN;
|
||||
}
|
||||
|
||||
/** Checks if this role can manage a specific team */
|
||||
public boolean isTeamLead() {
|
||||
return isOrgAdmin() || this == TEAM_LEAD;
|
||||
}
|
||||
|
||||
/** Gets the hierarchy level of this role (higher number = more permissions) */
|
||||
public int getHierarchyLevel() {
|
||||
return switch (this) {
|
||||
case SYSTEM_ADMIN, ADMIN -> 4;
|
||||
case ORG_ADMIN -> 3;
|
||||
case TEAM_LEAD -> 2;
|
||||
case USER -> 1;
|
||||
default -> 0; // Limited users
|
||||
};
|
||||
}
|
||||
|
||||
/** Checks if this role has higher or equal authority than another role */
|
||||
public boolean hasAuthorityOver(Role otherRole) {
|
||||
return this.getHierarchyLevel() >= otherRole.getHierarchyLevel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
multipart.enabled=true
|
||||
logging.level.org.springframework=WARN
|
||||
logging.level.org.hibernate=WARN
|
||||
#logging.level.root=DEBUG
|
||||
logging.level.org.eclipse.jetty=WARN
|
||||
#logging.level.org.springframework.security.saml2=TRACE
|
||||
#logging.level.org.springframework.security=DEBUG
|
||||
@@ -34,6 +35,7 @@ spring.datasource.driver-class-name=org.h2.Driver
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=
|
||||
spring.h2.console.enabled=false
|
||||
spring.h2.console.path=/h2-console
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
server.servlet.session.timeout:30m
|
||||
# Change the default URL path for OpenAPI JSON
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=هل يجب تعطيل/تمكين ال
|
||||
adminUserSettings.usernameInfo=يمكن أن يحتوي اسم المستخدم فقط على أحرف وأرقام والرموز الخاصة التالية @._+- أو يجب أن يكون عنوان بريد إلكتروني صالح.
|
||||
adminUserSettings.role=الدور
|
||||
adminUserSettings.actions=الإجراءات
|
||||
adminUserSettings.apiUser=مستخدم API محدود
|
||||
adminUserSettings.extraApiUser=مستخدم API محدود إضافي
|
||||
adminUserSettings.webOnlyUser=مستخدم الويب فقط
|
||||
adminUserSettings.demoUser=مستخدم تجريبي (بدون إعدادات مخصصة)
|
||||
adminUserSettings.internalApiUser=مستخدم API داخلي
|
||||
adminUserSettings.forceChange=إجبار المستخدم على تغيير كلمة المرور عند تسجيل الدخول
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=İstifadəçi aktivləşdirilməli/dea
|
||||
adminUserSettings.usernameInfo=İstifadəçi adı sadəcə hərflərdən, rəqəmlərdən və @._+- xüsusi simvollarından ibarət ola bilər və ya düzgün email ünvanı olmalıdır.
|
||||
adminUserSettings.role=Rol
|
||||
adminUserSettings.actions=Fəaliyyətlər
|
||||
adminUserSettings.apiUser=Məhdudlaşdırılmış API İstifadəçisi
|
||||
adminUserSettings.extraApiUser=Əlavə Məhdudlaşdırılmış API İstifadəçisi
|
||||
adminUserSettings.webOnlyUser=Yalnız Veb İstifadəçisi
|
||||
adminUserSettings.demoUser=Demo İstifadəçisi (Fərdi parametrlər yoxdur)
|
||||
adminUserSettings.internalApiUser=Daxili API İstifadəçisi
|
||||
adminUserSettings.forceChange=İstifadəçini giriş zamanı parolu dəyişməyə məcbur et
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Трябва ли потребител
|
||||
adminUserSettings.usernameInfo=Потребителското име може да съдържа само букви, цифри и следните специални символи @._+- или трябва да е валиден имейл адрес.
|
||||
adminUserSettings.role=Роля
|
||||
adminUserSettings.actions=Действия
|
||||
adminUserSettings.apiUser=Ограничен API потребител
|
||||
adminUserSettings.extraApiUser=Допълнителен ограничен API потребител
|
||||
adminUserSettings.webOnlyUser=Само за уеб-потребител
|
||||
adminUserSettings.demoUser=Демо потребител (без персонализирани настройки)
|
||||
adminUserSettings.internalApiUser=Вътрешен API потребител
|
||||
adminUserSettings.forceChange=Принудете потребителя да промени потребителското име/парола при влизане
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=སྤྱོད་མཁན་སྤ
|
||||
adminUserSettings.usernameInfo=སྤྱོད་མཁན་མིང་ནང་ཡི་གེ་དང་ཨང་ཀི། དམིགས་བསལ་མཚོན་རྟགས་ @._+- ཡང་ན་གློག་འཕྲིན་ཁ་བྱང་ཚད་ལྡན་ཞིག་དགོས།
|
||||
adminUserSettings.role=འགན་འཁུར།
|
||||
adminUserSettings.actions=བྱ་སྤྱོད།
|
||||
adminUserSettings.apiUser=ཚད་བཀག་ཅན་གྱི་ API སྤྱོད་མཁན།
|
||||
adminUserSettings.extraApiUser=ཚད་བཀག་ཅན་གྱི་ API སྤྱོད་མཁན་འཕར་མ།
|
||||
adminUserSettings.webOnlyUser=དྲ་ཚིགས་ཁོ་ནའི་སྤྱོད་མཁན།
|
||||
adminUserSettings.demoUser=བརྟག་དཔྱད་སྤྱོད་མཁན། (རང་སྒྲིག་མེད་པ།)
|
||||
adminUserSettings.internalApiUser=ནང་ཁུལ་ API སྤྱོད་མཁན།
|
||||
adminUserSettings.forceChange=ནང་འཛུལ་སྐབས་གསང་ཚིག་བསྒྱུར་དགོས་པ་བཟོ་བ།
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Vols deshabilitar/habilitar aquest usu
|
||||
adminUserSettings.usernameInfo=El nom d'usuari només pot contenir lletres, números i els següents caràcters especials: @._+- o ha de ser una adreça de correu electrònic vàlida.
|
||||
adminUserSettings.role=Rol
|
||||
adminUserSettings.actions=Accions
|
||||
adminUserSettings.apiUser=Usuari amb API limitada
|
||||
adminUserSettings.extraApiUser=Usuari Addicional amb API limitada
|
||||
adminUserSettings.webOnlyUser=Usuari només WEB
|
||||
adminUserSettings.demoUser=Usuari de Demo (Sense configuracions personalitzades)
|
||||
adminUserSettings.internalApiUser=Usuari d'API Interna
|
||||
adminUserSettings.forceChange=Força l'usuari a canviar la contrasenya en iniciar sessió
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Má být uživatel deaktivován/aktivo
|
||||
adminUserSettings.usernameInfo=Uživatelské jméno může obsahovat pouze písmena, číslice a následující speciální znaky @._+- nebo musí být platná e-mailová adresa.
|
||||
adminUserSettings.role=Role
|
||||
adminUserSettings.actions=Akce
|
||||
adminUserSettings.apiUser=Omezený API uživatel
|
||||
adminUserSettings.extraApiUser=Další omezený API uživatel
|
||||
adminUserSettings.webOnlyUser=Pouze webový uživatel
|
||||
adminUserSettings.demoUser=Demo uživatel (Bez vlastních nastavení)
|
||||
adminUserSettings.internalApiUser=Interní API uživatel
|
||||
adminUserSettings.forceChange=Vynutit změnu hesla při přihlášení
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Skal brugeren deaktiveres/aktiveres?
|
||||
adminUserSettings.usernameInfo=Brugernavn må kun indeholde bogstaver, tal og følgende specialtegn @._+- eller skal være en gyldig e-mailadresse.
|
||||
adminUserSettings.role=Rolle
|
||||
adminUserSettings.actions=Handlinger
|
||||
adminUserSettings.apiUser=Begrænset API-bruger
|
||||
adminUserSettings.extraApiUser=Yderligere Begrænset API-bruger
|
||||
adminUserSettings.webOnlyUser=Kun Web-bruger
|
||||
adminUserSettings.demoUser=Demo-bruger (Ingen brugerdefinerede indstillinger)
|
||||
adminUserSettings.internalApiUser=Intern API-bruger
|
||||
adminUserSettings.forceChange=Tving bruger til at ændre adgangskode ved login
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Soll der Benutzer deaktiviert/aktivier
|
||||
adminUserSettings.usernameInfo=Der Benutzername darf nur Buchstaben, Zahlen und die folgenden Sonderzeichen @._+- enthalten oder muss eine gültige E-Mail-Adresse sein.
|
||||
adminUserSettings.role=Rolle
|
||||
adminUserSettings.actions=Aktionen
|
||||
adminUserSettings.apiUser=Eingeschränkter API-Benutzer
|
||||
adminUserSettings.extraApiUser=Zusätzlicher eingeschränkter API-Benutzer
|
||||
adminUserSettings.webOnlyUser=Nur Web-Benutzer
|
||||
adminUserSettings.demoUser=Demo-Benutzer (Keine benutzerdefinierten Einstellungen)
|
||||
adminUserSettings.internalApiUser=Interner API-Benutzer
|
||||
adminUserSettings.forceChange=Benutzer dazu zwingen, Benutzernamen/Passwort bei der Anmeldung zu ändern
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Θέλετε να απενεργοπο
|
||||
adminUserSettings.usernameInfo=Το όνομα χρήστη μπορεί να περιέχει μόνο γράμματα, αριθμούς και τους ειδικούς χαρακτήρες @._+- ή πρέπει να είναι έγκυρη διεύθυνση email.
|
||||
adminUserSettings.role=Ρόλος
|
||||
adminUserSettings.actions=Ενέργειες
|
||||
adminUserSettings.apiUser=Περιορισμένος χρήστης API
|
||||
adminUserSettings.extraApiUser=Επιπλέον περιορισμένος χρήστης API
|
||||
adminUserSettings.webOnlyUser=Χρήστης μόνο web
|
||||
adminUserSettings.demoUser=Δοκιμαστικός χρήστης (Χωρίς προσαρμοσμένες ρυθμίσεις)
|
||||
adminUserSettings.internalApiUser=Εσωτερικός χρήστης API
|
||||
adminUserSettings.forceChange=Υποχρεωτική αλλαγή κωδικού κατά τη σύνδεση
|
||||
|
||||
@@ -446,6 +446,9 @@ account.adminNotif=You have admin privileges. Access system settings and user ma
|
||||
|
||||
adminUserSettings.title=User Control Settings
|
||||
adminUserSettings.header=Admin User Control Settings
|
||||
adminUserSettings.systemAdmin=System Administrator
|
||||
adminUserSettings.orgAdmin=Organisation Administrator
|
||||
adminUserSettings.teamLead=Team Leader
|
||||
adminUserSettings.admin=Admin
|
||||
adminUserSettings.user=User
|
||||
adminUserSettings.addUser=Add New User
|
||||
@@ -455,9 +458,6 @@ adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
|
||||
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
|
||||
adminUserSettings.role=Role
|
||||
adminUserSettings.actions=Actions
|
||||
adminUserSettings.apiUser=Limited API User
|
||||
adminUserSettings.extraApiUser=Additional Limited API User
|
||||
adminUserSettings.webOnlyUser=Web Only User
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Force user to change password on login
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
|
||||
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
|
||||
adminUserSettings.role=Role
|
||||
adminUserSettings.actions=Actions
|
||||
adminUserSettings.apiUser=Limited API User
|
||||
adminUserSettings.extraApiUser=Additional Limited API User
|
||||
adminUserSettings.webOnlyUser=Web Only User
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Force user to change password on login
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=¿Se debe habilitar/deshabilitar el us
|
||||
adminUserSettings.usernameInfo=El nombre de usuario solo puede contener letras, números y los siguientes caracteres especiales @._+- o debe ser una dirección de correo electrónico válida.
|
||||
adminUserSettings.role=Rol
|
||||
adminUserSettings.actions=Acciones
|
||||
adminUserSettings.apiUser=Usuario limitado de API
|
||||
adminUserSettings.extraApiUser=Otro usuario limitado de API
|
||||
adminUserSettings.webOnlyUser=Usuario solo web
|
||||
adminUserSettings.demoUser=Usuario Demo (Sin ajustes personalizados)
|
||||
adminUserSettings.internalApiUser=Usuario interno de API
|
||||
adminUserSettings.forceChange=Forzar usuario a cambiar usuario/contraseña en el acceso
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
|
||||
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
|
||||
adminUserSettings.role=Rol
|
||||
adminUserSettings.actions=Ekintzak
|
||||
adminUserSettings.apiUser=APIren erabiltzaile mugatua
|
||||
adminUserSettings.extraApiUser=Additional Limited API User
|
||||
adminUserSettings.webOnlyUser=Web-erabiltzailea bakarrik
|
||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||
adminUserSettings.internalApiUser=Internal API User
|
||||
adminUserSettings.forceChange=Force user to change password on login
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=آیا باید وضعیت کاربر
|
||||
adminUserSettings.usernameInfo=نام کاربری فقط میتواند شامل حروف، اعداد و کاراکترهای خاص @._+- باشد یا باید یک آدرس ایمیل معتبر باشد.
|
||||
adminUserSettings.role=نقش
|
||||
adminUserSettings.actions=اقدامات
|
||||
adminUserSettings.apiUser=کاربر محدود API
|
||||
adminUserSettings.extraApiUser=کاربر محدود اضافی API
|
||||
adminUserSettings.webOnlyUser=فقط کاربر وب
|
||||
adminUserSettings.demoUser=کاربر دمو (بدون تنظیمات سفارشی)
|
||||
adminUserSettings.internalApiUser=کاربر داخلی API
|
||||
adminUserSettings.forceChange=مجبور کردن کاربر به تغییر رمز عبور هنگام ورود
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Voulez vous vraiment déactiver/réact
|
||||
adminUserSettings.usernameInfo=Le nom d'utilisateur ne peut contenir que des lettres, des chiffres et les caractères spéciaux suivants @._+- ou doit être une adresse e-mail valide.
|
||||
adminUserSettings.role=Rôle
|
||||
adminUserSettings.actions=Actions
|
||||
adminUserSettings.apiUser=Utilisateur API limité
|
||||
adminUserSettings.extraApiUser=Utilisateur limité supplémentaire de l'API
|
||||
adminUserSettings.webOnlyUser=Utilisateur Web uniquement
|
||||
adminUserSettings.demoUser=Demo User (Paramètres par défaut)
|
||||
adminUserSettings.internalApiUser=Utilisateur de l'API interne
|
||||
adminUserSettings.forceChange=Forcer l'utilisateur à changer son nom d'utilisateur/mot de passe lors de la connexion
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Ar cheart an t-úsáideoir a dhíchuma
|
||||
adminUserSettings.usernameInfo=Ní féidir ach litreacha, uimhreacha agus na carachtair speisialta seo a leanas @._+- a bheith san ainm úsáideora nó ní mór gur seoladh ríomhphoist bailí é.
|
||||
adminUserSettings.role=Ról
|
||||
adminUserSettings.actions=Gníomhartha
|
||||
adminUserSettings.apiUser=Úsáideoir API Teoranta
|
||||
adminUserSettings.extraApiUser=Úsáideoir API Teoranta breise
|
||||
adminUserSettings.webOnlyUser=Úsáideoir Gréasáin Amháin
|
||||
adminUserSettings.demoUser=Úsáideoir Taispeána (Gan socruithe saincheaptha)
|
||||
adminUserSettings.internalApiUser=Úsáideoir API Inmheánach
|
||||
adminUserSettings.forceChange=Cuir iallach ar an úsáideoir pasfhocal a athrú ar logáil isteach
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=क्या उपयोगकर्
|
||||
adminUserSettings.usernameInfo=उपयोगकर्ता नाम में केवल अक्षर, संख्याएं और निम्नलिखित विशेष वर्ण @._+- हो सकते हैं या एक वैध ईमेल पता होना चाहिए।
|
||||
adminUserSettings.role=भूमिका
|
||||
adminUserSettings.actions=कार्रवाइयां
|
||||
adminUserSettings.apiUser=सीमित API उपयोगकर्ता
|
||||
adminUserSettings.extraApiUser=अतिरिक्त सीमित API उपयोगकर्ता
|
||||
adminUserSettings.webOnlyUser=केवल वेब उपयोगकर्ता
|
||||
adminUserSettings.demoUser=डेमो उपयोगकर्ता (कोई कस्टम सेटिंग्स नहीं)
|
||||
adminUserSettings.internalApiUser=आंतरिक API उपयोगकर्ता
|
||||
adminUserSettings.forceChange=लॉगिन पर उपयोगकर्ता को पासवर्ड बदलने के लिए मजबूर करें
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Treba li isključiti/uključiti ovog k
|
||||
adminUserSettings.usernameInfo=Korisničko ime može sadržavati samo slova, brojke i sljedeće posebne znakove @._+- ili mora biti važeća adresa e-pošte.
|
||||
adminUserSettings.role=Uloga
|
||||
adminUserSettings.actions=Akcije
|
||||
adminUserSettings.apiUser=Korisnik s ograničenim API pristupom
|
||||
adminUserSettings.extraApiUser=Dodatni korisnik s ograničenim API pristupom
|
||||
adminUserSettings.webOnlyUser=Web Korisnik
|
||||
adminUserSettings.demoUser=Demo korisnik (Bez prilagođenih Postavki)
|
||||
adminUserSettings.internalApiUser=Interni API Korisnik
|
||||
adminUserSettings.forceChange=Prisiliti korisnika da promijeni lozinku prilikom prijave
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Biztosan módosítja a felhasználó
|
||||
adminUserSettings.usernameInfo=A felhasználónév csak betűket, számokat és a következő speciális karaktereket tartalmazhatja: @._+- vagy érvényes e-mail címnek kell lennie.
|
||||
adminUserSettings.role=Szerepkör
|
||||
adminUserSettings.actions=Műveletek
|
||||
adminUserSettings.apiUser=Korlátozott API felhasználó
|
||||
adminUserSettings.extraApiUser=További korlátozott API felhasználó
|
||||
adminUserSettings.webOnlyUser=Csak webes felhasználó
|
||||
adminUserSettings.demoUser=Demo felhasználó (egyedi beállítások nélkül)
|
||||
adminUserSettings.internalApiUser=Belső API felhasználó
|
||||
adminUserSettings.forceChange=Jelszóváltoztatás kikényszerítése bejelentkezéskor
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Haruskah pengguna dinonaktifkan/diakti
|
||||
adminUserSettings.usernameInfo=Nama pengguna hanya boleh mengandung huruf, angka, dan karakter khusus berikut @._+- atau harus berupa alamat email yang valid.
|
||||
adminUserSettings.role=Peran
|
||||
adminUserSettings.actions=Tindakan
|
||||
adminUserSettings.apiUser=Pengguna API Terbatas
|
||||
adminUserSettings.extraApiUser=Pengguna API Terbatas Tambahan
|
||||
adminUserSettings.webOnlyUser=Pengguna Khusus Web
|
||||
adminUserSettings.demoUser=Pengguna Demo (Tanpa pengaturan kustom)
|
||||
adminUserSettings.internalApiUser=Pengguna API Internal
|
||||
adminUserSettings.forceChange=Memaksa pengguna untuk mengubah nama pengguna/kata sandi saat masuk
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=L'utente dovrebbe essere disabilitato/
|
||||
adminUserSettings.usernameInfo=Il nome utente può contenere solo lettere, numeri e i seguenti caratteri speciali @._+- oppure deve essere un indirizzo email valido.
|
||||
adminUserSettings.role=Ruolo
|
||||
adminUserSettings.actions=Azioni
|
||||
adminUserSettings.apiUser=Utente API limitato
|
||||
adminUserSettings.extraApiUser=API utente limitato aggiuntivo
|
||||
adminUserSettings.webOnlyUser=Utente solo Web
|
||||
adminUserSettings.demoUser=Utente demo (nessuna impostazione personalizzata)
|
||||
adminUserSettings.internalApiUser=API utente interna
|
||||
adminUserSettings.forceChange=Forza l'utente a cambiare nome utente/password all'accesso
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=ユーザーを無効/有効にする
|
||||
adminUserSettings.usernameInfo=ユーザー名には、文字、数字、および次の特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。
|
||||
adminUserSettings.role=ロール
|
||||
adminUserSettings.actions=アクション
|
||||
adminUserSettings.apiUser=限定されたAPIユーザー
|
||||
adminUserSettings.extraApiUser=追加の制限付きAPIユーザー
|
||||
adminUserSettings.webOnlyUser=ウェブ専用ユーザー
|
||||
adminUserSettings.demoUser=デモユーザー (カスタム設定なし)
|
||||
adminUserSettings.internalApiUser=内部APIユーザー
|
||||
adminUserSettings.forceChange=ログイン時にユーザー名/パスワードを強制的に変更する
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=사용자를 비활성화/활성화하
|
||||
adminUserSettings.usernameInfo=사용자 이름은 문자, 숫자 및 @._+- 특수문자만 포함하거나 유효한 이메일 주소여야 합니다.
|
||||
adminUserSettings.role=역할
|
||||
adminUserSettings.actions=작업
|
||||
adminUserSettings.apiUser=제한된 API 사용자
|
||||
adminUserSettings.extraApiUser=추가 제한된 API 사용자
|
||||
adminUserSettings.webOnlyUser=웹 전용 사용자
|
||||
adminUserSettings.demoUser=데모 사용자 (사용자 지정 설정 없음)
|
||||
adminUserSettings.internalApiUser=내부 API 사용자
|
||||
adminUserSettings.forceChange=로그인 시 사용자 비밀번호 변경 강제
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=ഉപയോക്താവിനെ
|
||||
adminUserSettings.usernameInfo=ഉപയോക്തൃനാമത്തിൽ അക്ഷരങ്ങൾ, അക്കങ്ങൾ, താഴെ പറയുന്ന പ്രത്യേക പ്രതീകങ്ങൾ @._+- എന്നിവ മാത്രമേ ഉണ്ടാകാവൂ അല്ലെങ്കിൽ സാധുവായ ഒരു ഇമെയിൽ വിലാസം ആയിരിക്കണം.
|
||||
adminUserSettings.role=റോൾ
|
||||
adminUserSettings.actions=പ്രവർത്തനങ്ങൾ
|
||||
adminUserSettings.apiUser=പരിമിതമായ API ഉപയോക്താവ്
|
||||
adminUserSettings.extraApiUser=അധിക പരിമിതമായ API ഉപയോക്താവ്
|
||||
adminUserSettings.webOnlyUser=വെബ് മാത്രം ഉപയോക്താവ്
|
||||
adminUserSettings.demoUser=ഡെമോ ഉപയോക്താവ് (ഇഷ്ടാനുസൃത ക്രമീകരണങ്ങളില്ല)
|
||||
adminUserSettings.internalApiUser=ആന്തരിക API ഉപയോക്താവ്
|
||||
adminUserSettings.forceChange=ലോഗിൻ ചെയ്യുമ്പോൾ പാസ്വേഡ് മാറ്റാൻ ഉപയോക്താവിനെ നിർബന്ധിക്കുക
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
|
||||
adminUserSettings.usernameInfo=Gebruikersnaam kan alleen letters, nummers en de volgende speciale tekens @._+- bevatten of moet een geldig emailadres zijn.
|
||||
adminUserSettings.role=Rol
|
||||
adminUserSettings.actions=Acties
|
||||
adminUserSettings.apiUser=Beperkte API gebruiker
|
||||
adminUserSettings.extraApiUser=Extra beperkte API gebruiker
|
||||
adminUserSettings.webOnlyUser=Alleen web gebruiker
|
||||
adminUserSettings.demoUser=Demogebruiker (geen aangepaste instellingen)
|
||||
adminUserSettings.internalApiUser=Interne API gebruiker
|
||||
adminUserSettings.forceChange=Forceer gebruiker om gebruikersnaam/wachtwoord te wijzigen bij inloggen
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Skal brukeren deaktiveres/aktiveres?
|
||||
adminUserSettings.usernameInfo=Brukernavn kan bare inneholde bokstaver, tall og følgende spesialtegn @._+- eller må være en gyldig e-postadresse.
|
||||
adminUserSettings.role=Rolle
|
||||
adminUserSettings.actions=Handlinger
|
||||
adminUserSettings.apiUser=Begrenset API Bruker
|
||||
adminUserSettings.extraApiUser=Ekstra Begrenset API Bruker
|
||||
adminUserSettings.webOnlyUser=Kun Web Bruker
|
||||
adminUserSettings.demoUser=Demo Bruker (Ingen tilpassede innstillinger)
|
||||
adminUserSettings.internalApiUser=Intern API Bruker
|
||||
adminUserSettings.forceChange=Tving bruker til å endre passord ved innlogging
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Czy użytkownik powinien zostać wył
|
||||
adminUserSettings.usernameInfo=Niewłaściwa nazwa użytkownika - musi zawierać litery, cyfry i @._+- LUB być adresem email.
|
||||
adminUserSettings.role=Rola
|
||||
adminUserSettings.actions=Akcje
|
||||
adminUserSettings.apiUser=Ograniczony Użytkownik API
|
||||
adminUserSettings.extraApiUser=Dodatkowy ograniczony Użytkownik API
|
||||
adminUserSettings.webOnlyUser=Użytkownik tylko WEB
|
||||
adminUserSettings.demoUser=Użytkownik DEMO
|
||||
adminUserSettings.internalApiUser=Wewnętrzny użytkownik API
|
||||
adminUserSettings.forceChange=Wymuś zmianę hasło po zalogowaniu
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=O usuário deve ser desabilitado/habil
|
||||
adminUserSettings.usernameInfo=Nome de usuário só pode incluir letras, números e os seguintes caracteres especiais @._+- ou deve ser um e-mail válido.
|
||||
adminUserSettings.role=Função
|
||||
adminUserSettings.actions=Ações
|
||||
adminUserSettings.apiUser=Usuário de API limitado
|
||||
adminUserSettings.extraApiUser=Usuário de API limitado adicional
|
||||
adminUserSettings.webOnlyUser=Usuário web apenas
|
||||
adminUserSettings.demoUser=Usuário demo (Sem configurações personalizadas)
|
||||
adminUserSettings.internalApiUser=Usuário de API interno
|
||||
adminUserSettings.forceChange=Forçar usuário a trocar a senha ao iniciar sessão
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Deve o utilizador ser desativado/ativa
|
||||
adminUserSettings.usernameInfo=O nome de utilizador só pode conter letras, números e os seguintes caracteres especiais @._+- ou deve ser um endereço de email válido.
|
||||
adminUserSettings.role=Função
|
||||
adminUserSettings.actions=Ações
|
||||
adminUserSettings.apiUser=Utilizador API Limitado
|
||||
adminUserSettings.extraApiUser=Utilizador API Limitado Adicional
|
||||
adminUserSettings.webOnlyUser=Utilizador Apenas Web
|
||||
adminUserSettings.demoUser=Utilizador Demo (Sem Definições Personalizadas)
|
||||
adminUserSettings.internalApiUser=Utilizador API Interno
|
||||
adminUserSettings.forceChange=Forçar utilizador a alterar palavra-passe no login
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Ar trebui dezactivat/activat utilizato
|
||||
adminUserSettings.usernameInfo=Numele de utilizator poate conține doar litere, numere și următoarele caractere speciale @._+- sau trebuie să fie o adresă de email validă.
|
||||
adminUserSettings.role=Rol
|
||||
adminUserSettings.actions=Acțiuni
|
||||
adminUserSettings.apiUser=Utilizator API Limitat
|
||||
adminUserSettings.extraApiUser=Utilizator API Limitat Suplimentar
|
||||
adminUserSettings.webOnlyUser=Utilizator Doar Web
|
||||
adminUserSettings.demoUser=Utilizator Demo (Fără setări personalizate)
|
||||
adminUserSettings.internalApiUser=Utilizator API Intern
|
||||
adminUserSettings.forceChange=Forțează utilizatorul să schimbe parola la conectare
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Отключить/включить п
|
||||
adminUserSettings.usernameInfo=Имя пользователя может содержать только буквы, цифры и следующие специальные символы @._+- или должно быть действительным адресом электронной почты.
|
||||
adminUserSettings.role=Роль
|
||||
adminUserSettings.actions=Действия
|
||||
adminUserSettings.apiUser=Ограниченный пользователь API
|
||||
adminUserSettings.extraApiUser=Дополнительный ограниченный пользователь API
|
||||
adminUserSettings.webOnlyUser=Только веб-пользователь
|
||||
adminUserSettings.demoUser=Демо-пользователь (без настраиваемых параметров)
|
||||
adminUserSettings.internalApiUser=Внутренний пользователь API
|
||||
adminUserSettings.forceChange=Требовать смену пароля при входе
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
|
||||
adminUserSettings.usernameInfo=Používateľské meno musí obsahovať iba písmená a čísla, žiadne medzery alebo špeciálne znaky.
|
||||
adminUserSettings.role=Rola
|
||||
adminUserSettings.actions=Akcie
|
||||
adminUserSettings.apiUser=Obmedzený API používateľ
|
||||
adminUserSettings.extraApiUser=Ďalší obmedzený API používateľ
|
||||
adminUserSettings.webOnlyUser=Používateľ iba pre web
|
||||
adminUserSettings.demoUser=Demo používateľ (Bez vlastných nastavení)
|
||||
adminUserSettings.internalApiUser=Interný API používateľ
|
||||
adminUserSettings.forceChange=Donútiť používateľa zmeniť heslo pri prihlásení
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Ali naj bo uporabnik onemogočen/omogo
|
||||
adminUserSettings.usernameInfo=Uporabniško ime lahko vsebuje samo črke, številke in naslednje posebne znake @._+- ali mora biti veljaven e-poštni naslov.
|
||||
adminUserSettings.role=Vloga
|
||||
adminUserSettings.actions=Dejanja
|
||||
adminUserSettings.apiUser=Omejen uporabnik API-ja
|
||||
adminUserSettings.extraApiUser=Dodatni omejeni uporabnik API-ja
|
||||
adminUserSettings.webOnlyUser=Samo spletni uporabnik
|
||||
adminUserSettings.demoUser=Demo uporabnik (brez nastavitev po meri)
|
||||
adminUserSettings.internalApiUser=Notranji uporabnik API-ja
|
||||
adminUserSettings.forceChange=Prisili uporabnika, da spremeni geslo ob prijavi
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Da onemogućim/omogućim korisnika?
|
||||
adminUserSettings.usernameInfo=Korisničko ime može sadržati samo slova, brojeve i specijalne karaktere @._+- ili mora biti validna email adresa.
|
||||
adminUserSettings.role=Uloga
|
||||
adminUserSettings.actions=Akcije
|
||||
adminUserSettings.apiUser=Korisnik s ograničenim API pristupom
|
||||
adminUserSettings.extraApiUser=Dodatni ograničeni API korisnik
|
||||
adminUserSettings.webOnlyUser=Korisnik samo za web
|
||||
adminUserSettings.demoUser=Demo korisnik (Bez prilagođenih podešavanja)
|
||||
adminUserSettings.internalApiUser=Interni API korisnik
|
||||
adminUserSettings.forceChange=Prisili korisnika da promeni korisničko ime/lozinku pri prijavi
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Ska användaren inaktiveras/aktiveras?
|
||||
adminUserSettings.usernameInfo=Användarnamn kan endast innehålla bokstäver, siffror och följande specialtecken @._+- eller måste vara en giltig e-postadress.
|
||||
adminUserSettings.role=Roll
|
||||
adminUserSettings.actions=Åtgärder
|
||||
adminUserSettings.apiUser=Begränsad API-användare
|
||||
adminUserSettings.extraApiUser=Ytterligare begränsad API-användare
|
||||
adminUserSettings.webOnlyUser=Endast webbanvändare
|
||||
adminUserSettings.demoUser=Demoanvändare (Inga anpassade inställningar)
|
||||
adminUserSettings.internalApiUser=Intern API-användare
|
||||
adminUserSettings.forceChange=Tvinga användare att ändra lösenord vid inloggning
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=ผู้ใช้นี้ควร
|
||||
adminUserSettings.usernameInfo=ชื่อผู้ใช้สามารถประกอบด้วยตัวอักษร ตัวเลข และอักขระพิเศษต่อไปนี้ @._+- หรือจะต้องเป็นที่อยู่อีเมลที่ถูกต้อง
|
||||
adminUserSettings.role=บทบาท
|
||||
adminUserSettings.actions=การดำเนินการ
|
||||
adminUserSettings.apiUser=ผู้ใช้ API จำกัด
|
||||
adminUserSettings.extraApiUser=ผู้ใช้ API เพิ่มเติม
|
||||
adminUserSettings.webOnlyUser=ผู้ใช้เว็บเท่านั้น
|
||||
adminUserSettings.demoUser=ผู้ใช้ทดลอง (ไม่มีการตั้งค่าปรับแต่ง)
|
||||
adminUserSettings.internalApiUser=ผู้ใช้ API ภายใน
|
||||
adminUserSettings.forceChange=บังคับให้ผู้ใช้เปลี่ยนรหัสผ่านในการเข้าสู่ระบบ
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Kullanıcı devre dışı bırakılmal
|
||||
adminUserSettings.usernameInfo=Kullanıcı adı yalnızca harf, rakam ve aşağıdaki özel karakterleri @._+- içerebilir veya geçerli bir e-posta adresi olmalıdır.
|
||||
adminUserSettings.role=Rol
|
||||
adminUserSettings.actions=Eylemler
|
||||
adminUserSettings.apiUser=Sınırlı API Kullanıcısı
|
||||
adminUserSettings.extraApiUser=Ek Sınırlı API Kullanıcısı
|
||||
adminUserSettings.webOnlyUser=Sadece Web Kullanıcısı
|
||||
adminUserSettings.demoUser=Demo Kullanıcısı (Özel ayar yok)
|
||||
adminUserSettings.internalApiUser=Dahili API Kullanıcısı
|
||||
adminUserSettings.forceChange=Kullanıcının girişte kullanıcı adı/şifre değiştirmesini zorla
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Чи потрібно вимкнути
|
||||
adminUserSettings.usernameInfo=Ім’я користувача може містити лише літери, цифри та наступні спеціальні символи @._+- або має бути дійсною електронною адресою.
|
||||
adminUserSettings.role=Роль
|
||||
adminUserSettings.actions=Дії
|
||||
adminUserSettings.apiUser=Обмежений користувач API
|
||||
adminUserSettings.extraApiUser=Додатковий обмежений користувач API
|
||||
adminUserSettings.webOnlyUser=Тільки веб-користувач
|
||||
adminUserSettings.demoUser=Демо-користувач (без налаштованих параметрів)
|
||||
adminUserSettings.internalApiUser=Внутрішній користувач API
|
||||
adminUserSettings.forceChange=Примусити користувача змінити пароль при вході в систему
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
|
||||
adminUserSettings.usernameInfo=Tên người dùng chỉ có thể chứa chữ cái, số và các ký tự đặc biệt sau @._+- hoặc phải là một địa chỉ email hợp lệ.
|
||||
adminUserSettings.role=Vai trò
|
||||
adminUserSettings.actions=Hành động
|
||||
adminUserSettings.apiUser=Người dùng API giới hạn
|
||||
adminUserSettings.extraApiUser=Người dùng API giới hạn bổ sung
|
||||
adminUserSettings.webOnlyUser=Chỉ người dùng web
|
||||
adminUserSettings.demoUser=Người dùng demo (Không có cài đặt tùy chỉnh)
|
||||
adminUserSettings.internalApiUser=Người dùng API nội bộ
|
||||
adminUserSettings.forceChange=Buộc người dùng thay đổi mật khẩu khi đăng nhập
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=是否应禁用/启用该用户?
|
||||
adminUserSettings.usernameInfo=用户名只能包含字母、数字和以下特殊字符@._+-,或者必须是有效的电子邮件地址。
|
||||
adminUserSettings.role=角色
|
||||
adminUserSettings.actions=操作
|
||||
adminUserSettings.apiUser=受限制的 API 用户
|
||||
adminUserSettings.extraApiUser=额外受限制的 API 用户
|
||||
adminUserSettings.webOnlyUser=仅限 Web 用户
|
||||
adminUserSettings.demoUser=演示用户(无自定义设置)
|
||||
adminUserSettings.internalApiUser=内部 API 用户
|
||||
adminUserSettings.forceChange=强制用户在登录时更改用户名/密码
|
||||
|
||||
@@ -455,9 +455,6 @@ adminUserSettings.confirmChangeUserStatus=是否要停用/啟用此使用者?
|
||||
adminUserSettings.usernameInfo=使用者名稱只能包含字母、數字和以下特殊字元 @._+- 或必須是有效的電子郵件地址。
|
||||
adminUserSettings.role=角色
|
||||
adminUserSettings.actions=操作
|
||||
adminUserSettings.apiUser=受限制的 API 使用者
|
||||
adminUserSettings.extraApiUser=額外受限制的 API 使用者
|
||||
adminUserSettings.webOnlyUser=僅網頁版使用者
|
||||
adminUserSettings.demoUser=示範使用者(無自訂設定)
|
||||
adminUserSettings.internalApiUser=內部 API 使用者
|
||||
adminUserSettings.forceChange=強制使用者在登入時變更密碼
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Admin Settings Banner (for admins only) -->
|
||||
<div th:if="${role == 'ROLE_ADMIN'}" class="data-panel data-mb-3" style="background-color: var(--md-sys-color-secondary-container);">
|
||||
<div th:if="${isSystemAdmin}" class="data-panel data-mb-3" style="background-color: var(--md-sys-color-secondary-container);">
|
||||
<div class="data-body" style="display: flex; align-items: center; justify-content: space-between; padding: 1rem 1.5rem; background-color: var(--md-sys-color-secondary-container);">
|
||||
<div style="display: flex; align-items: center; gap: 1rem;">
|
||||
<span class="material-symbols-rounded" style="font-size: 2rem; color: var(--md-sys-color-secondary);">
|
||||
|
||||
+3
-3
@@ -33,7 +33,7 @@ public class AdminJobController {
|
||||
* @return Job statistics
|
||||
*/
|
||||
@GetMapping("/api/v1/admin/job/stats")
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageAllUsers()")
|
||||
public ResponseEntity<JobStats> getJobStats() {
|
||||
JobStats stats = taskManager.getJobStats();
|
||||
log.info(
|
||||
@@ -49,7 +49,7 @@ public class AdminJobController {
|
||||
* @return Queue statistics
|
||||
*/
|
||||
@GetMapping("/api/v1/admin/job/queue/stats")
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageAllUsers()")
|
||||
public ResponseEntity<?> getQueueStats() {
|
||||
Map<String, Object> queueStats = jobQueue.getQueueStats();
|
||||
log.info("Admin requested queue stats: {} queued jobs", queueStats.get("queuedJobs"));
|
||||
@@ -62,7 +62,7 @@ public class AdminJobController {
|
||||
* @return A response indicating how many jobs were cleaned up
|
||||
*/
|
||||
@PostMapping("/api/v1/admin/job/cleanup")
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageAllUsers()")
|
||||
public ResponseEntity<?> cleanupOldJobs() {
|
||||
int beforeCount = taskManager.getJobStats().getTotalJobs();
|
||||
taskManager.cleanupOldJobs();
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ import stirling.software.proprietary.security.config.EnterpriseEndpoint;
|
||||
@Slf4j
|
||||
@Controller
|
||||
@RequestMapping("/audit")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageAllUsers()")
|
||||
@RequiredArgsConstructor
|
||||
@EnterpriseEndpoint
|
||||
public class AuditDashboardController {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package stirling.software.proprietary.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "organizations")
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
@ToString(onlyExplicitlyIncluded = true)
|
||||
public class Organization implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "org_id")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "name", unique = true, nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
@OneToMany(mappedBy = "organization", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private Set<Team> teams = new HashSet<>();
|
||||
|
||||
public void addTeam(Team team) {
|
||||
teams.add(team);
|
||||
team.setOrganization(this);
|
||||
}
|
||||
|
||||
public void removeTeam(Team team) {
|
||||
teams.remove(team);
|
||||
team.setOrganization(null);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import lombok.*;
|
||||
import stirling.software.proprietary.security.model.User;
|
||||
|
||||
@Entity
|
||||
@Table(name = "teams")
|
||||
@Table(name = "teams", uniqueConstraints = @UniqueConstraint(columnNames = {"name", "org_id"}))
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@@ -26,9 +26,15 @@ public class Team implements Serializable {
|
||||
@Column(name = "team_id")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "name", unique = true, nullable = false)
|
||||
@Column(name = "name", nullable = false)
|
||||
private String name;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(
|
||||
name = "org_id",
|
||||
nullable = true) // Nullable for backward compatibility during migration
|
||||
private Organization organization;
|
||||
|
||||
@OneToMany(mappedBy = "team", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private Set<User> users = new HashSet<>();
|
||||
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package stirling.software.proprietary.model.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class OrganizationWithTeamCountDTO {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String description;
|
||||
private Long teamCount;
|
||||
}
|
||||
+138
-2
@@ -15,9 +15,12 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.common.model.enumeration.Role;
|
||||
import stirling.software.common.model.exception.UnsupportedProviderException;
|
||||
import stirling.software.proprietary.model.Organization;
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.security.model.User;
|
||||
import stirling.software.proprietary.security.repository.TeamRepository;
|
||||
import stirling.software.proprietary.security.service.DatabaseServiceInterface;
|
||||
import stirling.software.proprietary.security.service.OrganizationService;
|
||||
import stirling.software.proprietary.security.service.TeamService;
|
||||
import stirling.software.proprietary.security.service.UserService;
|
||||
|
||||
@@ -28,6 +31,8 @@ public class InitialSecuritySetup {
|
||||
|
||||
private final UserService userService;
|
||||
private final TeamService teamService;
|
||||
private final OrganizationService organizationService;
|
||||
private final TeamRepository teamRepository;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
private final DatabaseServiceInterface databaseService;
|
||||
|
||||
@@ -44,6 +49,9 @@ public class InitialSecuritySetup {
|
||||
}
|
||||
|
||||
userService.migrateOauth2ToSSO();
|
||||
migrateAdminRolesToSystemAdmin();
|
||||
migrateDeprecatedRolesToUser();
|
||||
assignTeamsToDefaultOrganizationIfMissing();
|
||||
assignUsersToDefaultTeamIfMissing();
|
||||
initializeInternalApiUser();
|
||||
} catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {
|
||||
@@ -52,6 +60,49 @@ public class InitialSecuritySetup {
|
||||
}
|
||||
}
|
||||
|
||||
private void assignTeamsToDefaultOrganizationIfMissing() {
|
||||
// Find teams without organizations (legacy teams from before org feature)
|
||||
List<Team> teamsWithoutOrg = teamRepository.findTeamsWithoutOrganization();
|
||||
|
||||
if (teamsWithoutOrg.isEmpty()) {
|
||||
log.debug("No teams without organizations found - migration not needed");
|
||||
return;
|
||||
}
|
||||
|
||||
log.info(
|
||||
"Found {} teams without organizations. Starting migration...",
|
||||
teamsWithoutOrg.size());
|
||||
|
||||
// Ensure default organizations exist
|
||||
Organization defaultOrg = organizationService.getOrCreateDefaultOrganization();
|
||||
Organization internalOrg = organizationService.getOrCreateInternalOrganization();
|
||||
|
||||
int migratedCount = 0;
|
||||
for (Team team : teamsWithoutOrg) {
|
||||
try {
|
||||
// Assign internal team to internal org, all others to default org
|
||||
if (TeamService.INTERNAL_TEAM_NAME.equals(team.getName())) {
|
||||
team.setOrganization(internalOrg);
|
||||
log.debug("Assigned team '{}' to internal organization", team.getName());
|
||||
} else {
|
||||
team.setOrganization(defaultOrg);
|
||||
log.debug("Assigned team '{}' to default organization", team.getName());
|
||||
}
|
||||
teamRepository.save(team);
|
||||
migratedCount++;
|
||||
} catch (Exception e) {
|
||||
log.error(
|
||||
"Failed to migrate team '{}' to organization: {}",
|
||||
team.getName(),
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (migratedCount > 0) {
|
||||
log.info("Successfully migrated {} teams to organizations", migratedCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void assignUsersToDefaultTeamIfMissing() {
|
||||
Team defaultTeam = teamService.getOrCreateDefaultTeam();
|
||||
Team internalTeam = teamService.getOrCreateInternalTeam();
|
||||
@@ -86,7 +137,7 @@ public class InitialSecuritySetup {
|
||||
|
||||
Team team = teamService.getOrCreateDefaultTeam();
|
||||
userService.saveUser(
|
||||
initialUsername, initialPassword, team, Role.ADMIN.getRoleId(), false);
|
||||
initialUsername, initialPassword, team, Role.SYSTEM_ADMIN.getRoleId(), false);
|
||||
log.info("Admin user created: {}", initialUsername);
|
||||
} else {
|
||||
createDefaultAdminUser();
|
||||
@@ -100,7 +151,7 @@ public class InitialSecuritySetup {
|
||||
if (userService.findByUsernameIgnoreCase(defaultUsername).isEmpty()) {
|
||||
Team team = teamService.getOrCreateDefaultTeam();
|
||||
userService.saveUser(
|
||||
defaultUsername, defaultPassword, team, Role.ADMIN.getRoleId(), true);
|
||||
defaultUsername, defaultPassword, team, Role.SYSTEM_ADMIN.getRoleId(), true);
|
||||
log.info("Default admin user created: {}", defaultUsername);
|
||||
}
|
||||
}
|
||||
@@ -134,4 +185,89 @@ public class InitialSecuritySetup {
|
||||
}
|
||||
userService.syncCustomApiUser(applicationProperties.getSecurity().getCustomGlobalAPIKey());
|
||||
}
|
||||
|
||||
private void migrateAdminRolesToSystemAdmin() {
|
||||
List<User> adminUsers = userService.findByRole(Role.ADMIN.getRoleId());
|
||||
|
||||
if (adminUsers.isEmpty()) {
|
||||
log.debug("No ROLE_ADMIN users found - migration not needed");
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("Found {} ROLE_ADMIN users. Converting to SYSTEM_ADMIN...", adminUsers.size());
|
||||
|
||||
int migratedCount = 0;
|
||||
for (User user : adminUsers) {
|
||||
try {
|
||||
user.setUserRole(Role.SYSTEM_ADMIN);
|
||||
userService.saveUser(user);
|
||||
log.debug(
|
||||
"Converted user '{}' from ROLE_ADMIN to SYSTEM_ADMIN", user.getUsername());
|
||||
migratedCount++;
|
||||
} catch (Exception e) {
|
||||
log.error(
|
||||
"Failed to migrate user '{}' from ROLE_ADMIN to SYSTEM_ADMIN: {}",
|
||||
user.getUsername(),
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (migratedCount > 0) {
|
||||
log.info(
|
||||
"Successfully migrated {} users from ROLE_ADMIN to SYSTEM_ADMIN",
|
||||
migratedCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void migrateDeprecatedRolesToUser() {
|
||||
String[] deprecatedRoles = {
|
||||
"ROLE_WEB_ONLY_USER", "ROLE_EXTRA_LIMITED_API_USER", "ROLE_LIMITED_API_USER"
|
||||
};
|
||||
|
||||
int totalMigrated = 0;
|
||||
|
||||
for (String deprecatedRole : deprecatedRoles) {
|
||||
List<User> usersWithDeprecatedRole = userService.findByRole(deprecatedRole);
|
||||
|
||||
if (!usersWithDeprecatedRole.isEmpty()) {
|
||||
log.info(
|
||||
"Found {} users with role {}. Converting to USER...",
|
||||
usersWithDeprecatedRole.size(),
|
||||
deprecatedRole);
|
||||
|
||||
int migratedCount = 0;
|
||||
for (User user : usersWithDeprecatedRole) {
|
||||
try {
|
||||
user.setUserRole(Role.USER);
|
||||
userService.saveUser(user);
|
||||
log.debug(
|
||||
"Converted user '{}' from {} to USER",
|
||||
user.getUsername(),
|
||||
deprecatedRole);
|
||||
migratedCount++;
|
||||
} catch (Exception e) {
|
||||
log.error(
|
||||
"Failed to migrate user '{}' from {} to USER: {}",
|
||||
user.getUsername(),
|
||||
deprecatedRole,
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (migratedCount > 0) {
|
||||
log.info(
|
||||
"Successfully migrated {} users from {} to USER",
|
||||
migratedCount,
|
||||
deprecatedRole);
|
||||
totalMigrated += migratedCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (totalMigrated == 0) {
|
||||
log.debug("No users with deprecated roles found - migration not needed");
|
||||
} else {
|
||||
log.info("Total users migrated from deprecated roles to USER: {}", totalMigrated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
-2
@@ -7,6 +7,7 @@ import java.time.temporal.ChronoUnit;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -203,7 +204,7 @@ public class AccountWebController {
|
||||
return "login";
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageAllUsers()")
|
||||
@GetMapping("/usage")
|
||||
public String showUsage() {
|
||||
if (!runningEE) {
|
||||
@@ -212,13 +213,26 @@ public class AccountWebController {
|
||||
return "usage";
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageAllUsers()")
|
||||
@GetMapping("/adminSettings")
|
||||
public String showAddUserForm(
|
||||
HttpServletRequest request, Model model, Authentication authentication) {
|
||||
List<User> allUsers = userRepository.findAllWithTeam();
|
||||
Iterator<User> iterator = allUsers.iterator();
|
||||
Map<String, String> roleDetails = Role.getAllRoleDetails();
|
||||
|
||||
// Filter role details to only show SYSTEM_ADMIN, USER, and DEMO_USER in UI
|
||||
Map<String, String> filteredRoleDetails = new LinkedHashMap<>();
|
||||
String[] allowedRoles = {
|
||||
Role.SYSTEM_ADMIN.getRoleId(), Role.USER.getRoleId(), Role.DEMO_USER.getRoleId()
|
||||
};
|
||||
|
||||
for (String roleId : allowedRoles) {
|
||||
if (roleDetails.containsKey(roleId)) {
|
||||
filteredRoleDetails.put(roleId, roleDetails.get(roleId));
|
||||
}
|
||||
}
|
||||
roleDetails = filteredRoleDetails;
|
||||
// Map to store session information and user activity status
|
||||
Map<String, Boolean> userSessions = new HashMap<>();
|
||||
Map<String, Date> userLastRequest = new HashMap<>();
|
||||
@@ -426,6 +440,7 @@ public class AccountWebController {
|
||||
model.addAttribute("username", username);
|
||||
model.addAttribute("messageType", messageType);
|
||||
model.addAttribute("role", user.get().getRolesAsString());
|
||||
model.addAttribute("isSystemAdmin", user.get().isSystemAdmin());
|
||||
model.addAttribute("settings", settingsJson);
|
||||
model.addAttribute("changeCredsFlag", user.get().isFirstLogin());
|
||||
model.addAttribute("currentPage", "account");
|
||||
|
||||
+53
-26
@@ -8,6 +8,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
@@ -74,6 +75,7 @@ public class SecurityConfiguration {
|
||||
private final GrantedAuthoritiesMapper oAuth2userAuthoritiesMapper;
|
||||
private final RelyingPartyRegistrationRepository saml2RelyingPartyRegistrations;
|
||||
private final OpenSaml4AuthenticationRequestResolver saml2AuthenticationRequestResolver;
|
||||
private final Environment environment;
|
||||
|
||||
public SecurityConfiguration(
|
||||
PersistentLoginRepository persistentLoginRepository,
|
||||
@@ -91,7 +93,8 @@ public class SecurityConfiguration {
|
||||
@Autowired(required = false)
|
||||
RelyingPartyRegistrationRepository saml2RelyingPartyRegistrations,
|
||||
@Autowired(required = false)
|
||||
OpenSaml4AuthenticationRequestResolver saml2AuthenticationRequestResolver) {
|
||||
OpenSaml4AuthenticationRequestResolver saml2AuthenticationRequestResolver,
|
||||
Environment environment) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
this.userService = userService;
|
||||
this.loginEnabledValue = loginEnabledValue;
|
||||
@@ -106,6 +109,7 @@ public class SecurityConfiguration {
|
||||
this.oAuth2userAuthoritiesMapper = oAuth2userAuthoritiesMapper;
|
||||
this.saml2RelyingPartyRegistrations = saml2RelyingPartyRegistrations;
|
||||
this.saml2AuthenticationRequestResolver = saml2AuthenticationRequestResolver;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -129,33 +133,49 @@ public class SecurityConfiguration {
|
||||
new CsrfTokenRequestAttributeHandler();
|
||||
requestHandler.setCsrfRequestAttributeName(null);
|
||||
http.csrf(
|
||||
csrf ->
|
||||
csrf.ignoringRequestMatchers(
|
||||
request -> {
|
||||
String apiKey = request.getHeader("X-API-KEY");
|
||||
// If there's no API key, don't ignore CSRF
|
||||
csrf -> {
|
||||
var csrfConfig =
|
||||
csrf.ignoringRequestMatchers(
|
||||
request -> {
|
||||
String apiKey = request.getHeader("X-API-KEY");
|
||||
// If there's no API key, don't ignore CSRF
|
||||
// (return false)
|
||||
if (apiKey == null || apiKey.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// Validate API key using existing UserService
|
||||
try {
|
||||
Optional<User> user =
|
||||
userService.getUserByApiKey(apiKey);
|
||||
// If API key is valid, ignore CSRF (return
|
||||
// true)
|
||||
// If API key is invalid, don't ignore CSRF
|
||||
// (return false)
|
||||
if (apiKey == null || apiKey.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// Validate API key using existing UserService
|
||||
try {
|
||||
Optional<User> user =
|
||||
userService.getUserByApiKey(apiKey);
|
||||
// If API key is valid, ignore CSRF (return
|
||||
// true)
|
||||
// If API key is invalid, don't ignore CSRF
|
||||
// (return false)
|
||||
return user.isPresent();
|
||||
} catch (Exception e) {
|
||||
// If there's any error validating the API
|
||||
// key, don't ignore CSRF
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.csrfTokenRepository(cookieRepo)
|
||||
.csrfTokenRequestHandler(requestHandler));
|
||||
return user.isPresent();
|
||||
} catch (Exception e) {
|
||||
// If there's any error validating the API
|
||||
// key, don't ignore CSRF
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Only ignore CSRF for H2 console if H2 console is enabled
|
||||
if (isH2ConsoleEnabled()) {
|
||||
csrfConfig = csrfConfig.ignoringRequestMatchers("/h2-console/**");
|
||||
}
|
||||
|
||||
csrfConfig
|
||||
.csrfTokenRepository(cookieRepo)
|
||||
.csrfTokenRequestHandler(requestHandler);
|
||||
});
|
||||
}
|
||||
|
||||
// Allow H2 console frames only if H2 console is enabled
|
||||
if (isH2ConsoleEnabled()) {
|
||||
http.headers(
|
||||
headers -> headers.frameOptions(frameOptions -> frameOptions.sameOrigin()));
|
||||
}
|
||||
|
||||
http.addFilterBefore(rateLimitingFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||
http.addFilterAfter(firstLoginFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
http.sessionManagement(
|
||||
@@ -215,6 +235,9 @@ public class SecurityConfiguration {
|
||||
|| trimmedUri.startsWith("/images/")
|
||||
|| trimmedUri.startsWith("/public/")
|
||||
|| trimmedUri.startsWith("/css/")
|
||||
|| (isH2ConsoleEnabled()
|
||||
&& trimmedUri.startsWith(
|
||||
"/h2-console/"))
|
||||
|| trimmedUri.startsWith("/fonts/")
|
||||
|| trimmedUri.startsWith("/js/")
|
||||
|| trimmedUri.startsWith(
|
||||
@@ -323,4 +346,8 @@ public class SecurityConfiguration {
|
||||
public PersistentTokenRepository persistentTokenRepository() {
|
||||
return new JPATokenRepositoryImpl(persistentLoginRepository);
|
||||
}
|
||||
|
||||
private boolean isH2ConsoleEnabled() {
|
||||
return environment.getProperty("spring.h2.console.enabled", Boolean.class, false);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ import stirling.software.proprietary.security.model.api.admin.UpdateSettingsRequ
|
||||
@Tag(name = "Admin Settings", description = "Admin-only Settings Management APIs")
|
||||
@RequestMapping("/api/v1/admin/settings")
|
||||
@RequiredArgsConstructor
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageAllUsers()")
|
||||
@Slf4j
|
||||
public class AdminSettingsController {
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ import stirling.software.proprietary.security.service.DatabaseService;
|
||||
@Slf4j
|
||||
@Controller
|
||||
@RequestMapping("/api/v1/database")
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageAllUsers()")
|
||||
@Conditional(H2SQLCondition.class)
|
||||
@Tag(name = "Database", description = "Database APIs for backup, import, and management")
|
||||
@RequiredArgsConstructor
|
||||
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
package stirling.software.proprietary.security.controller.api;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.model.enumeration.Role;
|
||||
import stirling.software.proprietary.model.Organization;
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.security.config.PremiumEndpoint;
|
||||
import stirling.software.proprietary.security.database.repository.UserRepository;
|
||||
import stirling.software.proprietary.security.model.User;
|
||||
import stirling.software.proprietary.security.repository.TeamRepository;
|
||||
import stirling.software.proprietary.security.service.RoleBasedAuthorizationService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/org-admin")
|
||||
@Tag(name = "Organization Admin", description = "Organization Admin Management APIs")
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@PremiumEndpoint
|
||||
@PreAuthorize(
|
||||
"@roleBasedAuthorizationService.canManageOrgUsers() or @roleBasedAuthorizationService.canManageOrgTeams()")
|
||||
public class OrgAdminController {
|
||||
|
||||
private final TeamRepository teamRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final RoleBasedAuthorizationService authorizationService;
|
||||
|
||||
/** Get all teams in the org admin's organization */
|
||||
@GetMapping("/teams")
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageOrgTeams()")
|
||||
public ResponseEntity<List<Team>> getOrganizationTeams() {
|
||||
User currentUser = authorizationService.getCurrentUser();
|
||||
if (currentUser == null || currentUser.getOrganization() == null) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
List<Team> teams =
|
||||
teamRepository.findByOrganizationId(currentUser.getOrganization().getId());
|
||||
return ResponseEntity.ok(teams);
|
||||
}
|
||||
|
||||
/** Get all users in the org admin's organization */
|
||||
@GetMapping("/users")
|
||||
public ResponseEntity<List<User>> getOrganizationUsers() {
|
||||
if (!authorizationService.canManageOrgUsers()) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
|
||||
}
|
||||
|
||||
User currentUser = authorizationService.getCurrentUser();
|
||||
if (currentUser == null || currentUser.getOrganization() == null) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
// Get all users in teams belonging to this organization
|
||||
List<Team> orgTeams =
|
||||
teamRepository.findByOrganizationId(currentUser.getOrganization().getId());
|
||||
List<User> orgUsers =
|
||||
orgTeams.stream().flatMap(team -> team.getUsers().stream()).distinct().toList();
|
||||
|
||||
return ResponseEntity.ok(orgUsers);
|
||||
}
|
||||
|
||||
/** Assign a user to a team within the organization */
|
||||
@PostMapping("/assign-user-to-team")
|
||||
@Transactional
|
||||
public ResponseEntity<?> assignUserToTeam(
|
||||
@RequestParam("userId") Long userId, @RequestParam("teamId") Long teamId) {
|
||||
|
||||
if (!authorizationService.canManageOrgUsers()) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body("Not authorized to manage organization users");
|
||||
}
|
||||
|
||||
Optional<User> userOpt = userRepository.findById(userId);
|
||||
Optional<Team> teamOpt = teamRepository.findById(teamId);
|
||||
|
||||
if (userOpt.isEmpty() || teamOpt.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
User user = userOpt.get();
|
||||
Team team = teamOpt.get();
|
||||
|
||||
if (!authorizationService.canAddUserToTeam(userId, team)) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body("Not authorized to add user to this team");
|
||||
}
|
||||
|
||||
// Assign user to team
|
||||
user.setTeam(team);
|
||||
userRepository.save(user);
|
||||
|
||||
return ResponseEntity.ok().body("User assigned to team successfully");
|
||||
}
|
||||
|
||||
/** Promote a user to team lead */
|
||||
@PostMapping("/promote-to-team-lead")
|
||||
@Transactional
|
||||
public ResponseEntity<?> promoteToTeamLead(@RequestParam("userId") Long userId) {
|
||||
if (!authorizationService.canManageUser(userId)) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body("Not authorized to manage this user");
|
||||
}
|
||||
|
||||
if (!authorizationService.canAssignRole(Role.TEAM_LEAD)) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body("Not authorized to assign team lead role");
|
||||
}
|
||||
|
||||
Optional<User> userOpt = userRepository.findById(userId);
|
||||
if (userOpt.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
User user = userOpt.get();
|
||||
|
||||
// User must be in a team to become a team lead
|
||||
if (user.getTeam() == null) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body("User must be assigned to a team before becoming a team lead");
|
||||
}
|
||||
|
||||
user.setUserRole(Role.TEAM_LEAD);
|
||||
userRepository.save(user);
|
||||
|
||||
return ResponseEntity.ok().body("User promoted to team lead successfully");
|
||||
}
|
||||
|
||||
/** Demote a team lead to regular user */
|
||||
@PostMapping("/demote-from-team-lead")
|
||||
@Transactional
|
||||
public ResponseEntity<?> demoteFromTeamLead(@RequestParam("userId") Long userId) {
|
||||
if (!authorizationService.canManageUser(userId)) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body("Not authorized to manage this user");
|
||||
}
|
||||
|
||||
Optional<User> userOpt = userRepository.findById(userId);
|
||||
if (userOpt.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
User user = userOpt.get();
|
||||
user.setUserRole(Role.USER);
|
||||
userRepository.save(user);
|
||||
|
||||
return ResponseEntity.ok().body("User demoted from team lead successfully");
|
||||
}
|
||||
|
||||
/** Create a new team in the organization */
|
||||
@PostMapping("/create-team")
|
||||
@Transactional
|
||||
public ResponseEntity<?> createTeam(@RequestParam("teamName") String teamName) {
|
||||
if (!authorizationService.canManageOrgTeams()) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body("Not authorized to create teams");
|
||||
}
|
||||
|
||||
User currentUser = authorizationService.getCurrentUser();
|
||||
if (currentUser == null || currentUser.getOrganization() == null) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body("Org admin must be assigned to an organization");
|
||||
}
|
||||
|
||||
Organization organization = currentUser.getOrganization();
|
||||
|
||||
// Check if team name already exists in the organization
|
||||
if (teamRepository.existsByNameIgnoreCaseAndOrganizationId(
|
||||
teamName, organization.getId())) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body("Team with name '" + teamName + "' already exists in this organization");
|
||||
}
|
||||
|
||||
Team newTeam = new Team();
|
||||
newTeam.setName(teamName);
|
||||
newTeam.setOrganization(organization);
|
||||
|
||||
Team savedTeam = teamRepository.save(newTeam);
|
||||
return ResponseEntity.ok(savedTeam);
|
||||
}
|
||||
|
||||
/** Remove a user from the organization (removes from their team) */
|
||||
@PostMapping("/remove-user")
|
||||
@Transactional
|
||||
public ResponseEntity<?> removeUserFromOrganization(@RequestParam("userId") Long userId) {
|
||||
if (!authorizationService.canRemoveUserFromTeam(userId)) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body("Not authorized to remove this user");
|
||||
}
|
||||
|
||||
Optional<User> userOpt = userRepository.findById(userId);
|
||||
if (userOpt.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
User user = userOpt.get();
|
||||
user.setTeam(null);
|
||||
user.setUserRole(Role.USER); // Reset to basic user role
|
||||
userRepository.save(user);
|
||||
|
||||
return ResponseEntity.ok().body("User removed from organization successfully");
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package stirling.software.proprietary.security.controller.api;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.proprietary.model.Organization;
|
||||
import stirling.software.proprietary.model.dto.OrganizationWithTeamCountDTO;
|
||||
import stirling.software.proprietary.security.repository.OrganizationRepository;
|
||||
import stirling.software.proprietary.security.service.OrganizationService;
|
||||
import stirling.software.proprietary.security.service.RoleBasedAuthorizationService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/organizations")
|
||||
@RequiredArgsConstructor
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageOrganizations()")
|
||||
public class OrganizationController {
|
||||
|
||||
private final OrganizationRepository organizationRepository;
|
||||
private final OrganizationService organizationService;
|
||||
private final RoleBasedAuthorizationService authorizationService;
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<List<OrganizationWithTeamCountDTO>> getAllOrganizations() {
|
||||
List<OrganizationWithTeamCountDTO> organizations =
|
||||
organizationRepository.findAllOrganizationsWithTeamCount();
|
||||
return ResponseEntity.ok(organizations);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@PreAuthorize(
|
||||
"@roleBasedAuthorizationService.canViewOrganization(@organizationRepository.findById(#id).orElse(null))")
|
||||
public ResponseEntity<Organization> getOrganization(@PathVariable Long id) {
|
||||
Optional<Organization> organizationOpt = organizationRepository.findById(id);
|
||||
if (organizationOpt.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
Organization organization = organizationOpt.get();
|
||||
return ResponseEntity.ok(organization);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<?> createOrganization(@RequestBody Organization organization) {
|
||||
if (organizationRepository.existsByNameIgnoreCase(organization.getName())) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body("Organization with name '" + organization.getName() + "' already exists");
|
||||
}
|
||||
Organization savedOrganization = organizationRepository.save(organization);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(savedOrganization);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<?> updateOrganization(
|
||||
@PathVariable Long id, @RequestBody Organization organization) {
|
||||
Optional<Organization> existingOrganization = organizationRepository.findById(id);
|
||||
if (existingOrganization.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
if (organizationRepository.existsByNameIgnoreCase(organization.getName())
|
||||
&& !existingOrganization.get().getName().equalsIgnoreCase(organization.getName())) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body("Organization with name '" + organization.getName() + "' already exists");
|
||||
}
|
||||
|
||||
organization.setId(id);
|
||||
Organization savedOrganization = organizationRepository.save(organization);
|
||||
return ResponseEntity.ok(savedOrganization);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<?> deleteOrganization(@PathVariable Long id) {
|
||||
Optional<Organization> organization = organizationRepository.findById(id);
|
||||
if (organization.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
// Prevent deletion of default organizations
|
||||
if (OrganizationService.DEFAULT_ORG_NAME.equals(organization.get().getName())
|
||||
|| OrganizationService.INTERNAL_ORG_NAME.equals(organization.get().getName())) {
|
||||
return ResponseEntity.badRequest().body("Cannot delete system organizations");
|
||||
}
|
||||
|
||||
if (!organization.get().getTeams().isEmpty()) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body("Cannot delete organization with existing teams");
|
||||
}
|
||||
|
||||
organizationRepository.deleteById(id);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
}
|
||||
+37
-8
@@ -14,11 +14,16 @@ import jakarta.transaction.Transactional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.proprietary.model.Organization;
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.security.config.PremiumEndpoint;
|
||||
import stirling.software.proprietary.security.database.repository.UserRepository;
|
||||
import stirling.software.proprietary.security.model.User;
|
||||
import stirling.software.proprietary.security.repository.OrganizationRepository;
|
||||
import stirling.software.proprietary.security.repository.TeamRepository;
|
||||
import stirling.software.proprietary.security.service.OrganizationService;
|
||||
import stirling.software.proprietary.security.service.OrganizationValidationService;
|
||||
import stirling.software.proprietary.security.service.RoleBasedAuthorizationService;
|
||||
import stirling.software.proprietary.security.service.TeamService;
|
||||
|
||||
@Controller
|
||||
@@ -27,35 +32,50 @@ import stirling.software.proprietary.security.service.TeamService;
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@PremiumEndpoint
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageOrgTeams()")
|
||||
public class TeamController {
|
||||
|
||||
private final TeamRepository teamRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final OrganizationRepository organizationRepository;
|
||||
private final OrganizationService organizationService;
|
||||
private final OrganizationValidationService organizationValidationService;
|
||||
private final RoleBasedAuthorizationService authorizationService;
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PostMapping("/create")
|
||||
public RedirectView createTeam(@RequestParam("name") String name) {
|
||||
if (teamRepository.existsByNameIgnoreCase(name)) {
|
||||
public RedirectView createTeam(
|
||||
@RequestParam("name") String name,
|
||||
@RequestParam("organizationId") Long organizationId) {
|
||||
Organization organization = organizationService.getOrCreateDefaultOrganization();
|
||||
if (organizationId != null) {
|
||||
organization = organizationRepository.findById(organizationId).orElse(organization);
|
||||
}
|
||||
|
||||
if (teamRepository.existsByNameIgnoreCaseAndOrganizationId(name, organization.getId())) {
|
||||
return new RedirectView("/teams?messageType=teamExists");
|
||||
}
|
||||
Team team = new Team();
|
||||
team.setName(name);
|
||||
team.setOrganization(organization);
|
||||
teamRepository.save(team);
|
||||
return new RedirectView("/teams?messageType=teamCreated");
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PostMapping("/rename")
|
||||
@PreAuthorize(
|
||||
"@roleBasedAuthorizationService.canManageTeam(@teamRepository.findById(#teamId).orElse(null))")
|
||||
public RedirectView renameTeam(
|
||||
@RequestParam("teamId") Long teamId, @RequestParam("newName") String newName) {
|
||||
Optional<Team> existing = teamRepository.findById(teamId);
|
||||
if (existing.isEmpty()) {
|
||||
return new RedirectView("/teams?messageType=teamNotFound");
|
||||
}
|
||||
if (teamRepository.existsByNameIgnoreCase(newName)) {
|
||||
Team team = existing.get();
|
||||
|
||||
if (teamRepository.existsByNameIgnoreCaseAndOrganizationId(
|
||||
newName, team.getOrganization().getId())) {
|
||||
return new RedirectView("/teams?messageType=teamNameExists");
|
||||
}
|
||||
Team team = existing.get();
|
||||
|
||||
// Prevent renaming the Internal team
|
||||
if (team.getName().equals(TeamService.INTERNAL_TEAM_NAME)) {
|
||||
@@ -67,9 +87,10 @@ public class TeamController {
|
||||
return new RedirectView("/teams?messageType=teamRenamed");
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PostMapping("/delete")
|
||||
@Transactional
|
||||
@PreAuthorize(
|
||||
"@roleBasedAuthorizationService.canManageTeam(@teamRepository.findById(#teamId).orElse(null))")
|
||||
public RedirectView deleteTeam(@RequestParam("teamId") Long teamId) {
|
||||
Optional<Team> teamOpt = teamRepository.findById(teamId);
|
||||
if (teamOpt.isEmpty()) {
|
||||
@@ -92,9 +113,10 @@ public class TeamController {
|
||||
return new RedirectView("/teams?messageType=teamDeleted");
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PostMapping("/addUser")
|
||||
@Transactional
|
||||
@PreAuthorize(
|
||||
"@roleBasedAuthorizationService.canAddUserToTeam(#userId, @teamRepository.findById(#teamId).orElse(null))")
|
||||
public RedirectView addUserToTeam(
|
||||
@RequestParam("teamId") Long teamId, @RequestParam("userId") Long userId) {
|
||||
|
||||
@@ -121,6 +143,13 @@ public class TeamController {
|
||||
return new RedirectView("/teams/" + teamId + "?error=cannotMoveInternalUsers");
|
||||
}
|
||||
|
||||
// Ensure user and team are in the same organization (or user has no org yet)
|
||||
if (user.getOrganization() != null
|
||||
&& !organizationValidationService.isTeamInOrganization(
|
||||
team, user.getOrganization())) {
|
||||
return new RedirectView("/teams/" + teamId + "?error=userNotInSameOrganization");
|
||||
}
|
||||
|
||||
// Assign user to team
|
||||
user.setTeam(team);
|
||||
userRepository.save(user);
|
||||
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
package stirling.software.proprietary.security.controller.api;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.model.enumeration.Role;
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.security.config.PremiumEndpoint;
|
||||
import stirling.software.proprietary.security.database.repository.UserRepository;
|
||||
import stirling.software.proprietary.security.model.User;
|
||||
import stirling.software.proprietary.security.repository.TeamRepository;
|
||||
import stirling.software.proprietary.security.service.RoleBasedAuthorizationService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/team-lead")
|
||||
@Tag(name = "Team Lead", description = "Team Lead Management APIs")
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@PremiumEndpoint
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageTeamUsers()")
|
||||
public class TeamLeadController {
|
||||
|
||||
private final TeamRepository teamRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final RoleBasedAuthorizationService authorizationService;
|
||||
|
||||
/** Get team members that the current team lead can manage */
|
||||
@GetMapping("/my-team-members")
|
||||
public ResponseEntity<List<User>> getMyTeamMembers() {
|
||||
if (!authorizationService.canManageTeamUsers()) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
|
||||
}
|
||||
|
||||
User currentUser = authorizationService.getCurrentUser();
|
||||
if (currentUser == null || currentUser.getTeam() == null) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
List<User> teamMembers = userRepository.findByTeam(currentUser.getTeam());
|
||||
return ResponseEntity.ok(teamMembers);
|
||||
}
|
||||
|
||||
/** Add a user to the team lead's team */
|
||||
@PostMapping("/add-member")
|
||||
@Transactional
|
||||
public ResponseEntity<?> addMemberToMyTeam(@RequestParam("userId") Long userId) {
|
||||
User currentUser = authorizationService.getCurrentUser();
|
||||
if (currentUser == null || currentUser.getTeam() == null) {
|
||||
return ResponseEntity.badRequest().body("Team lead must be assigned to a team");
|
||||
}
|
||||
|
||||
if (!authorizationService.canAddUserToTeam(userId, currentUser.getTeam())) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body("Not authorized to add users to this team");
|
||||
}
|
||||
|
||||
Optional<User> userOpt = userRepository.findById(userId);
|
||||
if (userOpt.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
User user = userOpt.get();
|
||||
|
||||
// Check if user is already in a team
|
||||
if (user.getTeam() != null) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body("User is already assigned to team: " + user.getTeam().getName());
|
||||
}
|
||||
|
||||
// Assign user to team
|
||||
user.setTeam(currentUser.getTeam());
|
||||
userRepository.save(user);
|
||||
|
||||
return ResponseEntity.ok().body("User added to team successfully");
|
||||
}
|
||||
|
||||
/** Remove a user from the team lead's team */
|
||||
@PostMapping("/remove-member")
|
||||
@Transactional
|
||||
public ResponseEntity<?> removeMemberFromMyTeam(@RequestParam("userId") Long userId) {
|
||||
if (!authorizationService.canRemoveUserFromTeam(userId)) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body("Not authorized to remove this user");
|
||||
}
|
||||
|
||||
Optional<User> userOpt = userRepository.findById(userId);
|
||||
if (userOpt.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
User user = userOpt.get();
|
||||
User currentUser = authorizationService.getCurrentUser();
|
||||
|
||||
// Prevent team leads from removing themselves
|
||||
if (currentUser != null && currentUser.getId().equals(userId)) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body("Team leads cannot remove themselves from the team");
|
||||
}
|
||||
|
||||
// Remove user from team
|
||||
user.setTeam(null);
|
||||
userRepository.save(user);
|
||||
|
||||
return ResponseEntity.ok().body("User removed from team successfully");
|
||||
}
|
||||
|
||||
/** Update a team member's role (team leads can only assign USER role) */
|
||||
@PostMapping("/update-member-role")
|
||||
@Transactional
|
||||
public ResponseEntity<?> updateMemberRole(
|
||||
@RequestParam("userId") Long userId, @RequestParam("role") String roleString) {
|
||||
|
||||
if (!authorizationService.canManageUser(userId)) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body("Not authorized to manage this user");
|
||||
}
|
||||
|
||||
try {
|
||||
Role newRole = Role.fromString(roleString);
|
||||
|
||||
// Team leads can only assign USER role
|
||||
if (!authorizationService.canAssignRole(newRole)) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body("Not authorized to assign role: " + newRole.getRoleName());
|
||||
}
|
||||
|
||||
Optional<User> userOpt = userRepository.findById(userId);
|
||||
if (userOpt.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
User user = userOpt.get();
|
||||
user.setUserRole(newRole);
|
||||
userRepository.save(user);
|
||||
|
||||
return ResponseEntity.ok().body("User role updated successfully");
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.badRequest().body("Invalid role: " + roleString);
|
||||
}
|
||||
}
|
||||
|
||||
/** Get team information for the current team lead */
|
||||
@GetMapping("/my-team")
|
||||
public ResponseEntity<Team> getMyTeam() {
|
||||
User currentUser = authorizationService.getCurrentUser();
|
||||
if (currentUser == null || currentUser.getTeam() == null) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
if (!authorizationService.canManageTeam(currentUser.getTeam())) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(currentUser.getTeam());
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -207,7 +207,7 @@ public class UserController {
|
||||
return "redirect:/account";
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageAllUsers()")
|
||||
@PostMapping("/admin/saveUser")
|
||||
public RedirectView saveUser(
|
||||
@RequestParam(name = "username", required = true) String username,
|
||||
@@ -279,7 +279,7 @@ public class UserController {
|
||||
true);
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageAllUsers()")
|
||||
@PostMapping("/admin/changeRole")
|
||||
@Transactional
|
||||
public RedirectView changeRole(
|
||||
@@ -342,7 +342,7 @@ public class UserController {
|
||||
true);
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageAllUsers()")
|
||||
@PostMapping("/admin/changeUserEnabled/{username}")
|
||||
public RedirectView changeUserEnabled(
|
||||
@PathVariable("username") String username,
|
||||
@@ -392,7 +392,7 @@ public class UserController {
|
||||
true);
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageAllUsers()")
|
||||
@PostMapping("/admin/deleteUser/{username}")
|
||||
public RedirectView deleteUser(
|
||||
@PathVariable("username") String username, Authentication authentication) {
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ public class DatabaseWebController {
|
||||
|
||||
private final DatabaseService databaseService;
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageAllUsers()")
|
||||
@GetMapping("/database")
|
||||
public String database(HttpServletRequest request, Model model, Authentication authentication) {
|
||||
String error = request.getParameter("error");
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ public class TeamWebController {
|
||||
private final UserRepository userRepository;
|
||||
|
||||
@GetMapping
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageAllUsers()")
|
||||
public String listTeams(HttpServletRequest request, Model model) {
|
||||
// Get teams with user counts using a DTO projection
|
||||
List<TeamWithUserCountDTO> allTeamsWithCounts = teamRepository.findAllTeamsWithUserCount();
|
||||
@@ -87,7 +87,7 @@ public class TeamWebController {
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PreAuthorize("@roleBasedAuthorizationService.canManageAllUsers()")
|
||||
public String viewTeamDetails(
|
||||
HttpServletRequest request, @PathVariable("id") Long id, Model model) {
|
||||
// Get the team
|
||||
|
||||
+7
-4
@@ -24,9 +24,6 @@ public interface UserRepository extends JpaRepository<User, Long> {
|
||||
|
||||
List<User> findByAuthenticationTypeIgnoreCase(String authenticationType);
|
||||
|
||||
@Query("SELECT u FROM User u WHERE u.team IS NULL")
|
||||
List<User> findAllWithoutTeam();
|
||||
|
||||
@Query(value = "SELECT u FROM User u LEFT JOIN FETCH u.team")
|
||||
List<User> findAllWithTeam();
|
||||
|
||||
@@ -36,5 +33,11 @@ public interface UserRepository extends JpaRepository<User, Long> {
|
||||
|
||||
long countByTeam(Team team);
|
||||
|
||||
List<User> findAllByTeam(Team team);
|
||||
List<User> findByTeam(Team team);
|
||||
|
||||
@Query("SELECT u FROM User u WHERE u.team IS NULL")
|
||||
List<User> findUsersWithoutTeam();
|
||||
|
||||
@Query("SELECT u FROM User u JOIN u.authorities a WHERE a.authority = :role")
|
||||
List<User> findByRole(@Param("role") String role);
|
||||
}
|
||||
|
||||
+89
-1
@@ -55,7 +55,11 @@ public class User implements Serializable {
|
||||
@Column(name = "authenticationtype")
|
||||
private String authenticationType;
|
||||
|
||||
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user")
|
||||
@OneToMany(
|
||||
fetch = FetchType.EAGER,
|
||||
cascade = CascadeType.ALL,
|
||||
mappedBy = "user",
|
||||
orphanRemoval = true)
|
||||
private Set<Authority> authorities = new HashSet<>();
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@@ -102,4 +106,88 @@ public class User implements Serializable {
|
||||
public boolean hasPassword() {
|
||||
return this.password != null && !this.password.isEmpty();
|
||||
}
|
||||
|
||||
public stirling.software.proprietary.model.Organization getOrganization() {
|
||||
return this.team != null ? this.team.getOrganization() : null;
|
||||
}
|
||||
|
||||
// Role-based permission methods
|
||||
public Role getUserRole() {
|
||||
String roleString = getRolesAsString();
|
||||
if (roleString == null || roleString.isEmpty()) return Role.USER;
|
||||
|
||||
try {
|
||||
return Role.fromString(roleString);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Role.USER; // Default fallback
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSystemAdmin() {
|
||||
Role role = getUserRole();
|
||||
return role.isSystemAdmin();
|
||||
}
|
||||
|
||||
public boolean isOrgAdmin() {
|
||||
Role role = getUserRole();
|
||||
return role.isOrgAdmin();
|
||||
}
|
||||
|
||||
public boolean isTeamLead() {
|
||||
Role role = getUserRole();
|
||||
return role.isTeamLead();
|
||||
}
|
||||
|
||||
public boolean canManageUser(User otherUser) {
|
||||
// System admins can manage anyone
|
||||
if (isSystemAdmin()) return true;
|
||||
|
||||
// Org admins can manage users in their organization
|
||||
if (isOrgAdmin()) {
|
||||
stirling.software.proprietary.model.Organization thisOrg = getOrganization();
|
||||
stirling.software.proprietary.model.Organization otherOrg = otherUser.getOrganization();
|
||||
return thisOrg != null && otherOrg != null && thisOrg.getId().equals(otherOrg.getId());
|
||||
}
|
||||
|
||||
// Team leads can manage users in their team
|
||||
if (isTeamLead()) {
|
||||
return this.team != null
|
||||
&& otherUser.team != null
|
||||
&& this.team.getId().equals(otherUser.team.getId());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canManageTeam(stirling.software.proprietary.model.Team targetTeam) {
|
||||
if (targetTeam == null) return false;
|
||||
|
||||
// System admins can manage any team
|
||||
if (isSystemAdmin()) return true;
|
||||
|
||||
// Org admins can manage teams in their organization
|
||||
if (isOrgAdmin()) {
|
||||
stirling.software.proprietary.model.Organization thisOrg = getOrganization();
|
||||
stirling.software.proprietary.model.Organization teamOrg = targetTeam.getOrganization();
|
||||
return thisOrg != null && teamOrg != null && thisOrg.getId().equals(teamOrg.getId());
|
||||
}
|
||||
|
||||
// Team leads can only manage their own team
|
||||
if (isTeamLead()) {
|
||||
return this.team != null && this.team.getId().equals(targetTeam.getId());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setUserRole(Role role) {
|
||||
// Clear existing authorities
|
||||
this.authorities.clear();
|
||||
|
||||
// Add new authority
|
||||
Authority authority = new Authority();
|
||||
authority.setAuthority(role.getRoleId());
|
||||
authority.setUser(this);
|
||||
this.authorities.add(authority);
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package stirling.software.proprietary.security.repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import stirling.software.proprietary.model.Organization;
|
||||
import stirling.software.proprietary.model.dto.OrganizationWithTeamCountDTO;
|
||||
|
||||
@Repository
|
||||
public interface OrganizationRepository extends JpaRepository<Organization, Long> {
|
||||
Optional<Organization> findByName(String name);
|
||||
|
||||
@Query(
|
||||
"SELECT new stirling.software.proprietary.model.dto.OrganizationWithTeamCountDTO(o.id, o.name, o.description, COUNT(t)) "
|
||||
+ "FROM Organization o LEFT JOIN o.teams t GROUP BY o.id, o.name, o.description")
|
||||
List<OrganizationWithTeamCountDTO> findAllOrganizationsWithTeamCount();
|
||||
|
||||
boolean existsByNameIgnoreCase(String name);
|
||||
}
|
||||
+14
@@ -14,10 +14,24 @@ import stirling.software.proprietary.model.dto.TeamWithUserCountDTO;
|
||||
public interface TeamRepository extends JpaRepository<Team, Long> {
|
||||
Optional<Team> findByName(String name);
|
||||
|
||||
Optional<Team> findByNameAndOrganizationId(String name, Long organizationId);
|
||||
|
||||
List<Team> findByOrganizationId(Long organizationId);
|
||||
|
||||
@Query(
|
||||
"SELECT new stirling.software.proprietary.model.dto.TeamWithUserCountDTO(t.id, t.name, COUNT(u)) "
|
||||
+ "FROM Team t LEFT JOIN t.users u WHERE t.organization.id = :organizationId GROUP BY t.id, t.name")
|
||||
List<TeamWithUserCountDTO> findAllTeamsWithUserCountByOrganizationId(Long organizationId);
|
||||
|
||||
@Query(
|
||||
"SELECT new stirling.software.proprietary.model.dto.TeamWithUserCountDTO(t.id, t.name, COUNT(u)) "
|
||||
+ "FROM Team t LEFT JOIN t.users u GROUP BY t.id, t.name")
|
||||
List<TeamWithUserCountDTO> findAllTeamsWithUserCount();
|
||||
|
||||
boolean existsByNameIgnoreCase(String name);
|
||||
|
||||
boolean existsByNameIgnoreCaseAndOrganizationId(String name, Long organizationId);
|
||||
|
||||
@Query("SELECT t FROM Team t WHERE t.organization IS NULL")
|
||||
List<Team> findTeamsWithoutOrganization();
|
||||
}
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ class AppUpdateAuthService implements ShowAdminInterface {
|
||||
}
|
||||
Optional<User> user = userRepository.findByUsername(authentication.getName());
|
||||
if (user.isPresent() && showUpdateOnlyAdmin) {
|
||||
return "ROLE_ADMIN".equals(user.get().getRolesAsString());
|
||||
return user.get().isSystemAdmin();
|
||||
}
|
||||
return showUpdate;
|
||||
}
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package stirling.software.proprietary.security.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.proprietary.model.Organization;
|
||||
import stirling.software.proprietary.security.repository.OrganizationRepository;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class OrganizationService {
|
||||
|
||||
private final OrganizationRepository organizationRepository;
|
||||
|
||||
public static final String DEFAULT_ORG_NAME = "Default Organization";
|
||||
public static final String INTERNAL_ORG_NAME = "Internal Organization";
|
||||
|
||||
public Organization getOrCreateDefaultOrganization() {
|
||||
return organizationRepository
|
||||
.findByName(DEFAULT_ORG_NAME)
|
||||
.orElseGet(
|
||||
() -> {
|
||||
Organization defaultOrg = new Organization();
|
||||
defaultOrg.setName(DEFAULT_ORG_NAME);
|
||||
defaultOrg.setDescription("Default organization for initial setup");
|
||||
return organizationRepository.save(defaultOrg);
|
||||
});
|
||||
}
|
||||
|
||||
public Organization getOrCreateInternalOrganization() {
|
||||
return organizationRepository
|
||||
.findByName(INTERNAL_ORG_NAME)
|
||||
.orElseGet(
|
||||
() -> {
|
||||
Organization internalOrg = new Organization();
|
||||
internalOrg.setName(INTERNAL_ORG_NAME);
|
||||
internalOrg.setDescription(
|
||||
"Internal organization for system operations");
|
||||
return organizationRepository.save(internalOrg);
|
||||
});
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package stirling.software.proprietary.security.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.proprietary.model.Organization;
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.security.model.User;
|
||||
import stirling.software.proprietary.security.repository.TeamRepository;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class OrganizationValidationService {
|
||||
|
||||
private final TeamRepository teamRepository;
|
||||
|
||||
/**
|
||||
* Validates that a user has access to a specific team. Users can only access teams within their
|
||||
* own organization.
|
||||
*/
|
||||
public boolean canUserAccessTeam(User user, Team team) {
|
||||
if (user == null || team == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Organization userOrg = user.getOrganization();
|
||||
Organization teamOrg = team.getOrganization();
|
||||
|
||||
if (userOrg == null || teamOrg == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return userOrg.getId().equals(teamOrg.getId());
|
||||
}
|
||||
|
||||
/** Validates that a user has access to a specific team by ID. */
|
||||
public boolean canUserAccessTeam(User user, Long teamId) {
|
||||
if (user == null || teamId == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Organization userOrg = user.getOrganization();
|
||||
if (userOrg == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return teamRepository
|
||||
.findById(teamId)
|
||||
.map(team -> userOrg.getId().equals(team.getOrganization().getId()))
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
/** Validates that two users belong to the same organization. */
|
||||
public boolean areUsersInSameOrganization(User user1, User user2) {
|
||||
if (user1 == null || user2 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Organization org1 = user1.getOrganization();
|
||||
Organization org2 = user2.getOrganization();
|
||||
|
||||
if (org1 == null || org2 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return org1.getId().equals(org2.getId());
|
||||
}
|
||||
|
||||
/** Validates that a team belongs to a specific organization. */
|
||||
public boolean isTeamInOrganization(Team team, Organization organization) {
|
||||
if (team == null || organization == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Organization teamOrg = team.getOrganization();
|
||||
return teamOrg != null && teamOrg.getId().equals(organization.getId());
|
||||
}
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
package stirling.software.proprietary.security.service;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.common.model.enumeration.Role;
|
||||
import stirling.software.proprietary.model.Organization;
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.security.database.repository.UserRepository;
|
||||
import stirling.software.proprietary.security.model.User;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class RoleBasedAuthorizationService {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
/** Gets the current authenticated user */
|
||||
public User getCurrentUser() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication == null || !authentication.isAuthenticated()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String username = authentication.getName();
|
||||
return userRepository.findByUsername(username).orElse(null);
|
||||
}
|
||||
|
||||
/** Checks if current user can manage users across all organizations (System Admin) */
|
||||
public boolean canManageAllUsers() {
|
||||
User currentUser = getCurrentUser();
|
||||
return currentUser != null && currentUser.isSystemAdmin();
|
||||
}
|
||||
|
||||
/** Checks if current user can manage users within their organization (Org Admin or above) */
|
||||
public boolean canManageOrgUsers() {
|
||||
User currentUser = getCurrentUser();
|
||||
return currentUser != null && currentUser.isOrgAdmin();
|
||||
}
|
||||
|
||||
/** Checks if current user can manage team members (Team Lead or above) */
|
||||
public boolean canManageTeamUsers() {
|
||||
User currentUser = getCurrentUser();
|
||||
return currentUser != null && currentUser.isTeamLead();
|
||||
}
|
||||
|
||||
/** Checks if current user can manage a specific user */
|
||||
public boolean canManageUser(Long userId) {
|
||||
User currentUser = getCurrentUser();
|
||||
if (currentUser == null) return false;
|
||||
|
||||
User targetUser = userRepository.findById(userId).orElse(null);
|
||||
if (targetUser == null) return false;
|
||||
|
||||
return currentUser.canManageUser(targetUser);
|
||||
}
|
||||
|
||||
/** Checks if current user can manage a specific team */
|
||||
public boolean canManageTeam(Team team) {
|
||||
User currentUser = getCurrentUser();
|
||||
if (currentUser == null || team == null) return false;
|
||||
|
||||
return currentUser.canManageTeam(team);
|
||||
}
|
||||
|
||||
/** Checks if current user can manage teams within their organization */
|
||||
public boolean canManageOrgTeams() {
|
||||
User currentUser = getCurrentUser();
|
||||
return currentUser != null && currentUser.isOrgAdmin();
|
||||
}
|
||||
|
||||
/** Checks if current user can create/manage organizations (System Admin only) */
|
||||
public boolean canManageOrganizations() {
|
||||
return canManageAllUsers();
|
||||
}
|
||||
|
||||
/** Checks if current user can assign roles */
|
||||
public boolean canAssignRole(Role targetRole) {
|
||||
User currentUser = getCurrentUser();
|
||||
if (currentUser == null) return false;
|
||||
|
||||
// Users can only assign roles that are lower than or equal to their own
|
||||
return currentUser.getUserRole().hasAuthorityOver(targetRole);
|
||||
}
|
||||
|
||||
/** Checks if current user can remove a user from their team/organization */
|
||||
public boolean canRemoveUserFromTeam(Long userId) {
|
||||
return canManageUser(userId);
|
||||
}
|
||||
|
||||
/** Checks if current user can add a user to a specific team */
|
||||
public boolean canAddUserToTeam(Long userId, Team team) {
|
||||
User currentUser = getCurrentUser();
|
||||
if (currentUser == null || team == null) return false;
|
||||
|
||||
// Must be able to manage both the user and the target team
|
||||
return canManageUser(userId) && currentUser.canManageTeam(team);
|
||||
}
|
||||
|
||||
/** Gets the highest role the current user can assign to others */
|
||||
public Role getMaxAssignableRole() {
|
||||
User currentUser = getCurrentUser();
|
||||
if (currentUser == null) return Role.USER;
|
||||
|
||||
return switch (currentUser.getUserRole()) {
|
||||
case SYSTEM_ADMIN, ADMIN -> Role.ORG_ADMIN; // System admins can create org admins
|
||||
case ORG_ADMIN -> Role.TEAM_LEAD; // Org admins can create team leads
|
||||
case TEAM_LEAD -> Role.USER; // Team leads can only create regular users
|
||||
default -> Role.USER;
|
||||
};
|
||||
}
|
||||
|
||||
/** Checks if current user can view organization details */
|
||||
public boolean canViewOrganization(Organization organization) {
|
||||
User currentUser = getCurrentUser();
|
||||
if (currentUser == null || organization == null) return false;
|
||||
|
||||
// System admins can view any org
|
||||
if (currentUser.isSystemAdmin()) return true;
|
||||
|
||||
// Org admins and team leads can view their own organization
|
||||
Organization userOrg = currentUser.getOrganization();
|
||||
return userOrg != null && userOrg.getId().equals(organization.getId());
|
||||
}
|
||||
}
|
||||
+20
-2
@@ -4,6 +4,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.proprietary.model.Organization;
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.security.repository.TeamRepository;
|
||||
|
||||
@@ -12,29 +13,46 @@ import stirling.software.proprietary.security.repository.TeamRepository;
|
||||
public class TeamService {
|
||||
|
||||
private final TeamRepository teamRepository;
|
||||
private final OrganizationService organizationService;
|
||||
|
||||
public static final String DEFAULT_TEAM_NAME = "Default";
|
||||
public static final String INTERNAL_TEAM_NAME = "Internal";
|
||||
|
||||
public Team getOrCreateDefaultTeam() {
|
||||
Organization defaultOrg = organizationService.getOrCreateDefaultOrganization();
|
||||
return teamRepository
|
||||
.findByName(DEFAULT_TEAM_NAME)
|
||||
.findByNameAndOrganizationId(DEFAULT_TEAM_NAME, defaultOrg.getId())
|
||||
.orElseGet(
|
||||
() -> {
|
||||
Team defaultTeam = new Team();
|
||||
defaultTeam.setName(DEFAULT_TEAM_NAME);
|
||||
defaultTeam.setOrganization(defaultOrg);
|
||||
return teamRepository.save(defaultTeam);
|
||||
});
|
||||
}
|
||||
|
||||
public Team getOrCreateInternalTeam() {
|
||||
Organization internalOrg = organizationService.getOrCreateInternalOrganization();
|
||||
return teamRepository
|
||||
.findByName(INTERNAL_TEAM_NAME)
|
||||
.findByNameAndOrganizationId(INTERNAL_TEAM_NAME, internalOrg.getId())
|
||||
.orElseGet(
|
||||
() -> {
|
||||
Team internalTeam = new Team();
|
||||
internalTeam.setName(INTERNAL_TEAM_NAME);
|
||||
internalTeam.setOrganization(internalOrg);
|
||||
return teamRepository.save(internalTeam);
|
||||
});
|
||||
}
|
||||
|
||||
public Team getOrCreateTeamForOrganization(String teamName, Organization organization) {
|
||||
return teamRepository
|
||||
.findByNameAndOrganizationId(teamName, organization.getId())
|
||||
.orElseGet(
|
||||
() -> {
|
||||
Team team = new Team();
|
||||
team.setName(teamName);
|
||||
team.setOrganization(organization);
|
||||
return teamRepository.save(team);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -328,6 +328,10 @@ public class UserService implements UserServiceInterface {
|
||||
return userRepository.findByUsernameIgnoreCaseWithSettings(username);
|
||||
}
|
||||
|
||||
public List<User> findByRole(String role) {
|
||||
return userRepository.findByRole(role);
|
||||
}
|
||||
|
||||
public Authority findRole(User user) {
|
||||
return authorityRepository.findByUserId(user.getId());
|
||||
}
|
||||
@@ -620,10 +624,14 @@ public class UserService implements UserServiceInterface {
|
||||
}
|
||||
|
||||
public List<User> getUsersWithoutTeam() {
|
||||
return userRepository.findAllWithoutTeam();
|
||||
return userRepository.findUsersWithoutTeam();
|
||||
}
|
||||
|
||||
public void saveAll(List<User> users) {
|
||||
userRepository.saveAll(users);
|
||||
}
|
||||
|
||||
public User saveUser(User user) {
|
||||
return userRepository.save(user);
|
||||
}
|
||||
}
|
||||
|
||||
+36
-6
@@ -12,6 +12,7 @@ import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import stirling.software.proprietary.model.Organization;
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.security.repository.TeamRepository;
|
||||
|
||||
@@ -20,14 +21,23 @@ class TeamServiceTest {
|
||||
|
||||
@Mock private TeamRepository teamRepository;
|
||||
|
||||
@Mock private OrganizationService organizationService;
|
||||
|
||||
@InjectMocks private TeamService teamService;
|
||||
|
||||
@Test
|
||||
void getDefaultTeam() {
|
||||
var organization = new Organization();
|
||||
organization.setId(1L);
|
||||
organization.setName("Default Organization");
|
||||
|
||||
var team = new Team();
|
||||
team.setName("Marleyans");
|
||||
team.setOrganization(organization);
|
||||
|
||||
when(teamRepository.findByName(TeamService.DEFAULT_TEAM_NAME))
|
||||
when(organizationService.getOrCreateDefaultOrganization()).thenReturn(organization);
|
||||
when(teamRepository.findByNameAndOrganizationId(
|
||||
TeamService.DEFAULT_TEAM_NAME, organization.getId()))
|
||||
.thenReturn(Optional.of(team));
|
||||
|
||||
Team result = teamService.getOrCreateDefaultTeam();
|
||||
@@ -37,12 +47,19 @@ class TeamServiceTest {
|
||||
|
||||
@Test
|
||||
void createDefaultTeam_whenRepositoryIsEmpty() {
|
||||
var organization = new Organization();
|
||||
organization.setId(1L);
|
||||
organization.setName("Default Organization");
|
||||
|
||||
String teamName = "Default";
|
||||
var defaultTeam = new Team();
|
||||
defaultTeam.setId(1L);
|
||||
defaultTeam.setName(teamName);
|
||||
defaultTeam.setOrganization(organization);
|
||||
|
||||
when(teamRepository.findByName(teamName)).thenReturn(Optional.empty());
|
||||
when(organizationService.getOrCreateDefaultOrganization()).thenReturn(organization);
|
||||
when(teamRepository.findByNameAndOrganizationId(teamName, organization.getId()))
|
||||
.thenReturn(Optional.empty());
|
||||
when(teamRepository.save(any(Team.class))).thenReturn(defaultTeam);
|
||||
|
||||
Team result = teamService.getOrCreateDefaultTeam();
|
||||
@@ -52,10 +69,17 @@ class TeamServiceTest {
|
||||
|
||||
@Test
|
||||
void getInternalTeam() {
|
||||
var organization = new Organization();
|
||||
organization.setId(2L);
|
||||
organization.setName("Internal Organization");
|
||||
|
||||
var team = new Team();
|
||||
team.setName("Eldians");
|
||||
team.setOrganization(organization);
|
||||
|
||||
when(teamRepository.findByName(TeamService.INTERNAL_TEAM_NAME))
|
||||
when(organizationService.getOrCreateInternalOrganization()).thenReturn(organization);
|
||||
when(teamRepository.findByNameAndOrganizationId(
|
||||
TeamService.INTERNAL_TEAM_NAME, organization.getId()))
|
||||
.thenReturn(Optional.of(team));
|
||||
|
||||
Team result = teamService.getOrCreateInternalTeam();
|
||||
@@ -65,15 +89,21 @@ class TeamServiceTest {
|
||||
|
||||
@Test
|
||||
void createInternalTeam_whenRepositoryIsEmpty() {
|
||||
var organization = new Organization();
|
||||
organization.setId(2L);
|
||||
organization.setName("Internal Organization");
|
||||
|
||||
String teamName = "Internal";
|
||||
Team internalTeam = new Team();
|
||||
internalTeam.setId(2L);
|
||||
internalTeam.setName(teamName);
|
||||
internalTeam.setOrganization(organization);
|
||||
|
||||
when(teamRepository.findByName(teamName)).thenReturn(Optional.empty());
|
||||
when(teamRepository.save(any(Team.class))).thenReturn(internalTeam);
|
||||
when(teamRepository.findByName(TeamService.INTERNAL_TEAM_NAME))
|
||||
when(organizationService.getOrCreateInternalOrganization()).thenReturn(organization);
|
||||
when(teamRepository.findByNameAndOrganizationId(
|
||||
TeamService.INTERNAL_TEAM_NAME, organization.getId()))
|
||||
.thenReturn(Optional.empty());
|
||||
when(teamRepository.save(any(Team.class))).thenReturn(internalTeam);
|
||||
|
||||
Team result = teamService.getOrCreateInternalTeam();
|
||||
|
||||
|
||||
+1
-1
@@ -286,7 +286,7 @@ class UserServiceTest {
|
||||
String username = "testuser";
|
||||
String password = "password123";
|
||||
Long teamId = 1L;
|
||||
String customRole = Role.LIMITED_API_USER.getRoleId();
|
||||
String customRole = Role.USER.getRoleId();
|
||||
String encodedPassword = "encodedPassword123";
|
||||
|
||||
when(passwordEncoder.encode(password)).thenReturn(encodedPassword);
|
||||
|
||||
Reference in New Issue
Block a user