Autentizace a autorizace
Autentizace (Přihlášení) v systému ABRA Gen Web API je proces ověřování identity uživatele nebo aplikace, která se snaží přistupovat k API. Autentizace se provádí pomocí přihlašovacích údajů uživatele, tj. jeho přihlašovacího jména a hesla či tokenu. Na uživateli musí být také zatržen parametr Přihlášení nevizuálního uživatele API. Autorizace následuje po autentizaci a určuje, co může uživatel nebo systém udělat. Autorizace určuje, jaké akce, zdroje nebo služby jsou dostupné pro autentizovaného uživatele či aplikaci.
Níže se proces autentizace a autorizace v systému ABRA Gen probereme podrobně.

Přihlášení probíhá klíčem Authorization v HTTP hlavičce požadavku metodou Basic Authentication. Argumenty musí být v UTF-8 a musí být zakódovány do base64 (včetně oddělovacích znaků).
Authorization: Basic login:[password|token][;lock_connection]
Argument | Popis argumentu |
---|---|
login |
Přihlašovací jméno. Jedná se standardně o povinný parametr. Parametr je nepovinný pouze tehdy, pokud je zatržen parametr Umožnit rychlé přihlášení přes token. Přihlašovací jméno nesmí obsahovat znak dvojtečky ":". Pokud znak dvojtečky bude obsahovat, vrátí přihlášení do Web API chybu:
Přihlašovací jméno se vždy uvádí s dvojtečkou na konci. Tj. i tehdy, pokud není vyplněno heslo nebo token. |
password | Heslo uživatele, který se přihlašuje. Jedná se o nepovinný parametr. |
token |
Token uživatele, který se přihlašuje. Jedná se o nepovinný parametr. Pokud je zatržen parametr Umožnit rychlé přihlášení přes token není nutné vyplňovat login (Uživatelské jméno). V takovém případě je nutné před token uvést znak dvojtečky. Pokud se login nevyplní, použije se jako výchozí login řetězec ~token~. Přihlášení na uživatele s tímto loginem (resp. Přihlašovacím jménem) pak probíhá přes vyplněný Token. Přihlášení na tohoto uživatele se realizuje jako první. V aplikaci Postman se Token může na záložce Authorization vyplnit do pole Password. |
lock_connection |
Heslo zámku parametru Spojení je uzamčeno v nástroji DBAdmin.exe. Je třeba jej uvést za středník. Jedná se o nepovinný parametr. Heslo, token ani heslo zámku spojení nesmí obsahovat středník ";". Pokud znak středníku bude obsahovat uživatelské heslo nebo token, vrátí přihlášení do Web API chybu:
Pokud znak středníku bude obsahovat heslo zámku, vrátí přihlášení do Web API chybu:
Po nastavení Hesla zámku je nutno Web API restartovat. |
Uživatel je Supervisor, heslo (či token) je 1234. Supervisor:1234 zakódován do base64 je řetězec U3VwZXJ2aXNvcjoxMjM0. V HTTP hlavičce požadavku tedy bude uvedeno:
Authorization: Basic U3VwZXJ2aXNvcjoxMjM0
Uživatel je Supervisor, heslo ani token nejsou vyplněny. Supervisor: zakódován do base64 je řetězec U3VwZXJ2aXNvcjo=. V HTTP hlavičce požadavku tedy bude uvedeno:
Authorization: Basic U3VwZXJ2aXNvcjo=
Uživatel je Supervisor, heslo (či token) je 1234, heslo zámku je 567. Supervisor:1234;567 zakódován do base64 je řetězec U3VwZXJ2aXNvcjoxMjM0OzU2Nw. V HTTP hlavičce požadavku tedy bude uvedeno:
Authorization: Basic U3VwZXJ2aXNvcjoxMjM0OzU2Nw
Uživatel je Supervisor, heslo ani token nejsou vyplněny, heslo zámku je 567. Supervisor:;567 zakódován do base64 je řetězec U3VwZXJ2aXNvcjo7NTY3. V HTTP hlavičce požadavku tedy bude uvedeno:
Authorization: Basic U3VwZXJ2aXNvcjo7NTY3
Uživatel je Supervisor, token je 1234, parametr Umožnit rychlé přihlášení přes token je zatržen. :1234 zakódován do base64 je řetězec OjEyMzQ. V HTTP hlavičce požadavku tedy bude uvedeno:
Authorization: Basic OjEyMzQ
Pro zakódování a dekódování řetězců lze využít různé on-line nástroje typu Base64 decoder/encoder. Aplikace pro odesílání REST API požadavků jako např. Postman dokáží uživatelské jméno a heslo zakódovat i vygenerovat HTTP hlavičku.

