Closed sirex closed 8 months ago
O kad kataloge sugeneruot rakta, tam tiktu koks nors mygtukas? Jeigu taip, tai kokioje formoje jis turetu buti? Tarkim tiktu user profile?
Visi raktai saugomi Saugykloje, per Auth clients API. API naudojimo pavyzdžius galima pamatyti čia:
https://github.com/atviriduomenys/spinta/blob/master/notes/access/public.sh
Papildomai Saugyklos raktai saugomi ir Katalogo pusėje, lentelėje ApiKey
, kurioje atsiranda nauji laukai:
organization
- neprivalomas, organizacija, kuriai priskirtas raktasclient_id
- neprivalomas, išorinis Saugyklos client_id
UUID, kuris ateina iš APIclient_name
- neprivalomas, išorinis Saugyklos client_name
Atsiranda nauja lentelė ApiScope
su tokiais laukais:
key: ApiKey
- privalomas, API raktasscope: str
- privalomas, rakto scope, kuris ateina iš Saugyklos API, kiekvienas scope saugomas atskiroje eilutėjeorganization: Organization
- neprivalimas, organizacija, kurios duomenis leidžiama valdytidataset: Dataset
- duomenų rinkinys, kurio duomenis leidžiama valdytienabled: bool
- neprivalomas, gali būti None, nurodo ar leidimas aktyvus ar nescope
formatas aprašytas čia:
https://spinta.readthedocs.io/en/latest/manual/access.html#scopes
Kiekvieną kartą atidarius raktų puslapį, daroma API užklausa:
Ir atnaujinama ApiKey
lentelė įtraukiant naujus raktus, kurie yra Saugykloje, bet kurių nėra Kataloge. ApiKey.organization
nustatomas, pagal Saugyklos client_name
, kuris turėtu sutapti su Organization.name
. Jei nesutampa, tada ApiKey.organization
paliekamas NULL, tačiau išsaugomas ApiKey.client_id
ir ApiKey.client_name
.
Jei Saugykloje nebėra tokio client_id
, o ApiKey
lentelėje yra įrašas su tokiu client_id
, tada tas įrašas ApiKey
lentelėje pažymimas, kaip enabled = false
.
Jei Saugyklos API yra nepasiekiamas, tada sąrašo viršuje rodoma raudona juosta su informacija, kad nepavyko susisiekti su Saugyklos API, todėl raktai rodomi lentelėje gali nesutapti su raktais Saugykloje.
Kiekvieną kartą atidarius rakto formą, daroma API užklausa:
Ir atnaujinama ApiScope
lentelė įtraukiant scope
sąrašą į lentelę.
Pagal scope
pavadinimą nustatomas ApiSope.organization
ir ApiScope.dataset
.
Tarkime, turint scope spinta_datasets_gov_ivpk_getall
, nustatoma ivpk
pavadinimas, kuris sutampa su Organization.name
. Jei organizacijos nustatyti nepavyko, tada ApiScope.organization
nustatom į null.
Tarkime, turint scope spinta_datasets_gov_ivpk_adp_catalog_getall
nustatomas Dataset.name
pavadinimas datasets/gov/ivpk/adp/catalog
, pagal kurį išsaugom ApiScope.dataset
, ne pavadinimo nustatyti nepavyko, tada saugom null.
Reikia atkreipti dėmesį, kad Dataset.name
gali būti datasets/gov/ivpk/adp_catalog
, kurio scope bus spinta_datasets_gov_ivpk_adp_catalog
, todėl reikia, kad veiktų atpažinimas tiek adp_catalog
, tiek adp/catalog
atvejais.
Jei Saugykloje nėra tam tikro scope, o ApiScope
jis yra, tada įrašas iš ApiScope
turi būti pašalinamas.
Gali būti ir tokie scope, kaip spinta_set_meta_fields
, kurie nėra susiję su jokia organizacija ar duomenų rinkiniu, tokiu atveju, nei ApiKey
, nei ApiScope
nėra priskiriamas jokiai organizacijai ar rinkiniui.
Jei ApiScope.organization
ar ApiScope.dataset
jau buvo nurodyti, pakartotinai jų nenustatome. Nustatymą darome tik tuo atveju, jei organization ir dataset yra null.
Jei Saugyklos API yra nepasiekiamas, tada sąrašo viršuje rodoma raudona juosta su informacija, kad nepavyko susisiekti su Saugyklos API, todėl raktai rodomi lentelėje gali nesutapti su raktais Saugykloje.
Kadangi client_name
iš Saugyklos nebūtinai sutaps su Organization.name
, tai tam tikrais atvejais, reikės daryti pataisymus rankiniu būdu. Todėl ApiKey
turėtu būti pasiekiamas per Django admin aplinką.
ApiScope
turėtu taip pat būti pasiekiamas per ApiKey
, kaip InlineModelAdmin.
ApiKey
administravime, reikia tokių filtrų:
Sinchronizuojant raktus iš Saugyklos, svarbu užtikrinti, kad raiktai ir scope būtų priskiriami teisingoms organizacijoms ir duomenų rinkiniams.
Kad tai veiktu, reikia patikrinti ar Organization.name
ir Dataset.name
turi unique constraint, kad neatsitiktų taip, kad vienas raktas, priskirtas kelioms organizacijoms ar keliems rinkiniams.
-- https://data.gov.lt/orgs/269/
Organizacijos kontekste, atsiranda naujas puslapis „Raktai“.
Raktų puslapyje rodomos dvi lentelės su tokiais stulpeliais
Vidiniai raktai yra raktai, kurios sukūrė ir valdo pati organizacija.
client_name
.Paspaudus mygtuką [Keisti], atidaroma vidinio rakto forma.
Paspaudus mygtuką [Pašalinti], atidaromas patvirtinimo puslapis, kuriame klausiama, ar tikrai norima pašalinti. Sutikus, daroma
Užklausa ir raktas pašalinamas iš serverio.
Kitų organizacijų raktai, kurie turi prieigą, prie kontekstinės organizacijos duomenų. Nustatomi pagal ApiScope.organization
, jei bent vienas sutampa su kontekstine organizacija, tada raktas rodomas išorinių raktų sąraše.
Organizacijos raktų puslapyje rodomas mygtukas [Naujas raktas], kurį paspaudus rodoma forma su tokiais laukais:
client_name
. Jei nenurodyta, naudojamas toks pat pavadinimas kaip ir client_id
. Reikia patikrinti, kad client_name
būtų tik lotyniškos, mažosios raidės, _ simbolis ir skaičiai. Kiti simboliai neleidžiami.Išsaugojus formą, jei kuriamas naujas raktas, automatiškai priskiriami tokie scope:
Jei saugoma esamo rakto forma, tada scope neliečiami.
Sukūrus naują raktą, generuojamas ilgas saugus slaptažodis, kuris išsaugojus formą yra parodomas vieną kartą virš leidimų lentelės, su informacija, kad slaptažodis bus rodomas tik vieną kartą.
Rakto peržiūroje rodome:
client_id
client_name
Vidinio rakto atveju, kuri kontekstinė organizacija ir ApiKey.organization sutampa, rodome visus scope.
Išorinio rakto atveju, kur kontekstinė organizacija ir ApiKey.organization nesutampa, rodome tik tuos raktus, kurių ApiScope.organization sutampa su kontekstine organizacija.
Scope lentelėje rodome tokius stulpelius:
ApiScope.dataset.title
su nuoroda į duomenų rinkinį arba ApiScope.organization.title
su nuoroda į Organizaciją, jei ApiScope.dataset is None
. Ir ir ApiScope.organization is None
, tada tiesiog rodome scope pavadinimą atmetus spinta_
prefiksą ir action sufiksą. Jei pašalinus prefiksą ir action sufiksą gaunam empty string, tada rodome "(viskas)"._getone
arba _getall
arba _search
._insert
, _upsert
, _update
, _patch
arba _delete
._wipe
.ApiScope.enabled
būseną, jei enabled = true, rodome [Išjungti], jei enabled = false, rodome [Įjungti]. Jei leidimas išjungtas, tada scope turi būti pašalinti iš Saugyklos ir atvirkščiai, jei leidimas įjungtas, scope turi būti pridėti prie saugyklos.Paspaudus [Keisti] atidaroma Leidimo forma.
Paspaudus [Pašalinti] atidaromas patvirtinimo puslapis ir sutikus su patvirtinimu, pašalinami visi konkretaus dataset ar organizacijos scope.
Leidimų lentelė yra grupuojama pagal objektą. Tarkime, jei turime tokius scope:
Tada lentelė atrodys taip:
Objektas | Skaityti | Rašyti | Valyti | ||
---|---|---|---|---|---|
set_meta_fields | [Išjungti] | [Trinti] | |||
(viskas) | ✅ | [Išjungti] | [Keisti] [Trinti] | ||
Informacinės visuomenės plėtros komitetas | ✅ | [Išjungti] | [Keisti] [Trinti] | ||
COVID-19 pandemijos duomenys | ✅ | ✅ | [Išjungti] | [Keisti] [Trinti] |
Lentelės viršuje rodome mygtuką [Naujas leidimas], kurį paspaudus atidaroma Naujo leidimo forma.
Rakto peržiūros puslapyje rodyti mygtuką [Keisti slaptažodį], kurį paspaudus atidaroma forma, su sugeneruotu nauju slaptažodžiu, tačiau naujas slaptažodis kol kas niekur nesaugomas.
Tik paspaudus mygtuką [Keisti slaptažodį], realiai pakeičiamas slaptažodis tiek Saugykloje, tiek ApiKey lentoje api_key
lauke, užšifruotoje formoje.
Keitimo formoje, turi būti rodomas pranešimas, kad raktas bus parodytas tik vieną kartą, po pakeitimo, nebebus galimybės pamatyti rakto, todėl jis turi būti išsisaugotas.
Leidimo formoje rodome tokius laukus:
scope
- Objektas, privalomas, tekstinis laukas, kuriame įvedama Organization.name, Dataset.name arba arba custom scope name. Leidimas gali būtu su spinta_
prefiksu arba be jo, abu variantai turėtu veikti.Reikia patikrinti ar įvestas scope
yra Organization.name, Dataset.name
arba vienas iš custom scope, kuris yra tik vienas: set_meta_fields
. Jei nei vienas variantas neatitinka, tada mesti klaidą.
Jei pasirinktas custom scope (set_meta_fields
) ir pažymėtas bent vienas checkbox, mesti klaidą, nes custom scopes neturi action.
Saugant formą, reikia sukurti eilę ApiScope
įrašų, kiekvienam pažymėtam action atskirai.
Jei nurodyta kitos organizacijos scope, tada ApiScope
turi būti nustatytas į enabled = false
ir į Saugyklą tokie scope neturi būti siunčiami.
Jei viena organizacija (Prašytojas), prašo prieigos prie kitos organizacijos duomenų (Savininkas), tada Savininkui turi būti išsiųsta užduotis ir el. laiškas, apie prašymą prieiti prie duomenų. Užduotyje turi būti nuoroda į Prašytojo rakto peržiūrą, Savininko organizacijos kontekste, kur Savininkas gali įjungti prašomus leidimus prie duomenų.
See pull request comments: https://github.com/atviriduomenys/katalogas/pull/822
Dėl Saugyklos kliento, siūlau naudoti tokius nustatymus:
SPINTA_SERVER_URL = "https://get-test.data.gov.lt"
- Saugyklos serverio URL.SPINTA_SERVER_CLIENT_ID
- Saugyklos client id.SPINTA_SERVER_CLIENT_SECRET
- Saugyklos client secret.Vietoj dabar naudojamų:
CLIENTS_API_URL = env('CLIENTS_API_URL', default='https://get-test.data.gov.lt/auth/clients/')
CLIENTS_AUTH_BEARER = env('CLIENTS_AUTH_BEARER', default='')
Kadangi prieiga bus reikalinga keliose skirtingose vietose. Todėl tam reikalui siūlau sukurti atskirą funkciją, kuri sukuria requests.Session
ir autorizuoja klientą.
Autorizacijai, access token reikia cache'uoti, patikrinant tokeno expiration date. Likus kokiai valandai iki tokeno galiojimo pabaigos, paimamas naujas tokenas, naudojant client secret.
Dėl kešavimo žiūrėti: https://docs.djangoproject.com/en/3.2/topics/cache/#the-low-level-cache-api
Ir labai svarbu, negalima saugoti jokių secretų kodo repozitorijoje!
Klaida vis dar nepataisyta, žiūrėti https://github.com/atviriduomenys/katalogas/issues/262#issuecomment-1859242174.
Po atnaujinimo, gaunu lygiai tokią pačią klaidą.
Atidarius: https://test.data.gov.lt/orgs/269/apikeys/
Gaunu tokią klaidą:
Traceback (most recent call last):
File "vitrina/orgs/views.py", line 1050, in get_context_data
ApiKey.objects.create(
File "django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "django/db/models/query.py", line 451, in create
obj = self.model(**kwargs)
File "django/db/models/base.py", line 485, in __init__
_setattr(self, field.name, rel_obj)
File "django/db/models/fields/related_descriptors.py", line 215, in __set__
raise ValueError(
ValueError: Cannot assign "<MP_NodeQuerySet [<Organization: Valstybės duomenų agentūra>]>": "ApiKey.organization" must be a "Organization" instance.
Matau, kad ApiKey.organization
bandomas priskirit QuerySet
objektas, vietoj Organization
:
https://github.com/atviriduomenys/katalogas/commit/eef23c0197f15f9795d28f6bfd268b8d03a4e2ca commit metu buvo pridėtas ApiKey.enabled
laukas, kuris neturi default re
Atidarius organizacijos raktų puslapį, pavyzdžiui https://data.gov.lt/orgs/83/apikeys/ visi ApiKey.enabled
pakeičiami į False, dėl to nustoja veikti absoliučiai visi, visų organizacijų raktai.
Dėl to yra sugadinti visi raktų duomenys, kadangi neaišku, kurie raktai buvo aktyvūs, o kurie ne. Reikės galvoti, kaip atstatyti sugadintus duomenis.
Kol kas prašau pataisyti, kad nebūtų išjungiami raktai.
Problema yra šioje vietoje:
Sinchronizuojant Saugyklos raktus į ApiKey
lentelę, reikia atkreipti dėmesį, kad ApiKey
lentelė yra naudojama tiek Saugyklos, tiek Katalogo raktams.
Sinchronizuojant Saugyklos raktus, nereikia liesti Katalogo raktų.
Saugyklos raktus galima atpažinti pagal ApiKey.client_id
, kuris ateina iš Saugyklos. Katalogo atveju ApiKey.client_id
bus NULL
.
Iš Saugyklos gauname tik client_id
ir client_name
, jei jie sutampa, tada siūlau ApiKey
lentelėje nieko nekeisti. Jei ApiKey
lentelėje yra client_id
, o Saugykloje tokio nėra, tada tik tokiu atveju daryti ApiKey.enable = False
, taip mažinant rašymo veiksmų kiekį.
ApiKey
sinchronizavimui, užtenka tik vienos /auth/clients
užklausos.
ApiScope
lentelę siūlau sinchronizuoti, tik vienai organizacijai, kurios raktų sąrašas atidarytas, kitų organizacijų raktų sinchronizuoti nereikia. Aktyvios organizacijos raktai nustatomi pagal ApiKey.organization
ir ApiScope.organization
.
Sinchronizuojant, reikia stengtis mažinti rašymo veiksmus į duomenų bazę ir užklausų skaičių į Saugyklą, atliekant tiek rašymo veiksmų ir HTTP užklausų, kiek yra būtina tos organizacijos raktų sinchronizavimui.
Kai katalogo organizacijos atstovui sugeneruojamas prieigos raktas, toks pat raktas turėtu būti sukuriamas ir Saugykloje, naudojant Saugyklos klientų valdymo API (https://github.com/atviriduomenys/spinta/issues/122).
Kiekvienas klientas turi turėti prieigą tik prie tokių rinkinių, kuriuos turi teisę administruoti.
Prieigą prie Saugyklos gali suteikti tik koordinatorius.
Epic
Priklauso nuo
1091