ICM Account & Authentication¶
Türkçe
ICM (Identity & Configuration Management) modülü, çok kiracılı (multi-tenant) kullanıcı yönetimi, JWT tabanlı kimlik doğrulama, rol tabanlı yetkilendirme (RBAC) ve harici kimlik (SSO/LDAP) desteği sağlar. Tüm diğer modüller bu modüle bağımlıdır.
Overview¶
The ICM module provides authentication, authorization, and multi-tenant user management
for the entire ICOSYS platform. It runs as the core service on port 8010 with context path
/icglb/services.
┌──────────────────────────────────────────────────────────┐
│ ICM Module — icglb-services (port 8010) │
├──────────────────────────────────────────────────────────┤
│ Projects: │
│ ├ Icglb-Entities-1.0 → JPA Entities │
│ ├ Icglb-Entities-Dto-1.0 → DTOs │
│ ├ Icglb-Services-1.0 → Spring Boot Service │
│ └ ICOM-Micro-Services-Api-1.0 → Shared (JWT, ErrorCodes)│
│ │
│ Database: icglb2 (MySQL 8) │
│ ID Table: s_icglb_id │
└──────────────────────────────────────────────────────────┘
Multi-Tenancy Model¶
Türkçe
ICOSYS çok kiracılı mimaride CrProcess en üst seviye tenant'tır.
Her CrProcess altında birden fazla IcmAccount bulunabilir.
Kullanıcılar birden fazla hesaba bağlanabilir (IcmAccountUserProcess üzerinden).
Aktif hesap UserProcessSelect ile takip edilir.
CrProcess (enterprise tenant)
│
├── IcmAccount (customer account)
│ ├── Corporation (legal entity)
│ ├── IcmAccountUserProcess (user ↔ account link)
│ │ └── UserProcess (user identity)
│ │ ├── UserInfo (DB user) ─── OR ─── identityRef (external)
│ │ └── UserProcessSelect (active account per project)
│ └── IcmAccountLicense (feature & user limits)
│
└── Group (authorization group)
├── GroupRole (group → role mapping)
├── GroupUserProcess (user → group membership)
└── GroupAccount (group → account scope)
Entity Model¶
CrProcess (Tenant)¶
The top-level tenant entity. All data in ICOSYS is isolated by CrProcess.
| Field | Type | Description |
|---|---|---|
id |
Long | PK, auto-generated |
processName |
String | Internal process name |
processDisplayName |
String | Display name |
processCode |
String | Process code |
accountType |
CrProcessType | Account type enum |
crType |
CrProcessType | CrProcess type enum |
Table: s_cr_process (schema: icglb2)
Every entity in the system references CrProcess via AbstractICEntity6.crProcess.
IcmAccount¶
A customer account within a tenant.
| Field | Type | Description |
|---|---|---|
accountNo |
String(12) | PK (string, not auto-gen) |
accountName |
String | Account display name |
accountStatus |
Boolean | Active flag |
accountType |
IcmAccountType | CORPORATE or PERSONAL |
loginType |
IcmAccountLoginType | SSO, LDAP, OAUTH, EXTERNAL |
defaultUserRequest |
Boolean | Default user request policy |
addUserAuto |
Boolean | Auto-add users flag |
defaultStatus |
Boolean | Default user active status |
standardLoginStatus |
Boolean | Allow standard login |
canCreateUsers |
Boolean | Allow user creation |
Table: s_icm_account (schema: icglb2)
UserInfo (DB User)¶
Profile data for database-authenticated users.
| Field | Type | Description |
|---|---|---|
userId |
Long | PK, auto-generated |
userName |
String (encrypted) | Username (searchable hash) |
userDescription |
String (encrypted) | Display name |
companyEmployeeId |
String (encrypted) | Employee ID |
status |
Boolean | Active flag |
validationStatus |
Boolean | Email validated |
pictureName |
String | Profile picture filename |
registerDate |
Instant | Registration date |
country |
Country (FK) | User country |
city |
City (FK) | User city |
Table: s_user_info (schema: icglb2)
Related collections: userInfoAddresses, userInfoPhones, userInfoEmails (1:N)
Türkçe
userName ve userDescription alanları searchable encryption ile korunur.
Arama yapmak için hash alanı (userNameHash) kullanılır,
görüntülemek için şifrelenmiş alan (userName) çözülür.
UserLogon (Password)¶
Authentication credentials for DB users. 1:1 with UserInfo via @MapsId.
| Field | Type | Description |
|---|---|---|
userId |
Long | PK (= UserInfo.userId) |
userPassword |
String (encrypted) | BCrypt password hash |
lastLoginDate |
Instant | Last successful login |
lastLoginCount |
Integer | Total login count |
lastPasswordUpdateDate |
Instant | Password change date (auto @PrePersist) |
hintQuestion |
String (encrypted) | Security question |
hintAnswer |
String (encrypted) | Security answer |
Table: s_user_logon (schema: icglb2)
UserProcess (User Identity)¶
The central user identity entity that bridges DB users and external identities.
| Field | Type | Description |
|---|---|---|
id |
Long | PK, auto-generated |
userInfo |
UserInfo (FK) | DB user reference (null for external) |
userCode |
String (encrypted) | Username (encrypted, searchable hash) |
userDescription |
String (encrypted) | Display name |
emailAddress |
String (encrypted) | Email for external users |
corporationCode |
String | Active corporation |
status |
Boolean | Active flag |
dateFormat |
String | User preference |
decimalSeperator |
String | Decimal separator preference |
thousandSeperator |
String | Thousand separator preference |
timeZone |
String | Time zone preference |
languageCode |
String | Language preference |
lastLoginDate |
Instant | Last login timestamp |
tokenVersion |
Long (default 0) | JWT invalidation counter |
identityRef |
String(500), UNIQUE | External identity reference |
Table: s_user_process (schema: icglb2)
XOR Rule: userInfo XOR identityRef — exactly one must be set.
IcmAccountUserProcess (User ↔ Account Link)¶
Junction table linking users to accounts with ownership flags.
| Field | Type | Description |
|---|---|---|
accountNo |
String | PK part — Account reference |
userProcessId |
Long | PK part — User reference |
status |
Boolean | Link active flag |
ownerStatus |
Boolean | User is account owner |
adminStatus |
Boolean | User is account admin |
Table: s_icm_account_user_process (schema: icglb2)
Türkçe
ownerStatus ve adminStatus bayrakları, frontend'deki "Owner/Admin Bypass"
mekanizmasını besler. Bir kullanıcı role sahip olmasa bile, hesap sahibi veya admin ise
belirli sayfalara erişebilir (bkz. permissions.md).
UserProcessSelect (Active Account)¶
Tracks which account a user currently has active per project.
| Field | Type | Description |
|---|---|---|
userProcessId |
Long | PK part |
projectCode |
String | PK part — Project (e.g., "ICOSYS") |
accountNo |
String | PK part — Active account |
corporation |
Corporation (FK) | Active corporation (nullable) |
Table: s_user_process_select (schema: icglb2)
IcmAccountLicense¶
License and feature limits per account per project.
| Field | Type | Description |
|---|---|---|
id |
Long | PK |
accountNo |
String | Account reference |
projectCode |
String | Project reference |
licenseKey |
String (encrypted) | Encrypted license key |
licenseType |
LicenseType | License tier |
maxUsers |
Integer | Maximum user count |
maxCorporations |
Integer | Maximum corporation count |
startDate |
LocalDate | License start |
expiryDate |
LocalDate | License expiry |
gracePeriodDays |
Integer | Grace period after expiry |
modules |
String (JSON) | Enabled modules array |
features |
String (JSON) | Enabled features array |
isActive |
Boolean | Active flag |
validationStatus |
LicenseStatus | Validation result |
Table: s_icm_account_license2 (schema: icglb2)
Authorization Model (RBAC)¶
Entity Chain¶
User Login
↓
UserProcess (user identity)
↓ GroupUserProcess (membership)
Group (authorization group)
↓ GroupRole (permissions)
Role (role definition)
↓
RoleModule (module scope — project-level)
Role¶
| Field | Type | Description |
|---|---|---|
roleName |
String | PK — Role name (e.g., "Viewer") |
roleType |
RoleModule (FK) | Module this role belongs to |
roleExp |
String | Role description |
assignable |
Boolean (default true) | Can be assigned to groups |
Table: s_role
RoleModule¶
| Field | Type | Description |
|---|---|---|
roleType |
String | PK — Module name (e.g., "ICOSYS_BPM") |
project |
Project (FK) | Project this module belongs to |
Table: s_role_type
Group¶
| Field | Type | Description |
|---|---|---|
id |
Long | PK |
groupName |
String | Group name |
groupExp |
String | Group description |
allLoggedIn |
Boolean | Applies to all logged-in users |
status |
Boolean | Active flag |
Table: s_group
GroupRole (Group → Role)¶
Composite PK: (groupId, roleName)
GroupUserProcess (User → Group)¶
Composite PK: (groupId, userProcessId)
GroupAccount (Group → Account Scope)¶
Composite PK: (groupId, accountNo)
Türkçe
RBAC zinciri: Kullanıcı → Grup → Rol → Modül şeklinde ilerler.
Gruplar GroupAccount ile hesaplara, GroupProject ile projelere kapsamlandırılabilir.
Frontend'de extractRoles() fonksiyonu bu zinciri çözerek aktif hesaba ait rolleri çıkarır.
Role Name Format¶
Backend (DB): ICOSYS_BPM_BUSINESS_PARTNER_DEF_VIEWER
└─ module ─┘ └── entity def ──┘ └ action ┘
Frontend: ICOSYS_BPM.BusinessPartnerDef.Viewer
└─ module ─┘.└── entity def ──┘.└ action ┘
| Action Suffix | Backend | Frontend |
|---|---|---|
| View | _VIEWER |
.Viewer |
| Create | _CREATOR |
.Creator |
| Edit | _UPDATER |
.Updater |
| Delete | _DELETER |
.Deleter |
| Approve | _APPROVER |
.Approver |
| Manage | _MANAGER |
.Manager |
Authentication Flow¶
Login¶
1. Client → POST /api/auth/login
Body: { userCode, password, languageCode, captchaToken }
│
▼
2. UserAuthController
├── Verify reCAPTCHA v3 token (if enabled)
├── Call UserAuthService.validateUserLogin()
│
├── Super User Mode?
│ └── Yes → Validate against config password
│ Return superUser=true in JWT claims
│
├── DB User:
│ ├── Find UserInfo by encrypted username hash
│ ├── Validate BCrypt password
│ ├── Check user status (active)
│ └── Get/create UserProcess
│
├── Generate JWT token (HMAC-SHA256)
│ Claims: sub, userProcessId, userCode,
│ userDescription, superUser, tokenVersion
│
├── Set JWT as httpOnly cookie
│ (SameSite=Lax, Secure=false in dev)
│
├── Create session log entry
│ (IP, User-Agent, browser, OS, device)
│
└── Return UserAuthResponse
(user info in body, token ONLY in cookie)
Session Restore (/me)¶
1. Client → GET /api/auth/me
Cookie: JWT (automatic)
│
▼
2. Extract JWT from httpOnly cookie
3. Validate token (signature + expiry + tokenVersion)
4. Return user info (no new token)
5. Update session last activity
Account Switch¶
1. Client → POST /api/auth/switch-account
Body: { accountNo }
│
▼
2. Update UserProcessSelect (active account)
3. Reload user groups/roles for new account
4. Return refreshed UserAuthResponse
Password Change¶
1. Client → POST /api/auth/change-password
Body: { currentPassword, newPassword }
│
▼
2. Validate current password (BCrypt)
3. Update UserLogon.userPassword (BCrypt hash)
4. Increment UserProcess.tokenVersion
└── All existing JWTs become invalid
5. Close session log
6. Clear JWT cookie
7. Client must re-login
Logout¶
1. Client → POST /api/auth/logout
│
▼
2. Close session log (logoutType: NORMAL)
3. Clear JWT cookie
4. Return 204 No Content
JWT Token¶
Token Claims¶
.subject(userCode) // sub: username
.claim("userProcessId", id) // User identity ID
.claim("userCode", userCode) // Username
.claim("userDescription", description) // Display name
.claim("superUser", isSuperUser) // Super admin flag
.claim("tokenVersion", tokenVersion) // Invalidation counter
.issuedAt(now)
.expiration(expirationTime) // Default: 8 hours
.signWith(HMAC-SHA256 key)
Token Version Invalidation¶
The tokenVersion mechanism enables immediate invalidation of all user tokens
(e.g., on password change) without a token blacklist:
Issue Token:
1. Read UserProcess.tokenVersion (e.g., 5)
2. Embed tokenVersion=5 in JWT claims
Validate Token:
1. Extract tokenVersion from JWT (e.g., 5)
2. Read current UserProcess.tokenVersion from DB/cache (e.g., 6)
3. If 5 ≠ 6 → token is INVALID (password was changed)
Password Change:
1. Update password hash
2. Increment UserProcess.tokenVersion (5 → 6)
3. All tokens with version < 6 are now invalid
Configuration¶
| Property | Default | Description |
|---|---|---|
jwt.secret.key |
env variable | HMAC-SHA256 secret |
jwt.expiration.ms |
28800000 |
8 hours |
jwt.cookie.same-site |
Lax |
Cookie SameSite policy |
jwt.cookie.secure |
false (dev) / true (prod) |
HTTPS-only cookie |
External Identity (SSO / LDAP)¶
Türkçe
Harici kimlik desteği, SSO/LDAP/OAuth sağlayıcılarından gelen kullanıcıları
UserProcess.identityRef alanı üzerinden sisteme entegre eder.
Bu kullanıcıların UserInfo kaydı yoktur — kimlik bilgileri harici sağlayıcıda saklanır.
Identity Reference Format¶
{TYPE}:{PROVIDER}:{EXTERNAL_ID}
Examples:
SSO:okta:abc123def456
LDAP:corp:cn=ali.yilmaz,ou=users,dc=corp,dc=local
EXT:crm:12345
| Part | Description | Allowed Values |
|---|---|---|
| TYPE | Identity type | SSO, LDAP, EXT |
| PROVIDER | Provider name | Free-text (e.g., "okta", "corp") |
| EXTERNAL_ID | External user ID | Free-text (provider-specific) |
DB User vs External User¶
| Aspect | DB User | External User |
|---|---|---|
UserProcess.userInfo |
Set (FK) | null |
UserProcess.identityRef |
null | Set (e.g., "SSO:okta:abc") |
| Password stored | Yes (UserLogon) | No |
| Login method | Username + password | External provider |
| Profile data | UserInfo entity | UserProcess fields only |
| RBAC | Same (groups/roles) | Same (groups/roles) |
XOR Validation¶
// Enforced in UserProcessService
if (userInfo != null && identityRef != null) {
throw ServiceException("Cannot have both userInfo and identityRef");
}
if (userInfo == null && identityRef == null) {
throw ServiceException("Must have either userInfo or identityRef");
}
Data Encryption¶
Searchable Encryption¶
Sensitive fields use a two-column approach — encrypted value for display, hash for search:
@SearchableEncrypted(hashField = "userNameHash")
@Convert(converter = EncryptedStringConverter.class)
private String userName;
@Column(name = "user_name_hash", length = 64)
private String userNameHash;
| Entity | Encrypted Fields |
|---|---|
| UserInfo | userName, userDescription, companyEmployeeId |
| UserLogon | userPassword, hintQuestion, hintAnswer |
| UserProcess | userCode, userDescription, emailAddress |
Password Hashing¶
- Algorithm: BCrypt (Spring Security
BCryptPasswordEncoder) - Cost factor: 10 (default)
- Password generation: 14-char with uppercase, lowercase, digit, special character
Session Management¶
UserSessionService
├── createSessionLog() → New login entry
├── closeSessionLog() → Mark session closed (NORMAL/FORCED)
├── updateSessionActivity() → Update last activity timestamp
└── SessionCleanupScheduler → Runs every 5 min
└── Timeout inactive sessions (30 min default)
Session log captures:
| Field | Description |
|---|---|
| User ID | UserProcess ID |
| Account No | Active account |
| Session ID | JWT token ID |
| IP Address | X-Forwarded-For aware |
| User-Agent | Raw UA string |
| Browser | Parsed browser name |
| OS | Parsed OS name |
| Device Type | Desktop / Mobile / Tablet |
| Project Code | Active project |
| Language Code | User language |
| Login Time | Session start |
| Logout Time | Session end |
| Logout Type | NORMAL / FORCED / TIMEOUT |
Super User Mode¶
Türkçe
Super User modu yapılandırma tabanlıdır (DB'de saklanmaz).
Bu mod yalnızca geliştirme ve ilk kurulum için kullanılmalıdır.
Super User JWT'de superUser=true claim'i taşır ve tüm yetkilere sahiptir.
1. Username & password from DynamicConfigServiceHelper (environment config)
2. No UserInfo record in DB
3. JWT generated with superUser=true claim
4. Frontend: isSuperAdmin=true → bypasses all RBAC checks
5. Source in response: "SUPERUSER-CONFIG"
REST API¶
Base path: /icglb/services/api
Authentication — /api/auth¶
| Method | Endpoint | Description |
|---|---|---|
| POST | /login |
Login (returns JWT in httpOnly cookie) |
| GET | /me |
Session restore (validate JWT, return user info) |
| POST | /switch-account |
Switch active account |
| POST | /logout |
Logout (clear cookie) |
| POST | /change-password |
Change password (invalidates all tokens) |
UserInfo — /api/user-info¶
| Method | Endpoint | Description |
|---|---|---|
| GET | /{userId} |
Get user profile |
| POST | / |
Create DB user |
| PUT | /{userId} |
Update user profile |
| DELETE | /{userId} |
Soft delete user |
| POST | /list |
Paginated list |
| GET | /stats |
Entity statistics |
UserProcess — /api/user-process¶
Standard CRUD + list + stats endpoints, plus:
| Method | Endpoint | Description |
|---|---|---|
| POST | /link-account |
Link user to account |
| DELETE | /unlink-account |
Unlink user from account |
| PUT | /update-flags |
Update owner/admin flags |
IcmAccount — /api/account¶
Standard CRUD + list + stats endpoints.
Group — /api/group¶
Standard CRUD + list + stats endpoints.
Database Tables¶
| Table | PK | Purpose |
|---|---|---|
s_cr_process |
id (Long) |
Tenant (enterprise) |
s_icm_account |
accountNo (String) |
Customer account |
s_icm_account_user_process |
(accountNo, userProcessId) |
User ↔ account link |
s_icm_account_license2 |
id (Long) |
License / features |
s_user_info |
userId (Long) |
DB user profile |
s_user_logon |
userId (Long, FK) |
Password storage |
s_user_process |
id (Long) |
User identity |
s_user_process_select |
(userProcessId, projectCode, accountNo) |
Active account |
s_group |
groupId (Long) |
Authorization group |
s_group_user_process2 |
(groupId, userProcessId) |
User → group |
s_role |
roleName (String) |
Role definition |
s_role_type |
roleType (String) |
Module / project scope |
s_group_role2 |
(groupId, roleName) |
Group → role |
s_group_account |
(groupId, accountNo) |
Group → account scope |
Configuration¶
Application Properties¶
| Property | Value | Description |
|---|---|---|
server.port |
8010 |
Dev server port |
server.servlet.context-path |
/icglb/services |
Context path |
spring.datasource.url |
jdbc:mysql://localhost:3306/icglb2 |
Database URL |
jwt.secret.key |
${JWT_SECRET:...} |
JWT signing secret |
jwt.expiration.ms |
28800000 |
Token expiry (8 hours) |
session.timeout.minutes |
30 |
Session timeout |
recaptcha.enabled |
false (dev) |
reCAPTCHA v3 |
recaptcha.threshold |
0.5 |
reCAPTCHA score threshold |
Security Endpoints¶
# Fully public (no auth)
ims.security.fully-public-endpoints=/api/health,/actuator/**,/api/auth/login
# Protected (JWT required)
ims.security.protected-endpoints=/**
Rate Limiting¶
| Tier | Requests/min |
|---|---|
| Public endpoints | 60 |
| API-Key endpoints | 100 |
| JWT endpoints | 200 |
Package Structure¶
com.icom.icglb
├── db/
│ ├── crprocess/ → CrProcess entity
│ ├── account/ → IcmAccount, IcmAccountUserProcess, IcmAccountLicense
│ ├── userinfo/ → UserInfo, UserLogon
│ ├── userprocess/ → UserProcess, UserProcessSelect
│ └── userauthorization/ → Group, Role, RoleModule, GroupRole,
│ GroupUserProcess, GroupAccount
├── dto/
│ ├── account/ → IcmAccountEditDto, IcmAccountListDto, etc.
│ ├── userinfo/ → UserInfoEditDto, UserInfoLogonDto, etc.
│ ├── userprocess/ → UserProcessEditDto, UserProcessExtDto, etc.
│ ├── userauth/ → UserAuthRequest, UserAuthResponse, etc.
│ └── userauthorization/ → GroupEditDto, RoleDto, RoleModuleDto, etc.
└── service/
└── zapi/
├── userauth/ → UserAuthController, UserAuthService
├── userinfo/ → UserInfoController, UserInfoService
├── userprocess/ → UserProcessController, UserProcessService
├── account/ → IcmAccountController, IcmAccountService
└── group/ → GroupController, GroupService