Диаграммы последовательностей работы коннектора IDM#

Данный документ содержит диаграммы последовательностей (PlantUML), описывающие алгоритмы работы коннектора IDM (Identity Management System).

Тестирование подключения (test)#

@startuml
title Диаграмма последовательности: Тестирование подключения

participant "IDM Framework" as Framework
participant "IdmConnector" as Connector
participant "IdmClient" as Client
participant "HttpClient" as Http
participant "IDM API" as API

Framework -> Connector: test()
activate Connector
Connector -> Client: self()
activate Client

Client -> Http: GET /ws/rest/self\nheaders: Cookie/Authorization
Http -> API: GET /ws/rest/self
API --> Http: 200 OK\n{"user": {...}}
Http --> Client: HttpResponse

alt response == null
    Client --> Connector: null
    Connector --> Framework: ConnectionFailedException
else !response.containsKey("user")
    Client --> Connector: без user
    Connector --> Framework: ConnectionFailedException
else OK
    Client --> Connector: Map<String, Object>
    deactivate Client
    Connector --> Framework: OK
end
deactivate Connector
@enduml

Проверка активности (checkAlive)#

@startuml
title Диаграмма последовательности: Проверка активности (PoolableConnector)

participant "IDM Framework" as Framework
participant "IdmConnector" as Connector
participant "IdmClient" as Client

Framework -> Connector: checkAlive()
activate Connector
Connector -> Client: self()

alt успех
    Client --> Connector: OK
    deactivate Connector
else исключение
    Client --> Connector: Exception
    Connector -> Client: clearSessionCookies()
    Client --> Client: кэш cookies очищен
    deactivate Connector
end
@enduml

Получение схемы (schema)#

@startuml
title Диаграмма последовательности: Получение схемы объектов

participant "IDM Framework" as Framework
participant "IdmConnector" as Connector
participant "UserTypeHandler" as Handler
participant "IdmClient" as Client
participant "SchemaBuilder" as Builder

Framework -> Connector: schema()
activate Connector
Connector -> Connector: new SchemaBuilder()

Connector -> Handler: getObjectClassInfo()
activate Handler

Handler -> Handler: new ObjectClassInfoBuilder()
Handler -> Handler: setType("UserType")

note right: Получение схемы атрибутов\nчерез XML-скрипт
Handler -> Client: executeXmlScript(script)
activate Client
Client -> Client: POST /ws/rest/rpc/executeScript\nContent-Type: application/xml
Client --> Handler: Map{"object": {"dataOutput": {...}}}
deactivate Client

Handler -> Handler: распарсить CSV из скрипта\n(имя;тип;multi;complexType)

loop для каждой строки атрибутов
    Handler -> Handler: создать AttributeInfo
    alt name атрибут
        Handler -> Handler: setName(Name.NAME)
        Handler -> Handler: setRequired(true)
    else multi-valued
        Handler -> Handler: setMultiValued(true)
    else complexType
        Handler -> Handler: setSubtype(STRING_JSON)
    end
end

Handler -> Handler: добавить Uid атрибут
Handler -> Handler: кэшировать AttributeInfo
Handler --> Connector: ObjectClassInfo
deactivate Handler

Connector -> Builder: defineObjectClass(ObjectClassInfo)
Connector -> Builder: defineOperationOption(PageSize)
Connector -> Builder: defineOperationOption(PagedResultsOffset)
Connector -> Builder: build()
Connector --> Framework: Schema
deactivate Connector
@enduml

Создание объекта (create)#

@startuml
title Диаграмма последовательности: Создание объекта

participant "IDM Framework" as Framework
participant "IdmConnector" as Connector
participant "UserTypeHandler" as Handler
participant "IdmClient" as Client
participant "HttpClient" as Http
participant "IDM API" as API

Framework -> Connector: create(ObjectClass, attributes)
activate Connector
Connector -> Handler: create(objectClass, attributes, options)
activate Handler

Handler -> Handler: new userAttributes Map
Handler -> Handler: new userExtAttributes Map

loop для каждого атрибута
    alt имя начинается с "extension."
        Handler -> Handler: добавить в extAttributes\n(без префикса)
    else имя в REQUIRED_ATTRIBUTE_MAPPING
        Handler -> Handler: маппинг имени (Uid->oid, Name->name)
        Handler -> Handler: добавить в userAttributes
    else
        Handler -> Handler: добавить в userAttributes
    end
end

alt userExtAttributes не пуст
    Handler -> Handler: userAttributes.put("extension", extAttributes)
end

Handler -> Client: createUser(userAttributes)
activate Client
Client -> Client: requestBody = {"user": userAttributes}
Client -> Http: POST /ws/rest/users\nJSON body
Http -> API: POST /ws/rest/users
API --> Http: 201 Created\nLocation: /ws/rest/users/{oid}
Http --> Client: HttpResponse

