aaieduhr/aosi-module-libldifsync

AOSI LibLdifSync Module

Maintainers

Package info

gitlab.opencode.hr/srce/aai-eduhr/aosi/aosi-module-libldifsync

Type:aosi-module

pkg:composer/aaieduhr/aosi-module-libldifsync

Statistics

Installs: 9

Dependents: 1

Suggesters: 0

v5.0.0 2026-04-14 11:02 UTC

This package is not auto-updated.

Last update: 2026-04-15 09:16:26 UTC


README

Ovaj modul pruža SOAP metodu za LDIF sinkronizaciju (legacy AOSI v4 stil i novi v5 stil). Funkcionalno replicira ponašanje starog Perl modula LDIFSync.pm.

SOAP endpointi

Modul ne izlaže vlastite SOAP endpoint-e. SOAP server i endpointi dolaze iz aosi-module-soap-server:

  • RPC/Encoded: POST /AOSI
  • Document/Literal: POST /AOSI-DOCUMENT
  • WSDL (RPC): GET /wsdl
  • WSDL (Document): GET /wsdl-document

Metode iz ovog modula registriraju se kroz getSoapServerMethods() i objavljuju u glavnom AOSI WSDL-u zajedno s ostalim SOAP metodama.

Metoda ldapLDIFSync

Parametri:

  • user (uid ili DN)
  • password (base64)
  • base (base DN)
  • type (1-8)
  • offset (opcionalno, bajtovi; koristi se za type=8)
  • chunk_mb (opcionalno, MB; koristi se za type=8)

Značenje type:

  1. info o originalnoj LDIF datoteci
  2. rotacija + čitanje rotirane datoteke
  3. info o rotiranoj datoteci
  4. čitanje rotirane datoteke
  5. info o dump datoteci
  6. dump svih zapisa u LDIF datoteku (default: asinkrono pokretanje)
  7. čitanje dump datoteke
  8. čitanje dump datoteke u chunkovima (offset + chunk_mb)

Putanje LDIF datoteka uvijek se citaju iz aosi-module-libldifmanip/config/libldifmanip.php (outputs['LDIFSync']). Ovaj modul vise nema vlastite sync/dump putanje.

Rotacija LDIF datoteka

Rotaciju (logrotate ili PHP fallback) obavlja servis LdifRotator iz modula aosi-module-libldifmanip. Konfiguracija je u:

  • aosi-module-libldifmanip/config/libldifmanip.phplogrotate

Ako logrotate izvrsna datoteka nije dostupna (npr. macOS), koristi se PHP fallback koji rotira u .1/.2/... i kreira novu praznu datoteku.

Dump LDAP-a

Dump svih zapisa (type=6) radi servis LdifDumper iz modula aosi-module-libldifmanip. Konfiguracija izlaza je u:

  • aosi-module-libldifmanip/config/libldifmanip.phpoutputs['LDIFSync']

Dump koristi isti profil kao i LDIFSync, samo dodaje sufiks _dumpAll na file_prefix. Default datoteka dumpa je data/LDIFSync/LDIFSync_dumpAll_<realm>.ldif i ne prepisuje standardne LDIFSync datoteke.

Za type=6 modul po defaultu pokrece dump asinkrono (CLI u pozadini) i odmah vraca SOAP odgovor, da se izbjegnu HTTP timeouti na velikim imenicima. Konfiguracija:

  • libldifsync.dump.async (true|false, default true)
  • libldifsync.dump.php_binary (opcijski; putanja do izvršne datoteke PHP interpretera za asinkrono pokretanje)
  • libldifsync.dump.async_log_file (naziv log datoteke u app.logs.dir)
  • libldifsync.read.type7_size_guard_enabled (true|false, default true)
  • libldifsync.read.type7_limit_mb (default 50)
  • libldifsync.read.type7_limit_min_mb (default 10)
  • libldifsync.read.type7_limit_max_mb (default 50)
  • libldifsync.read.type8_chunk_mb (default 5)
  • libldifsync.read.type8_chunk_max_mb (default 50)

Potpuni primjer konfiguracije (s komentarima) je u:

  • config/libldifsync.example.php