Doménové přihlášení je z pohledu API realizováno stejně jako standardní přihlášení, ale je třeba zohlednit obdobná pravidla jako pro přihlášení uživatele přes doménový server.
Podmínky, aby doménové přihlášení fungovalo:
- Aplikační server musí být na serveru (PC), který je v doméně a musí běžet pod doménovým účtem.
- API server může být nainstalován na doménovém serveru, ale i na nedoménovém. Spuštěn může být pod doménovým i nedoménovým, účtem.
- Klient (např. Postman) může být spuštěn kdekoliv, tzn. není vyžadováno PC připojené do domény ani doménový účet.
Autentizace proběhne v těchto případech:
- Dotaz bude obsahovat doménové jméno shodné s přihlašovacím jménem v ABRA Gen a doménové heslo.
-
Dotaz bude obsahovat doménové jméno shodné s přihlašovacím jménem v ABRA Gen a tokenem v ABRA Gen.
V případě přihlášení pomocí domény je při přihlášení do API vyžadováno neprázdné heslo. Důvodem je zamezení přístupu bez ověření v případě, že apiserver služba je spuštěna v doméně pod doménovým účtem.
Pokud dotaz bude obsahovat doménové jméno, bude zatržen parametr Umožnit rychlé přihlášení přes token, bude vyplněn Token, Heslo nebude prázdné a jako Přihlašovací jméno bude uveden řetězec ~token~, pak přihlášení také proběhne a navíc se tento způsob přihlášení realizuje jako první.
Autentizace neproběhne v těchto případech:
-
Dotaz bude obsahovat doménové jméno shodné s přihlašovacím jménem v ABRA Gen, ale nebude obsahovat heslo či token.
Pokud bude klient přihlášen pod doménovým jménem shodným s přihlašovacím jménem v ABRA Gen a současně pod stejným doménovým účtem je spuštěn API server, dojde k přihlášení, i pokud dotaz nebude obsahovat heslo.
- Dotaz bude obsahovat doménové jméno shodné s přihlašovacím jménem v ABRA Gen a heslo v ABRA Gen.

JWT (JSON Web Token) umožňuje bezpečné a efektivní přihlášení uživatelů do Web API systému ABRA Gen. Proces zahrnuje vytvoření přístupového a případně i obnovovacího tokenu, které slouží k autorizaci požadavků na API. Přihlášení pomocí JWT tokenu je třeba nakonfigurovat v agendě Definice JWT a explicitně zatrhnou na uživateli parametr JWT Přihlášení.
Pro generování přihlašovacích JWT na endpointech/currentuser/logintoken a /currentuser/refreshtoken je třeba přidělit uživateli právo Použít příslušnou Definici JWT. Právo lze udělit v agendě Rolenebo Skupiny rolí, na záložce Práva k objektům
Endpointy podporují query parametr jwtsettingaudience, který umožňuje zvolit definici pro generování tokenu dle hodnoty audience. Parametr lze použít, jen pokud je audience unikátní, jinak dotaz skončí chybou. Platí, že pokud je token generován za pomoci jednoho z query parametrů, tak musí existovat právě jedna odpovídající definice, jinak dotaz selže. Pokud se dotaz provádí bez query parametru, systém dohledá definici sám v případě, že v systému existuje pouze jedna definice JWT typu přihlášení do API.