alt statusCode == 201
    Client -> Client: извлечь oid из Location header
    Client --> Handler: Uid(oid)
    deactivate Client
    Handler --> Connector: Uid
    deactivate Handler
    Connector --> Framework: Uid
    deactivate Connector
else statusCode == 409
    Client --> Handler: AlreadyExistsException
    Handler --> Framework: AlreadyExistsException
else
    Client --> Handler: RuntimeException
    Handler --> Framework: RuntimeException
end
deactivate Connector
@enduml

Поиск по UID (executeQuery - EqualsFilter Uid)#

@startuml
title Диаграмма последовательности: Поиск по UID

participant "IDM Framework" as Framework
participant "IdmConnector" as Connector
participant "UserTypeHandler" as Handler
participant "IdmClient" as Client
participant "HttpClient" as Http
participant "IDM API" as API
participant "ResultsHandler" as Results

Framework -> Connector: executeQuery(ObjectClass, EqualsFilter(uid), resultsHandler)
activate Connector
Connector -> Handler: executeQuery(objectClass, filter, resultsHandler, options)
activate Handler

Handler -> Handler: filter instanceof EqualsFilter
Handler -> Handler: attribute instanceof Uid
Handler -> Client: getUser(uid.getUidValue())
activate Client
Client -> Http: GET /ws/rest/users/{oid}\noptions: raw
Http -> API: GET /ws/rest/users/{oid}
API --> Http: 200 OK\n{"user": {...}}
Http --> Client: HttpResponse
Client -> Client: распарсить JSON
Client --> Handler: Map<String, Object>
deactivate Client

alt !userMap.containsKey("user")
    Handler --> Framework: ConnectorException
    deactivate Handler
    deactivate Connector
end

Handler -> Handler: attributes = userMap.get("user")
Handler -> Handler: createConnectorObject(attributes)
Handler -> Results: handle(connectorObject)
Handler --> Framework: завершено
deactivate Handler
deactivate Connector
@enduml

Поиск по NAME (executeQuery - EqualsFilter Name)#

@startuml
title Диаграмма последовательности: Поиск по NAME

participant "IDM Framework" as Framework
participant "IdmConnector" as Connector
participant "UserTypeHandler" as Handler
participant "IdmClient" as Client
participant "HttpClient" as Http
participant "IDM API" as API
participant "ResultsHandler" as Results

Framework -> Connector: executeQuery(ObjectClass, EqualsFilter(name), resultsHandler)
activate Connector
Connector -> Handler: executeQuery(objectClass, filter, resultsHandler, options)
activate Handler

Handler -> Handler: filter instanceof EqualsFilter
Handler -> Handler: attribute instanceof Name
Handler -> Client: searchUserByName(name.getNameValue())
activate Client
Client -> Client: requestBody = {"query": {"filter": {"text": "name = 'username'"}}}
Client -> Http: POST /ws/rest/users/search\nJSON body
Http -> API: POST /ws/rest/users/search
API --> Http: 200 OK\n{"object": {"object": [...]}}
Http --> Client: HttpResponse
Client -> Client: распарсить JSON
Client --> Handler: Map<String, Object>
deactivate Client

Handler -> Handler: users = getValueByPath("object/object")

alt users == null
    Handler --> Framework: завершено
    deactivate Handler
    deactivate Connector
else users.size() > 1
    Handler --> Framework: ConnectorException("Found more than one user")
    deactivate Handler
    deactivate Connector
else
    Handler -> Handler: createConnectorObject(users.get(0))
    Handler -> Results: handle(connectorObject)
    Handler --> Framework: завершено
    deactivate Handler
    deactivate Connector
end
@enduml

Поиск с пагинацией (executeQuery - не EqualsFilter)#

@startuml
title Диаграмма последовательности: Поиск с пагинацией

participant "IDM Framework" as Framework
participant "IdmConnector" as Connector
participant "UserTypeHandler" as Handler
participant "IdmClient" as Client
participant "HttpClient" as Http
participant "IDM API" as API
participant "SearchResultsHandler" as Results

Framework -> Connector: executeQuery(ObjectClass, filter, resultsHandler, options)
activate Connector
Connector -> Handler: executeQuery(objectClass, filter, resultsHandler, options)
activate Handler

Handler -> Handler: offset = options.getPagedResultsOffset() - 1
Handler -> Handler: pageSize = options.getPageSize()
Handler -> Client: searchUsers(offset, pageSize)
activate Client
Client -> Client: requestBody = {"query": {"paging": {"offset": offset, "maxSize": pageSize}}}
Client -> Http: POST /ws/rest/users/search\nJSON body
Http -> API: POST /ws/rest/users/search
API --> Http: 200 OK\n{"object": {"object": [...], "count": N}}
Http --> Client: HttpResponse
Client -> Client: распарсить JSON
Client --> Handler: Map<String, Object>
deactivate Client

Handler -> Handler: users = getValueByPath("object/object")