U asinkronom nacinu:

  • type=6 vraca info o dump datoteci i warning da je dump pokrenut
  • type=5 vraca metadata + dump_status (IN_PROGRESS, READY, NOT_FOUND)
  • type=7 cita cijeli dump tek kada je spreman; dok je dump aktivan vraca kod -25
  • ako je type7_size_guard_enabled=true i dump je veci od limita, type=7 vraca -26 i preporuku za type=8
  • type=8 vraca chunk dumpa i metapodatke (chunk_next_offset, eof, total_size)
  • app.log zapis ldapLDIFSync called. ukljucuje i client_ip
  • app.log zapis LibLdifSync: async dump started ukljucuje base i client_ip
  • async dump log je per-realm datoteka (<ime>_<realm>.log) i na startu dumpa dobiva zaglavlje s: STARTED_AT, CLIENT_IP, REALM, BASE, DUMP_FILE

Preporuceni polling redoslijed:

  1. Pozovi type=6 (pokretanje dumpa).
  2. Zatim pozivaj type=5 dok dump_status ne postane READY.
  3. Ako je dump manji od limita, pozovi type=7 za puni sadrzaj.
  4. Ako type=7 vrati -26, koristi type=8: prvi poziv offset=0, zatim ponavljaj pozive s offset=chunk_next_offset dok eof ne postane 1.

Napomena

SOAP endpointi rade kroz aosi-module-soap-server, a HTTP status (npr. 403) može postaviti sam SOAP handler preko http_response_code(...). SOAP server taj status preslikava u stvarni HTTP odgovor.

Modul ne registrira event listenere (before/after add/modify/delete). Ta funkcionalnost za automatski LDIF zapis je u modulu aosi-module-libldifmanip.

LDAP autentikacija za LDIFSync

Za SOAP metodu ldapLDIFSync dopušten je isključivo korisnik cn=hreduadmin.

  • Parametar user mora biti točno cn=hreduadmin (ignoriraju se velika/mala slova i razmaci).
  • Parametar password mora biti Base64 enkodirana lozinka.
  • Parametar base ulazi u završni DN: cn=hreduadmin,<base>.

To znači:

  • bilo koji drugi korisnik (uid=..., cn=..., plain UID i sl.) je odbijen
  • neispravna lozinka je odbijena
  • u oba slučaja SOAP vraća istu LDAP grešku autentikacije (code=49, poruka Wrong dn or password.), bez otkrivanja detalja.

Provjera je centralizirana u core modulu (aosi-module-core) kroz metodu AaiEduHr\Aosi\Module\Core\Ldap::hrEduAdminBind(...), a libldifsync je samo koristi:

$result = $this->ldap->hrEduAdminBind($user, $password, $base);
if (!$result instanceof \LDAP\Connection) {
    // Greška (npr. code=49) propagira se kroz $this->ldap->err_code()/err_message().
}

Provjera dozvoljene IP adrese

Primarni put provjere koristi metodu: AaiEduHr\Aosi\Authn\ApiAuthnResolver::for($request, $validIps).

libldifsync SOAP handler:

  • dohvaća allowlistu iz libldifsync.valid_ips
  • poziva ApiAuthnResolver::for(...)
  • koristi ApiAuthn::isClientAuthenticated():
    • true ako je klijent autenticiran certifikatom ili dozvoljenom IP adresom
    • false ako nije autentificiran
  • za neautenticiranog klijenta vraća SOAP kod -23, postavlja HTTP status 403 i zapisuje warning log s IP adresom klijenta

Konfiguracija se nalazi u:

  • vendor/aaieduhr/aosi-module-libldifsync/config/libldifsync.php

Pravila za valid_ips:

  • prazna lista ([]) znači zabrani sve
  • inače se radi točno podudaranje IP adrese (===)

Primjer produkcijske konfiguracije:

'valid_ips' => [
    '172.24.240.1',  // primjer: Windows host prema WSL servisu
    '10.10.65.107',  // primjer: interni servisni host
],

Primjer testiranja:

curl -s -o /tmp/soap_response.xml -w "%{http_code}\n" \
  -X POST "https://<host>/aosi/AOSI-DOCUMENT" \
  -H "Content-Type: text/xml; charset=utf-8" \
  -d @soap_payload.xml

Očekivani rezultat:

  • dozvoljena IP: HTTP 200, SOAP code=0
  • nedozvoljena IP: HTTP 403, SOAP code=-23

Ako je aplikacija iza reverznog proxyja, ClientIpResolver koristi forwarding zaglavlja tek kada je proxy dodan u env.trusted_proxies (u config/env.php).

Podrška za više realm-ova

  • Realm se derivira iz base parametra (dc=srce,dc=hr -> srce).
  • Ako je per_realm=true u profilu outputs['LDIFSync'], naziv datoteke dobiva sufiks _<realm>.
  • To omogućuje istovremeni rad s više base DN-ova (npr. dc=srce,dc=hr i dc=intsrce,dc=hr) bez kolizija u nazivima datoteka.

SOAP poziv uvijek radi samo nad jednim realm-om (onim iz base parametra) i vraća samo datoteku tog realma. Ako želite obraditi više realma, potrebno je pozvati metodu više puta s različitim base vrijednostima.

Primjer poziva za više realma (RPC/Encoded):

# realm "srce"
curl -X POST "https://<host>/aosi/AOSI" \
  -H "Content-Type: text/xml; charset=utf-8" \
  -H "SOAPAction: \"urn:AOSI#ldapLDIFSync\"" \
  -d '<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:tns="urn:AOSI">
  <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <tns:ldapLDIFSync>
      <user>cn=hreduadmin</user>
      <password>BASE64</password>
      <base>dc=srce,dc=hr</base>
      <type>1</type>
    </tns:ldapLDIFSync>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>'

# realm "intsrce"
curl -X POST "https://<host>/aosi/AOSI" \
  -H "Content-Type: text/xml; charset=utf-8" \
  -H "SOAPAction: \"urn:AOSI#ldapLDIFSync\"" \
  -d '<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:tns="urn:AOSI">
  <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <tns:ldapLDIFSync>
      <user>cn=hreduadmin</user>
      <password>BASE64</password>
      <base>dc=intsrce,dc=hr</base>
      <type>1</type>
    </tns:ldapLDIFSync>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>'

Ako je env.environment postavljen na production, modul nema korisničko sučelje: nema stavke u admin meniju i ne učitava se /libldifsync stranica. SOAP i WSDL endpointi ostaju aktivni kroz aosi-module-soap-server.

Preduvjeti

Potrebne PHP ekstenzije:

  • ext-ldap

Testovi

Testovi su u tests/Soap/LibLdifSyncSoapHandlerTest.php i pokrivaju:

  • type=1 (info o originalnoj LDIF datoteci)
  • type=2 (rotacija + citanje rotirane datoteke)
  • type=3 (info o rotiranoj datoteci)
  • type=4 (citanje rotirane datoteke)
  • type=5 (info o dump datoteci)
  • type=6 (dump svih zapisa u LDIF datoteku)
  • type=7 (citanje dump datoteke)
  • type=8 (chunk citanje dump datoteke)
  • normalizaciju type=8 parametara (offset i chunk_mb) prema konfiguraciji
  • ograničenje LDAP autentikacije na korisnika cn=hreduadmin
  • uspjesan asinkroni start za type=6 (simulacija background procesa)
  • IP allowlist autentikaciju (allow/deny prema libldifsync.valid_ips)
  • autentikaciju klijenta preko ApiAuthnResolver::for(...) (allow/deny scenariji)
  • LDAP bind greske (propagacija err_code/err_message)

Pokretanje testova:

vendor/bin/phpunit

Zadnje lokalno pokretanje:

  • 24 testa
  • 99 assertiona
  • 0 skipped

Licence

This work is published under European Union Public License (the ‘EUPL’) v1.2.

Autorstvo i alati

Autor: Krešimir Mihalj (kresimir.mihalj@srce.hr).
Kod je razvijen u PHPStormu uz asistenciju Codex‑a.