Přihlášení se provádí přes endpoint:
POST http://localhost:81/currentuser/logintoken
Parametry:
- Basic Authentication: Uživatel se přihlásí pomocí jména a hesla (pomocí HTTP Basic Auth).
-
jwtsettingcode (volitelné): Pokud existuje více definic JWT, je nutné specifikovat kód definice:
POST http://localhost:81/demodata/currentuser/logintoken?jwtsettingcode=<kod_definice>
Výsledek:
- Přístupový token (access_token): Používá se pro autorizaci běžných API požadavků.
- Obnovovací token (refresh_token, pokud je povolen): Používá se k získání nových tokenů.

Pro autorizaci požadavků na API se přístupový token přidává do hlavičky Authorization
:
Authorization: Bearer <access_token>
Token je nutné použít před vypršením jeho platnosti (definováno v agendě Definice JWT).

Pokud je povoleno obnovení přihlášení, lze použít endpoint:
POST http://localhost:81/demodata/currentuser/refreshtoken
Podmínky:
- Obnovovací token musí být platný.
- Přístupový token musí být v poslední desetině své životnosti.
Výsledek:
- Nový přístupový a obnovovací token.

Pro účely příkladu je nakonfigurováno použití přihlašovacího i obnovovacího tokenu.
Vygenerování tokenů, použití přihlašovacího a obnovovacího tokenu:
import base64
import json
import sys
import requests
from http_constants.headers import HttpHeaders
session = requests.Session()
headers = {}
headers[HttpHeaders.CONTENT_TYPE] = HttpHeaders.CONTENT_TYPE_VALUES.json
headers[HttpHeaders.AUTHORIZATION] = "Basic " + (
base64.urlsafe_b64encode(("tester" + ":" + "testerp").encode("utf-8"))
).decode("utf-8")
# generate new token
response = session.post(
url="http://localhost:81/demodata/currentuser/logintoken?jwtsettingcode=<kod_definice>",
headers=headers,
)
if response.status_code != 200:
print(f"\n\nerror issuing new token: {response.status_code} {response.text}")
sys.exit(0)
print(f"\n\nnew login and refresh tokens:\n{json.dumps(response.json(), indent=2)}")
access_token = response.json()["access_token"]
refresh_token = response.json()["refresh_token"]
# use login token in authorization as a bearer
headers_token = {}
headers_token[HttpHeaders.CONTENT_TYPE] = HttpHeaders.CONTENT_TYPE_VALUES.json
headers_token[HttpHeaders.AUTHORIZATION] = "Bearer " + access_token
response = session.get(
url="http://localhost:81/demodata/countries?where=code eq 'cz'",
headers=headers_token,
)
print("\n\nuse of login token:")
if response.status_code != 200:
print(f"error: {response.status_code} {response.text}")
sys.exit(0)
print(f"status code: {response.status_code}")
print("response body:")
print(response.text)
# use refresh token to generate new token(if use of refresh tokens is configured)
headers_refresh = {}
headers_refresh[HttpHeaders.CONTENT_TYPE] = HttpHeaders.CONTENT_TYPE_VALUES.json
headers_refresh[HttpHeaders.AUTHORIZATION] = "Bearer " + refresh_token
response = session.post(
url="http://localhost:81/demodata/currentuser/refreshtoken",
headers=headers_refresh,
)
print("\n\nuse of refresh token:")
if response.status_code != 200:
print(f"error: {response.status_code} {response.text}")
sys.exit(0)
print(f"new login and refresh tokens: {json.dumps(response.json(), indent=2)}")
Výstup:
new login and refresh tokens:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJEZW1vdmVyemUiLCJzdWIiOiJ0ZXN0ZXIiLCJhdWQiOiJ0ZXN0IiwiZXhwIjoxNzA4OTU4MzEwLCJwcnAiOiJhdCJ9.dqegGmsotsTvizWflhEgyCetPqy8KmW8rPWXd47XqwQ",
"expires_in": 320,
"refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJEZW1vdmVyemUiLCJzdWIiOiJ0ZXN0ZXIiLCJhdWQiOiJ0ZXN0IiwiaWF0IjoxNzA4OTM5MTEwLCJleHAiOjE3MDg5NTgzMTAsInBycCI6InJ0In0.w9VJ9PIInZlxRpoMIezXcoRHkTLvkOkAJKreiFbKahI",
"refresh_token_expires_in": 10080
}
use of login token:
status code: 200
response body:
[{"alternatecode":"","code":"CZ","currency_id":"0000CZK000","hidden":false,"id":"00000CZ000","name":"Česká republika","numcode":"203","objversion":6}]
use of refresh token:
new login and refresh tokens: {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJEZW1vdmVyemUiLCJzdWIiOiJ0ZXN0ZXIiLCJhdWQiOiJ0ZXN0IiwiZXhwIjoxNzA4OTU4MzE1LCJwcnAiOiJhdCJ9.KasrkJTMKjzAC5RbjvQpcqru6aCXrNwfKBVIevXBDc4",
"expires_in": 320,
"refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJEZW1vdmVyemUiLCJzdWIiOiJ0ZXN0ZXIiLCJhdWQiOiJ0ZXN0IiwiaWF0IjoxNzA4OTM5MTE1LCJleHAiOjE3MDg5NTgzMTUsInBycCI6InJ0In0.C2ts75DOPtVJJX_w3c98ZE8Ji4tHg3KFnAWFkh0CFK4",
"refresh_token_expires_in": 10080
}