loop для каждого пользователя
    Handler -> Handler: createConnectorObject(user)
    Handler -> Results: handle(connectorObject)
end

alt resultsHandler instanceof SearchResultsHandler
    Handler -> Handler: count = getValueByPath("object/count")
    Handler -> Handler: remainingPageResults = count - offset - pageSize
    Handler -> Handler: allResultsReturned = remainingPageResults <= 0
    Handler -> Handler: new SearchResult(..., remainingPageResults, allResultsReturned)
    Handler -> Results: handleResult(searchResult)
end

Handler --> Framework: завершено
deactivate Handler
deactivate Connector
@enduml

Обновление объекта (updateDelta)#

@startuml
title Диаграмма последовательности: Обновление объекта (Delta)

participant "IDM Framework" as Framework
participant "IdmConnector" as Connector
participant "UserTypeHandler" as Handler
participant "IdmClient" as Client
participant "HttpClient" as Http
participant "IDM API" as API

Framework -> Connector: updateDelta(ObjectClass, uid, deltas, options)
activate Connector
Connector -> Handler: updateDelta(objectClass, uid, deltas, options)
activate Handler

Handler -> Handler: convertAttributeDeltaToItemDelta(deltas)
activate Handler

loop для каждого AttributeDelta
    Handler -> Handler: path = delta.getName().replace(".", "/")
    
    alt valuesToAdd не пуст
        loop для каждого значения
            Handler -> Handler: itemDeltaList.add({"modificationType": "add", "path": path, "value": value})
        end
    end
    
    alt valuesToReplace не пуст
        loop для каждого значения
            Handler -> Handler: itemDeltaList.add({"modificationType": "replace", "path": path, "value": value})
        end
    end
    
    alt valuesToRemove не пуст
        loop для каждого значения
            Handler -> Handler: itemDeltaList.add({"modificationType": "delete", "path": path, "value": value})
        end
    end
end

Handler --> Handler: List<Map<String, Object>>
deactivate Handler

Handler -> Client: modifyUser(uid.getUidValue(), itemDeltaList)
activate Client
Client -> Client: requestBody = {"objectModification": {"itemDelta": itemDeltaList}}
Client -> Http: PATCH /ws/rest/users/{oid}\nJSON body
Http -> API: PATCH /ws/rest/users/{oid}
API --> Http: 200 OK или 204 No Content
Http --> Client: HttpResponse

alt statusCode == 404
    Client --> Handler: UnknownUidException
    Handler --> Framework: UnknownUidException
    deactivate Handler
    deactivate Connector
else statusCode > 400
    Client --> Handler: RuntimeException
    Handler --> Framework: RuntimeException
    deactivate Handler
    deactivate Connector
else OK
    Client --> Handler: OK
    deactivate Client
    Handler --> Framework: emptySet<AttributeDelta>
    deactivate Handler
    deactivate Connector
end
@enduml

HTTP запросы IdmClient#

@startuml
title Диаграмма последовательности: HTTP запросы IdmClient

participant "IdmClient" as Client
participant "HttpClient" as Http
participant "IDM API" as API

note over Client: self()
Client -> Http: GET /ws/rest/self\nheaders: Cookie/Authorization
Http -> API: GET /ws/rest/self
API --> Http: 200 OK
Http --> Client: HttpResponse
Client -> Client: saveSessionCookies(response)
Client --> Client: Map<String, Object>

note over Client: createUser(attributes)
Client -> Client: body = {"user": attributes}
Client -> Http: POST /ws/rest/users\nJSON body
Http -> API: POST /ws/rest/users
API --> Http: 201 Created\nLocation header
Http --> Client: HttpResponse
Client --> Client: Uid (из Location)

note over Client: getUser(oid)
Client -> Http: GET /ws/rest/users/{oid}\noptions: raw
Http -> API: GET /ws/rest/users/{oid}
API --> Http: 200 OK
Http --> Client: HttpResponse
Client --> Client: Map<String, Object>

note over Client: searchUsers(offset, pageSize)
Client -> Client: body = {"query": {"paging": {...}}}
Client -> Http: POST /ws/rest/users/search
Http -> API: POST /ws/rest/users/search
API --> Http: 200 OK
Http --> Client: HttpResponse
Client --> Client: Map<String, Object>

note over Client: modifyUser(oid, itemDeltaList)
Client -> Client: body = {"objectModification": {"itemDelta": itemDeltaList}}
Client -> Http: PATCH /ws/rest/users/{oid}
Http -> API: PATCH /ws/rest/users/{oid}
API --> Http: 200/204
Http --> Client: HttpResponse
Client --> Client: OK

note over Client: executeXmlScript(xml)
Client -> Http: POST /ws/rest/rpc/executeScript\nXML body
Http -> API: POST /ws/rest/rpc/executeScript
API --> Http: 200 OK
Http --> Client: HttpResponse
Client --> Client: Map<String, Object>
@enduml