Skip to content

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.

isDbUser()        userInfo != null
isExternalUser()  identityRef != null

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