Přihlašování do API podporuje normu RFC 4648. V praxi to znamená, že je možné používat URL Safe verzi kódováni Base64 (base64url) v hlavičce Authorization i v Query parametr auth.
Mějme uživatele Supervisor s heslem 12345 a aliasem spojení data. Abra Web API nám poběží na lokálním portu 8082. Potom výběr z kolekce faktur vydaných, kde každá faktura bude obsahovat částku (Amount) a celý objekt firmy (Firm_ID), provedeme s parametrem auth takto.
http://localhost:8082/data/issuedinvoices?select=Amount&expand=Firm_ID&auth=U3VwZXJ2aXNvcjoxMjM0NQ
Upozorňujeme, že, jak bylo řečeno výše, parametru auth je nutno předat hodnotu jméno:heslo (v našem příkladu Supervisor:12345) zakódovanou do base64url. Samotné base64 nestačí!

Přístup k API je umožněn pouze uživatelům systému ABRA Gen, kteří mají ve svém nastavení zatržený příznak Přihlášení nevizuálního uživatele API. Při přístupu k datům jsou zohledňována práva k chráněným objektům a také obecná funkcionalita ochrany dat.
Nejsou zohledňována práva k funkcím, která se týkají pouze práce s uživatelským rozhraním ABRA Gen.

Thread pool v systému ABRA Gen Web API slouží k paralelní obsluze volání, kde maximální počet současně obsluhovaných volání je určen parametrem threadPoolSize. Každé volání musí projít procesem autentizace, který je pro opakované dotazy z téhož zdroje urychlen díky serverové keši.
V reálném provozu se však můžeme setkat s výzvou v podobě nevalidních volání generovaných typicky různými boty, což může ovlivnit celkovou výkonnost API. Pro řešení této situace byl vytvořen tzv. Connection pool.
V kontextu systému se Connection pool využívá pro nepřihlášená pracovní vlákna. Tato implementace působí jako počáteční etapa zpracování, kde probíhá ověření volání předtím, než jsou tato volání předána do hlavního Thread poolu. Tato prvotní fáze zpracování je méně náročná na systémové prostředky, neboť se soustředí především na základní ověření volání. Connection pool je konfigurovatelný pomocí parametrů connectionPoolSize, connectionQueueTimeoutMs a connectionTimeoutMs.
Pokud je skrze Connection pool ověřeno, že se jedná o validní volání (tj. volání od uživatele identifikovaného v databázi, nikoli od bota), je takové volání předáno do hlavního Thread poolu pro další zpracování. Tímto postupem je zajištěno, že výkonnost Web API je optimalizována při současném dodržování nezbytných bezpečnostních opatření.

Kromě ochrany dat ve smyslu obecné funkcionality ochrany dat
Obecně platí:
-
K získávání informací o business objektech s vazbami na chráněné objekty (metodou GET v režimu základního dotazování i metodou POST v režimu rozšířeného dotazování) je zapotřebí právo Zobrazit ke všem chráněným objektům, které jsou na daném objektu použity.
-
K ukládání (metody POST, PUT a DELETE) je zapotřebí právo Použít ke všem chráněným objektům, na které se ukládaný business objekt odkazuje.
Obě práva tedy mají při práci v aplikaci ABRA Gen i v rozhraní Web API stejný význam.
V číselníku vlastních bankovních účtů je nadefinovaný účet 98765432100/5500.
Uživatel Smith je obsazený do role, která má k tomuto účtu právo Zobrazit, ale nemá právo Použít. K datům přistupuje prostřednictvím uživatelského rozhraní aplikace ABRA Gen.
- Uživatel Smith si může zobrazit fakturu vydanou, která má v položce Vlastní účet vyplněný odkaz na bankovní účet 98765432100/5500, protože k tomuto účtu má právo Zobrazit.
- Pokud je na některé faktuře vydané nastavený jiný bankovní účet, uživatel Smith ho v aplikaci ABRA Gen nemůže změnit na 98765432100/5500, protože k tomuto účtu nemá právo Použít.
Uživatel Apu je obsazený do stejné role, ve vlastnostech uživatele má zatržený příznak přihlášení nevizuálního uživatele API, k datům přistupuje prostřednictvím rozhraní Web API.
- Stejně jako uživatel Smith může uživatel Apu získat (zobrazit) informace o faktuře vydané.
- Stejně jako uživatel Smith nemůže uživatel Apu na hlavičkách faktur nastavovat vazbu na účet 98765432100/5500, protože k tomuto účtu nemá právo Použít.
Při aktualizaci business objektů je zapotřebí právo Použít ke všem navázaným chráněným objektům, nejen k těm, jejichž hodnoty se mění. Pro změnu bankovního účtu na hlavičce faktury je tedy zapotřebí nejen právo Použít k příslušnému bankovnímu účtu, ale také například ke všem skladům, které jsou použity na řádcích daného dokladu.
Při práci s doklady tvořenými hlavičkou a řádky platí:
- Je uplatňována stejná logika jako při práci s doklady ve vizuálním prostředí ABRA Gen - tj. pokud doklad obsahuje řádky s odkazy na chráněné objekty, ke kterým uživatel nemá přístup, doklad je možné načíst (včetně řádků), ale není možné jej uložit.
Pro bezpečnostní omezení (security podmínky) platí:
- Pro systémové položky na jednotlivých business objektech jsou brány v úvahu informace zaregistrované v DynSQL - pokud není změna v DynSQL, použije se výchozí (DynSQL)SecurityMask z číselníku. Výchozí hodnota parametru SecurityWithNull je True.
- Parametry definovatelných položek (běžných uživatelských položek i extra položek) jsou načítány zvlášť, bezpečnostní omezení jsou u nich společná pro výběr i pro ukládání.
- Na vlastněné kolekce a na odkazované business objekty nejsou bezpečnostní omezení aplikována.
- Rovněž na hierarchické business objekty nejsou bezpečnostní omezení aplikována. Přesněji nejsou aplikována na referenční položky business objektu, které ukazují na stejný typ business objektu, jako je ten, na kterém jsou definovány. Příkladem je hierarchická struktura středisek - i když je struktura definována (s využitím položky Parent_ID), bezpečnostní omezení je zapotřebí nastavit pro každý záznam (středisko) zvlášť, bez ohledu na hierarchii.