diff --git a/.idea/dataSources.local.xml b/.idea/dataSources.local.xml
new file mode 100644
index 0000000..04919cb
--- /dev/null
+++ b/.idea/dataSources.local.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ "
+
+
+ master_key
+ postgres
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 0000000..cf4f20f
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ postgresql
+ true
+ org.postgresql.Driver
+ jdbc:postgresql://localhost:5432/hy_passport
+ $ProjectFileDir$
+
+
+
\ No newline at end of file
diff --git a/.idea/dataSources/74bcf3ef-a2b9-435b-b9e5-f32902a33b25.xml b/.idea/dataSources/74bcf3ef-a2b9-435b-b9e5-f32902a33b25.xml
new file mode 100644
index 0000000..1daf5ad
--- /dev/null
+++ b/.idea/dataSources/74bcf3ef-a2b9-435b-b9e5-f32902a33b25.xml
@@ -0,0 +1,5716 @@
+
+
+
+
+ mdy
+ 1||-9223372036854775808|c|G
+1||10|c|G
+1||10|C|G
+1||10|T|G
+4||-9223372036854775808|c|G
+4||10|c|G
+4||10|C|G
+4||10|T|G
+ 6258
+ 16.2
+ 1713083875
+ true ACDT
+true ACSST
+false ACST
+false ACT
+false ACWST
+true ADT
+true AEDT
+true AESST
+false AEST
+false AFT
+true AKDT
+false AKST
+true ALMST
+false ALMT
+false AMST
+false AMT
+false ANAST
+false ANAT
+false ARST
+false ART
+false AST
+true AWSST
+false AWST
+true AZOST
+false AZOT
+false AZST
+false AZT
+false Africa/Abidjan
+false Africa/Accra
+false Africa/Addis_Ababa
+false Africa/Algiers
+false Africa/Asmara
+false Africa/Asmera
+false Africa/Bamako
+false Africa/Bangui
+false Africa/Banjul
+false Africa/Bissau
+false Africa/Blantyre
+false Africa/Brazzaville
+false Africa/Bujumbura
+false Africa/Cairo
+false Africa/Casablanca
+true Africa/Ceuta
+false Africa/Conakry
+false Africa/Dakar
+false Africa/Dar_es_Salaam
+false Africa/Djibouti
+false Africa/Douala
+false Africa/El_Aaiun
+false Africa/Freetown
+false Africa/Gaborone
+false Africa/Harare
+false Africa/Johannesburg
+false Africa/Juba
+false Africa/Kampala
+false Africa/Khartoum
+false Africa/Kigali
+false Africa/Kinshasa
+false Africa/Lagos
+false Africa/Libreville
+false Africa/Lome
+false Africa/Luanda
+false Africa/Lubumbashi
+false Africa/Lusaka
+false Africa/Malabo
+false Africa/Maputo
+false Africa/Maseru
+false Africa/Mbabane
+false Africa/Mogadishu
+false Africa/Monrovia
+false Africa/Nairobi
+false Africa/Ndjamena
+false Africa/Niamey
+false Africa/Nouakchott
+false Africa/Ouagadougou
+false Africa/Porto-Novo
+false Africa/Sao_Tome
+false Africa/Timbuktu
+false Africa/Tripoli
+false Africa/Tunis
+false Africa/Windhoek
+true America/Adak
+true America/Anchorage
+false America/Anguilla
+false America/Antigua
+false America/Araguaina
+false America/Argentina/Buenos_Aires
+false America/Argentina/Catamarca
+false America/Argentina/ComodRivadavia
+false America/Argentina/Cordoba
+false America/Argentina/Jujuy
+false America/Argentina/La_Rioja
+false America/Argentina/Mendoza
+false America/Argentina/Rio_Gallegos
+false America/Argentina/Salta
+false America/Argentina/San_Juan
+false America/Argentina/San_Luis
+false America/Argentina/Tucuman
+false America/Argentina/Ushuaia
+false America/Aruba
+false America/Asuncion
+false America/Atikokan
+true America/Atka
+false America/Bahia
+false America/Bahia_Banderas
+false America/Barbados
+false America/Belem
+false America/Belize
+false America/Blanc-Sablon
+false America/Boa_Vista
+false America/Bogota
+true America/Boise
+false America/Buenos_Aires
+true America/Cambridge_Bay
+false America/Campo_Grande
+false America/Cancun
+false America/Caracas
+false America/Catamarca
+false America/Cayenne
+false America/Cayman
+true America/Chicago
+false America/Chihuahua
+true America/Ciudad_Juarez
+false America/Coral_Harbour
+false America/Cordoba
+false America/Costa_Rica
+false America/Creston
+false America/Cuiaba
+false America/Curacao
+false America/Danmarkshavn
+false America/Dawson
+false America/Dawson_Creek
+true America/Denver
+true America/Detroit
+false America/Dominica
+true America/Edmonton
+false America/Eirunepe
+false America/El_Salvador
+true America/Ensenada
+false America/Fort_Nelson
+true America/Fort_Wayne
+false America/Fortaleza
+true America/Glace_Bay
+true America/Godthab
+true America/Goose_Bay
+true America/Grand_Turk
+false America/Grenada
+false America/Guadeloupe
+false America/Guatemala
+false America/Guayaquil
+false America/Guyana
+true America/Halifax
+true America/Havana
+false America/Hermosillo
+true America/Indiana/Indianapolis
+true America/Indiana/Knox
+true America/Indiana/Marengo
+true America/Indiana/Petersburg
+true America/Indiana/Tell_City
+true America/Indiana/Vevay
+true America/Indiana/Vincennes
+true America/Indiana/Winamac
+true America/Indianapolis
+true America/Inuvik
+true America/Iqaluit
+false America/Jamaica
+false America/Jujuy
+true America/Juneau
+true America/Kentucky/Louisville
+true America/Kentucky/Monticello
+true America/Knox_IN
+false America/Kralendijk
+false America/La_Paz
+false America/Lima
+true America/Los_Angeles
+true America/Louisville
+false America/Lower_Princes
+false America/Maceio
+false America/Managua
+false America/Manaus
+false America/Marigot
+false America/Martinique
+true America/Matamoros
+false America/Mazatlan
+false America/Mendoza
+true America/Menominee
+false America/Merida
+true America/Metlakatla
+false America/Mexico_City
+true America/Miquelon
+true America/Moncton
+false America/Monterrey
+false America/Montevideo
+true America/Montreal
+false America/Montserrat
+true America/Nassau
+true America/New_York
+true America/Nipigon
+true America/Nome
+false America/Noronha
+true America/North_Dakota/Beulah
+true America/North_Dakota/Center
+true America/North_Dakota/New_Salem
+true America/Nuuk
+true America/Ojinaga
+false America/Panama
+true America/Pangnirtung
+false America/Paramaribo
+false America/Phoenix
+true America/Port-au-Prince
+false America/Port_of_Spain
+false America/Porto_Acre
+false America/Porto_Velho
+false America/Puerto_Rico
+false America/Punta_Arenas
+true America/Rainy_River
+true America/Rankin_Inlet
+false America/Recife
+false America/Regina
+true America/Resolute
+false America/Rio_Branco
+false America/Rosario
+true America/Santa_Isabel
+false America/Santarem
+false America/Santiago
+false America/Santo_Domingo
+false America/Sao_Paulo
+true America/Scoresbysund
+true America/Shiprock
+true America/Sitka
+false America/St_Barthelemy
+true America/St_Johns
+false America/St_Kitts
+false America/St_Lucia
+false America/St_Thomas
+false America/St_Vincent
+false America/Swift_Current
+false America/Tegucigalpa
+true America/Thule
+true America/Thunder_Bay
+true America/Tijuana
+true America/Toronto
+false America/Tortola
+true America/Vancouver
+false America/Virgin
+false America/Whitehorse
+true America/Winnipeg
+true America/Yakutat
+true America/Yellowknife
+false Antarctica/Casey
+false Antarctica/Davis
+false Antarctica/DumontDUrville
+false Antarctica/Macquarie
+false Antarctica/Mawson
+false Antarctica/McMurdo
+false Antarctica/Palmer
+false Antarctica/Rothera
+false Antarctica/South_Pole
+false Antarctica/Syowa
+true Antarctica/Troll
+false Antarctica/Vostok
+true Arctic/Longyearbyen
+false Asia/Aden
+false Asia/Almaty
+false Asia/Amman
+false Asia/Anadyr
+false Asia/Aqtau
+false Asia/Aqtobe
+false Asia/Ashgabat
+false Asia/Ashkhabad
+false Asia/Atyrau
+false Asia/Baghdad
+false Asia/Bahrain
+false Asia/Baku
+false Asia/Bangkok
+false Asia/Barnaul
+true Asia/Beirut
+false Asia/Bishkek
+false Asia/Brunei
+false Asia/Calcutta
+false Asia/Chita
+false Asia/Choibalsan
+false Asia/Chongqing
+false Asia/Chungking
+false Asia/Colombo
+false Asia/Dacca
+false Asia/Damascus
+false Asia/Dhaka
+false Asia/Dili
+false Asia/Dubai
+false Asia/Dushanbe
+true Asia/Famagusta
+true Asia/Gaza
+false Asia/Harbin
+true Asia/Hebron
+false Asia/Ho_Chi_Minh
+false Asia/Hong_Kong
+false Asia/Hovd
+false Asia/Irkutsk
+false Asia/Istanbul
+false Asia/Jakarta
+false Asia/Jayapura
+true Asia/Jerusalem
+false Asia/Kabul
+false Asia/Kamchatka
+false Asia/Karachi
+false Asia/Kashgar
+false Asia/Kathmandu
+false Asia/Katmandu
+false Asia/Khandyga
+false Asia/Kolkata
+false Asia/Krasnoyarsk
+false Asia/Kuala_Lumpur
+false Asia/Kuching
+false Asia/Kuwait
+false Asia/Macao
+false Asia/Macau
+false Asia/Magadan
+false Asia/Makassar
+false Asia/Manila
+false Asia/Muscat
+true Asia/Nicosia
+false Asia/Novokuznetsk
+false Asia/Novosibirsk
+false Asia/Omsk
+false Asia/Oral
+false Asia/Phnom_Penh
+false Asia/Pontianak
+false Asia/Pyongyang
+false Asia/Qatar
+false Asia/Qostanay
+false Asia/Qyzylorda
+false Asia/Rangoon
+false Asia/Riyadh
+false Asia/Saigon
+false Asia/Sakhalin
+false Asia/Samarkand
+false Asia/Seoul
+false Asia/Shanghai
+false Asia/Singapore
+false Asia/Srednekolymsk
+false Asia/Taipei
+false Asia/Tashkent
+false Asia/Tbilisi
+false Asia/Tehran
+true Asia/Tel_Aviv
+false Asia/Thimbu
+false Asia/Thimphu
+false Asia/Tokyo
+false Asia/Tomsk
+false Asia/Ujung_Pandang
+false Asia/Ulaanbaatar
+false Asia/Ulan_Bator
+false Asia/Urumqi
+false Asia/Ust-Nera
+false Asia/Vientiane
+false Asia/Vladivostok
+false Asia/Yakutsk
+false Asia/Yangon
+false Asia/Yekaterinburg
+false Asia/Yerevan
+true Atlantic/Azores
+true Atlantic/Bermuda
+true Atlantic/Canary
+false Atlantic/Cape_Verde
+true Atlantic/Faeroe
+true Atlantic/Faroe
+true Atlantic/Jan_Mayen
+true Atlantic/Madeira
+false Atlantic/Reykjavik
+false Atlantic/South_Georgia
+false Atlantic/St_Helena
+false Atlantic/Stanley
+false Australia/ACT
+false Australia/Adelaide
+false Australia/Brisbane
+false Australia/Broken_Hill
+false Australia/Canberra
+false Australia/Currie
+false Australia/Darwin
+false Australia/Eucla
+false Australia/Hobart
+false Australia/LHI
+false Australia/Lindeman
+false Australia/Lord_Howe
+false Australia/Melbourne
+false Australia/NSW
+false Australia/North
+false Australia/Perth
+false Australia/Queensland
+false Australia/South
+false Australia/Sydney
+false Australia/Tasmania
+false Australia/Victoria
+false Australia/West
+false Australia/Yancowinna
+true BDST
+false BDT
+false BNT
+false BORT
+false BOT
+false BRA
+true BRST
+false BRT
+true BST
+false BTT
+false Brazil/Acre
+false Brazil/DeNoronha
+false Brazil/East
+false Brazil/West
+true CADT
+false CAST
+false CCT
+true CDT
+true CEST
+false CET
+true CETDST
+true CHADT
+false CHAST
+false CHUT
+false CKT
+true CLST
+false CLT
+false COT
+false CST
+true CST6CDT
+false CXT
+true Canada/Atlantic
+true Canada/Central
+true Canada/Eastern
+true Canada/Mountain
+true Canada/Newfoundland
+true Canada/Pacific
+false Canada/Saskatchewan
+false Canada/Yukon
+false Chile/Continental
+false Chile/EasterIsland
+true Cuba
+false DAVT
+false DDUT
+false EASST
+false EAST
+false EAT
+true EDT
+true EEST
+false EET
+true EETDST
+true EGST
+false EGT
+false EST
+true EST5EDT
+false Egypt
+false Eire
+false Etc/GMT
+false Etc/GMT+0
+false Etc/GMT+1
+false Etc/GMT+10
+false Etc/GMT+11
+false Etc/GMT+12
+false Etc/GMT+2
+false Etc/GMT+3
+false Etc/GMT+4
+false Etc/GMT+5
+false Etc/GMT+6
+false Etc/GMT+7
+false Etc/GMT+8
+false Etc/GMT+9
+false Etc/GMT-0
+false Etc/GMT-1
+false Etc/GMT-10
+false Etc/GMT-11
+false Etc/GMT-12
+false Etc/GMT-13
+false Etc/GMT-14
+false Etc/GMT-2
+false Etc/GMT-3
+false Etc/GMT-4
+false Etc/GMT-5
+false Etc/GMT-6
+false Etc/GMT-7
+false Etc/GMT-8
+false Etc/GMT-9
+false Etc/GMT0
+false Etc/Greenwich
+false Etc/UCT
+false Etc/UTC
+false Etc/Universal
+false Etc/Zulu
+true Europe/Amsterdam
+true Europe/Andorra
+false Europe/Astrakhan
+true Europe/Athens
+true Europe/Belfast
+true Europe/Belgrade
+true Europe/Berlin
+true Europe/Bratislava
+true Europe/Brussels
+true Europe/Bucharest
+true Europe/Budapest
+true Europe/Busingen
+true Europe/Chisinau
+true Europe/Copenhagen
+false Europe/Dublin
+true Europe/Gibraltar
+true Europe/Guernsey
+true Europe/Helsinki
+true Europe/Isle_of_Man
+false Europe/Istanbul
+true Europe/Jersey
+false Europe/Kaliningrad
+true Europe/Kiev
+false Europe/Kirov
+true Europe/Kyiv
+true Europe/Lisbon
+true Europe/Ljubljana
+true Europe/London
+true Europe/Luxembourg
+true Europe/Madrid
+true Europe/Malta
+true Europe/Mariehamn
+false Europe/Minsk
+true Europe/Monaco
+false Europe/Moscow
+true Europe/Nicosia
+true Europe/Oslo
+true Europe/Paris
+true Europe/Podgorica
+true Europe/Prague
+true Europe/Riga
+true Europe/Rome
+false Europe/Samara
+true Europe/San_Marino
+true Europe/Sarajevo
+false Europe/Saratov
+false Europe/Simferopol
+true Europe/Skopje
+true Europe/Sofia
+true Europe/Stockholm
+true Europe/Tallinn
+true Europe/Tirane
+true Europe/Tiraspol
+false Europe/Ulyanovsk
+true Europe/Uzhgorod
+true Europe/Vaduz
+true Europe/Vatican
+true Europe/Vienna
+true Europe/Vilnius
+false Europe/Volgograd
+true Europe/Warsaw
+true Europe/Zagreb
+true Europe/Zaporozhye
+true Europe/Zurich
+false FET
+true FJST
+false FJT
+false FKST
+false FKT
+true FNST
+false FNT
+false Factory
+false GALT
+false GAMT
+true GB
+true GB-Eire
+false GEST
+false GET
+false GFT
+false GILT
+false GMT
+false GMT+0
+false GMT-0
+false GMT0
+false GYT
+false Greenwich
+false HKT
+false HST
+false Hongkong
+false ICT
+true IDT
+false IOT
+false IRKST
+false IRKT
+false IRT
+false IST
+false Iceland
+false Indian/Antananarivo
+false Indian/Chagos
+false Indian/Christmas
+false Indian/Cocos
+false Indian/Comoro
+false Indian/Kerguelen
+false Indian/Mahe
+false Indian/Maldives
+false Indian/Mauritius
+false Indian/Mayotte
+false Indian/Reunion
+false Iran
+true Israel
+false JAYT
+false JST
+false Jamaica
+false Japan
+true KDT
+true KGST
+false KGT
+false KOST
+false KRAST
+false KRAT
+false KST
+false Kwajalein
+false LHDT
+false LHST
+false LIGT
+false LINT
+false LKT
+false Libya
+false MAGST
+false MAGT
+false MART
+false MAWT
+true MDT
+true MEST
+true MESZ
+true MET
+true METDST
+false MEZ
+false MHT
+false MMT
+false MPT
+true MSD
+false MSK
+false MST
+true MST7MDT
+true MUST
+false MUT
+false MVT
+false MYT
+true Mexico/BajaNorte
+false Mexico/BajaSur
+false Mexico/General
+true NDT
+false NFT
+false NOVST
+false NOVT
+false NPT
+false NST
+false NUT
+false NZ
+false NZ-CHAT
+true NZDT
+false NZST
+false NZT
+true Navajo
+false OMSST
+false OMST
+true PDT
+false PET
+false PETST
+false PETT
+false PGT
+false PHT
+true PKST
+false PKT
+true PMDT
+false PMST
+false PONT
+false PRC
+false PST
+true PST8PDT
+false PWT
+true PYST
+false PYT
+false Pacific/Apia
+false Pacific/Auckland
+false Pacific/Bougainville
+false Pacific/Chatham
+false Pacific/Chuuk
+false Pacific/Easter
+false Pacific/Efate
+false Pacific/Enderbury
+false Pacific/Fakaofo
+false Pacific/Fiji
+false Pacific/Funafuti
+false Pacific/Galapagos
+false Pacific/Gambier
+false Pacific/Guadalcanal
+false Pacific/Guam
+false Pacific/Honolulu
+false Pacific/Johnston
+false Pacific/Kanton
+false Pacific/Kiritimati
+false Pacific/Kosrae
+false Pacific/Kwajalein
+false Pacific/Majuro
+false Pacific/Marquesas
+false Pacific/Midway
+false Pacific/Nauru
+false Pacific/Niue
+false Pacific/Norfolk
+false Pacific/Noumea
+false Pacific/Pago_Pago
+false Pacific/Palau
+false Pacific/Pitcairn
+false Pacific/Pohnpei
+false Pacific/Ponape
+false Pacific/Port_Moresby
+false Pacific/Rarotonga
+false Pacific/Saipan
+false Pacific/Samoa
+false Pacific/Tahiti
+false Pacific/Tarawa
+false Pacific/Tongatapu
+false Pacific/Truk
+false Pacific/Wake
+false Pacific/Wallis
+false Pacific/Yap
+true Poland
+true Portugal
+false RET
+false ROC
+false ROK
+true SADT
+false SAST
+false SCT
+false SGT
+false Singapore
+false TAHT
+false TFT
+false TJT
+false TKT
+false TMT
+false TOT
+false TRUT
+false TVT
+false Turkey
+false UCT
+true ULAST
+false ULAT
+true US/Alaska
+true US/Aleutian
+false US/Arizona
+true US/Central
+true US/East-Indiana
+true US/Eastern
+false US/Hawaii
+true US/Indiana-Starke
+true US/Michigan
+true US/Mountain
+true US/Pacific
+false US/Samoa
+false UT
+false UTC
+true UYST
+false UYT
+true UZST
+false UZT
+false Universal
+false VET
+false VLAST
+false VLAT
+false VOLT
+false VUT
+false W-SU
+true WADT
+false WAKT
+false WAST
+false WAT
+true WDT
+true WET
+true WETDST
+false WFT
+true WGST
+false WGT
+false XJT
+false YAKST
+false YAKT
+false YAPT
+true YEKST
+false YEKT
+false Z
+false Zulu
+
+
+
+ 1
+ 11||10|C|G
+11||-9223372036854775808|U|G
+11||10|U|G
+2200||6171|C|G
+2200||-9223372036854775808|U|G
+2200||6171|U|G
+13474||10|C|G
+13474||-9223372036854775808|U|G
+13474||10|U|G
+ 6258
+ 37312
+ postgres
+
+
+ default administrative connection database
+ 5
+ littlesheep
+
+
+ 19304
+ postgres
+
+
+ 20328
+ postgres
+
+
+ 37162
+ postgres
+
+
+ 1
+ 1
+ 1
+ 1
+ 10
+ 1
+ 1
+
+
+ 6171
+
+
+ 6181
+
+
+ 6182
+
+
+ 3373
+ 3374
+3375
+3377
+
+
+ 3374
+
+
+ 3375
+
+
+ 3377
+
+
+ 4569
+
+
+ 4570
+
+
+ 4571
+
+
+ 4200
+
+
+ 4544
+
+
+ 4550
+
+
+ 6304
+
+
+ 1
+ 18757
+ 1
+
+
+ 1663
+ 1
+ littlesheep
+
+
+ 1664
+ 1
+ littlesheep
+
+
+ block range index (BRIN) access method
+ 3580
+ 1
+ index
+ 335
+ brinhandler
+ pg_catalog
+
+
+ b-tree index access method
+ 403
+ 1
+ index
+ 330
+ bthandler
+ pg_catalog
+
+
+ GIN index access method
+ 2742
+ 1
+ index
+ 333
+ ginhandler
+ pg_catalog
+
+
+ GiST index access method
+ 783
+ 1
+ index
+ 332
+ gisthandler
+ pg_catalog
+
+
+ hash index access method
+ 405
+ 1
+ index
+ 331
+ hashhandler
+ pg_catalog
+
+
+ heap table access method
+ 2
+ 1
+ table
+ 3
+ heap_tableam_handler
+ pg_catalog
+
+
+ SP-GiST index access method
+ 4000
+ 1
+ index
+ 334
+ spghandler
+ pg_catalog
+
+
+ explicit
+ function
+ 10035
+ 1
+ 2558
+ int4
+ pg_catalog
+ 16
+ bool
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ assignment
+ function
+ 10201
+ 1
+ 2971
+ text
+ pg_catalog
+ 16
+ bool
+ pg_catalog
+ 1042
+ bpchar
+ pg_catalog
+
+
+ assignment
+ function
+ 10191
+ 1
+ 2971
+ text
+ pg_catalog
+ 16
+ bool
+ pg_catalog
+ 25
+ text
+ pg_catalog
+
+
+ assignment
+ function
+ 10196
+ 1
+ 2971
+ text
+ pg_catalog
+ 16
+ bool
+ pg_catalog
+ 1043
+ varchar
+ pg_catalog
+
+
+ explicit
+ function
+ 10143
+ 1
+ 77
+ int4
+ pg_catalog
+ 18
+ char
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ assignment
+ function
+ 10133
+ 1
+ 946
+ text
+ pg_catalog
+ 18
+ char
+ pg_catalog
+ 1043
+ varchar
+ pg_catalog
+
+
+ implicit
+ function
+ 10131
+ 1
+ 946
+ text
+ pg_catalog
+ 18
+ char
+ pg_catalog
+ 25
+ text
+ pg_catalog
+
+
+ assignment
+ function
+ 10132
+ 1
+ 860
+ bpchar
+ pg_catalog
+ 18
+ char
+ pg_catalog
+ 1042
+ bpchar
+ pg_catalog
+
+
+ assignment
+ function
+ 10135
+ 1
+ 408
+ bpchar
+ pg_catalog
+ 19
+ name
+ pg_catalog
+ 1042
+ bpchar
+ pg_catalog
+
+
+ implicit
+ function
+ 10134
+ 1
+ 406
+ text
+ pg_catalog
+ 19
+ name
+ pg_catalog
+ 25
+ text
+ pg_catalog
+
+
+ assignment
+ function
+ 10136
+ 1
+ 1401
+ varchar
+ pg_catalog
+ 19
+ name
+ pg_catalog
+ 1043
+ varchar
+ pg_catalog
+
+
+ implicit
+ function
+ 10090
+ 1
+ 1287
+ oid
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 2206
+ regtype
+ pg_catalog
+
+
+ implicit
+ function
+ 10060
+ 1
+ 1287
+ oid
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 2203
+ regoper
+ pg_catalog
+
+
+ implicit
+ function
+ 10003
+ 1
+ 482
+ float8
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 701
+ float8
+ pg_catalog
+
+
+ implicit
+ function
+ 10069
+ 1
+ 1287
+ oid
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 2204
+ regoperator
+ pg_catalog
+
+
+ assignment
+ function
+ 10001
+ 1
+ 480
+ int4
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ implicit
+ function
+ 10044
+ 1
+ 1287
+ oid
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 24
+ regproc
+ pg_catalog
+
+
+ implicit
+ function
+ 10113
+ 1
+ 1287
+ oid
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 4096
+ regrole
+ pg_catalog
+
+
+ implicit
+ function
+ 10120
+ 1
+ 1287
+ oid
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 4089
+ regnamespace
+ pg_catalog
+
+
+ implicit
+ function
+ 10002
+ 1
+ 652
+ float4
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 700
+ float4
+ pg_catalog
+
+
+ implicit
+ function
+ 10104
+ 1
+ 1287
+ oid
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 3769
+ regdictionary
+ pg_catalog
+
+
+ implicit
+ function
+ 10083
+ 1
+ 1287
+ oid
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 4191
+ regcollation
+ pg_catalog
+
+
+ assignment
+ function
+ 10033
+ 1
+ 3812
+ money
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 790
+ money
+ pg_catalog
+
+
+ implicit
+ function
+ 10037
+ 1
+ 1287
+ oid
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 26
+ oid
+ pg_catalog
+
+
+ implicit
+ function
+ 10097
+ 1
+ 1287
+ oid
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 3734
+ regconfig
+ pg_catalog
+
+
+ assignment
+ function
+ 10000
+ 1
+ 714
+ int2
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+
+
+ explicit
+ function
+ 10185
+ 1
+ 2075
+ bit
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 1560
+ bit
+ pg_catalog
+
+
+ implicit
+ function
+ 10004
+ 1
+ 1781
+ numeric
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 1700
+ numeric
+ pg_catalog
+
+
+ implicit
+ function
+ 10053
+ 1
+ 1287
+ oid
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 2202
+ regprocedure
+ pg_catalog
+
+
+ implicit
+ function
+ 10076
+ 1
+ 1287
+ oid
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+ 2205
+ regclass
+ pg_catalog
+
+
+ implicit
+ function
+ 10045
+ 1
+ 313
+ int4
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 24
+ regproc
+ pg_catalog
+
+
+ implicit
+ function
+ 10091
+ 1
+ 313
+ int4
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 2206
+ regtype
+ pg_catalog
+
+
+ implicit
+ function
+ 10084
+ 1
+ 313
+ int4
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 4191
+ regcollation
+ pg_catalog
+
+
+ implicit
+ function
+ 10070
+ 1
+ 313
+ int4
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 2204
+ regoperator
+ pg_catalog
+
+
+ implicit
+ function
+ 10038
+ 1
+ 313
+ int4
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 26
+ oid
+ pg_catalog
+
+
+ implicit
+ function
+ 10009
+ 1
+ 1782
+ numeric
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 1700
+ numeric
+ pg_catalog
+
+
+ implicit
+ function
+ 10077
+ 1
+ 313
+ int4
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 2205
+ regclass
+ pg_catalog
+
+
+ implicit
+ function
+ 10006
+ 1
+ 313
+ int4
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ implicit
+ function
+ 10054
+ 1
+ 313
+ int4
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 2202
+ regprocedure
+ pg_catalog
+
+
+ implicit
+ function
+ 10007
+ 1
+ 236
+ float4
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 700
+ float4
+ pg_catalog
+
+
+ implicit
+ function
+ 10005
+ 1
+ 754
+ int8
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ implicit
+ function
+ 10114
+ 1
+ 313
+ int4
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 4096
+ regrole
+ pg_catalog
+
+
+ implicit
+ function
+ 10008
+ 1
+ 235
+ float8
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 701
+ float8
+ pg_catalog
+
+
+ implicit
+ function
+ 10105
+ 1
+ 313
+ int4
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 3769
+ regdictionary
+ pg_catalog
+
+
+ implicit
+ function
+ 10121
+ 1
+ 313
+ int4
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 4089
+ regnamespace
+ pg_catalog
+
+
+ implicit
+ function
+ 10061
+ 1
+ 313
+ int4
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 2203
+ regoper
+ pg_catalog
+
+
+ implicit
+ function
+ 10098
+ 1
+ 313
+ int4
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+ 3734
+ regconfig
+ pg_catalog
+
+
+ implicit
+ binary
+ 10078
+ 1
+ 23
+ int4
+ pg_catalog
+ 2205
+ regclass
+ pg_catalog
+
+
+ implicit
+ binary
+ 10085
+ 1
+ 23
+ int4
+ pg_catalog
+ 4191
+ regcollation
+ pg_catalog
+
+
+ implicit
+ binary
+ 10115
+ 1
+ 23
+ int4
+ pg_catalog
+ 4096
+ regrole
+ pg_catalog
+
+
+ explicit
+ function
+ 10144
+ 1
+ 78
+ char
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+ 18
+ char
+ pg_catalog
+
+
+ implicit
+ binary
+ 10122
+ 1
+ 23
+ int4
+ pg_catalog
+ 4089
+ regnamespace
+ pg_catalog
+
+
+ implicit
+ function
+ 10010
+ 1
+ 481
+ int8
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ implicit
+ binary
+ 10106
+ 1
+ 23
+ int4
+ pg_catalog
+ 3769
+ regdictionary
+ pg_catalog
+
+
+ implicit
+ binary
+ 10099
+ 1
+ 23
+ int4
+ pg_catalog
+ 3734
+ regconfig
+ pg_catalog
+
+
+ assignment
+ function
+ 10011
+ 1
+ 314
+ int2
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+
+
+ implicit
+ binary
+ 10092
+ 1
+ 23
+ int4
+ pg_catalog
+ 2206
+ regtype
+ pg_catalog
+
+
+ implicit
+ binary
+ 10071
+ 1
+ 23
+ int4
+ pg_catalog
+ 2204
+ regoperator
+ pg_catalog
+
+
+ implicit
+ binary
+ 10062
+ 1
+ 23
+ int4
+ pg_catalog
+ 2203
+ regoper
+ pg_catalog
+
+
+ implicit
+ binary
+ 10046
+ 1
+ 23
+ int4
+ pg_catalog
+ 24
+ regproc
+ pg_catalog
+
+
+ implicit
+ binary
+ 10055
+ 1
+ 23
+ int4
+ pg_catalog
+ 2202
+ regprocedure
+ pg_catalog
+
+
+ explicit
+ function
+ 10034
+ 1
+ 2557
+ bool
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+ 16
+ bool
+ pg_catalog
+
+
+ implicit
+ function
+ 10014
+ 1
+ 1740
+ numeric
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+ 1700
+ numeric
+ pg_catalog
+
+
+ implicit
+ binary
+ 10039
+ 1
+ 23
+ int4
+ pg_catalog
+ 26
+ oid
+ pg_catalog
+
+
+ explicit
+ function
+ 10186
+ 1
+ 1683
+ bit
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+ 1560
+ bit
+ pg_catalog
+
+
+ implicit
+ function
+ 10012
+ 1
+ 318
+ float4
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+ 700
+ float4
+ pg_catalog
+
+
+ implicit
+ function
+ 10013
+ 1
+ 316
+ float8
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+ 701
+ float8
+ pg_catalog
+
+
+ assignment
+ function
+ 10032
+ 1
+ 3811
+ money
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+ 790
+ money
+ pg_catalog
+
+
+ assignment
+ binary
+ 10048
+ 1
+ 24
+ regproc
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ assignment
+ function
+ 10047
+ 1
+ 1288
+ int8
+ pg_catalog
+ 24
+ regproc
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ implicit
+ binary
+ 10043
+ 1
+ 24
+ regproc
+ pg_catalog
+ 26
+ oid
+ pg_catalog
+
+
+ implicit
+ binary
+ 10049
+ 1
+ 24
+ regproc
+ pg_catalog
+ 2202
+ regprocedure
+ pg_catalog
+
+
+ implicit
+ binary
+ 10125
+ 1
+ 25
+ text
+ pg_catalog
+ 1042
+ bpchar
+ pg_catalog
+
+
+ implicit
+ function
+ 10140
+ 1
+ 407
+ name
+ pg_catalog
+ 25
+ text
+ pg_catalog
+ 19
+ name
+ pg_catalog
+
+
+ assignment
+ function
+ 10137
+ 1
+ 944
+ char
+ pg_catalog
+ 25
+ text
+ pg_catalog
+ 18
+ char
+ pg_catalog
+
+
+ implicit
+ binary
+ 10126
+ 1
+ 25
+ text
+ pg_catalog
+ 1043
+ varchar
+ pg_catalog
+
+
+ explicit
+ function
+ 10193
+ 1
+ 2896
+ xml
+ pg_catalog
+ 25
+ text
+ pg_catalog
+ 142
+ xml
+ pg_catalog
+
+
+ implicit
+ function
+ 10109
+ 1
+ 1079
+ regclass
+ pg_catalog
+ 25
+ text
+ pg_catalog
+ 2205
+ regclass
+ pg_catalog
+
+
+ implicit
+ binary
+ 10074
+ 1
+ 26
+ oid
+ pg_catalog
+ 2205
+ regclass
+ pg_catalog
+
+
+ implicit
+ binary
+ 10051
+ 1
+ 26
+ oid
+ pg_catalog
+ 2202
+ regprocedure
+ pg_catalog
+
+
+ implicit
+ binary
+ 10095
+ 1
+ 26
+ oid
+ pg_catalog
+ 3734
+ regconfig
+ pg_catalog
+
+
+ implicit
+ binary
+ 10058
+ 1
+ 26
+ oid
+ pg_catalog
+ 2203
+ regoper
+ pg_catalog
+
+
+ implicit
+ binary
+ 10081
+ 1
+ 26
+ oid
+ pg_catalog
+ 4191
+ regcollation
+ pg_catalog
+
+
+ implicit
+ binary
+ 10067
+ 1
+ 26
+ oid
+ pg_catalog
+ 2204
+ regoperator
+ pg_catalog
+
+
+ implicit
+ binary
+ 10042
+ 1
+ 26
+ oid
+ pg_catalog
+ 24
+ regproc
+ pg_catalog
+
+
+ assignment
+ function
+ 10040
+ 1
+ 1288
+ int8
+ pg_catalog
+ 26
+ oid
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ implicit
+ binary
+ 10111
+ 1
+ 26
+ oid
+ pg_catalog
+ 4096
+ regrole
+ pg_catalog
+
+
+ implicit
+ binary
+ 10102
+ 1
+ 26
+ oid
+ pg_catalog
+ 3769
+ regdictionary
+ pg_catalog
+
+
+ implicit
+ binary
+ 10088
+ 1
+ 26
+ oid
+ pg_catalog
+ 2206
+ regtype
+ pg_catalog
+
+
+ assignment
+ binary
+ 10041
+ 1
+ 26
+ oid
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ implicit
+ binary
+ 10118
+ 1
+ 26
+ oid
+ pg_catalog
+ 4089
+ regnamespace
+ pg_catalog
+
+
+ assignment
+ io
+ 10214
+ 1
+ 114
+ json
+ pg_catalog
+ 3802
+ jsonb
+ pg_catalog
+
+
+ assignment
+ binary
+ 10202
+ 1
+ 142
+ xml
+ pg_catalog
+ 1042
+ bpchar
+ pg_catalog
+
+
+ assignment
+ binary
+ 10197
+ 1
+ 142
+ xml
+ pg_catalog
+ 1043
+ varchar
+ pg_catalog
+
+
+ assignment
+ binary
+ 10192
+ 1
+ 142
+ xml
+ pg_catalog
+ 25
+ text
+ pg_catalog
+
+
+ implicit
+ binary
+ 10145
+ 1
+ 194
+ pg_node_tree
+ pg_catalog
+ 25
+ text
+ pg_catalog
+
+
+ assignment
+ function
+ 10165
+ 1
+ 4091
+ box
+ pg_catalog
+ 600
+ point
+ pg_catalog
+ 603
+ box
+ pg_catalog
+
+
+ explicit
+ function
+ 10166
+ 1
+ 1532
+ point
+ pg_catalog
+ 601
+ lseg
+ pg_catalog
+ 600
+ point
+ pg_catalog
+
+
+ assignment
+ function
+ 10167
+ 1
+ 1449
+ polygon
+ pg_catalog
+ 602
+ path
+ pg_catalog
+ 604
+ polygon
+ pg_catalog
+
+
+ explicit
+ function
+ 10168
+ 1
+ 1534
+ point
+ pg_catalog
+ 603
+ box
+ pg_catalog
+ 600
+ point
+ pg_catalog
+
+
+ explicit
+ function
+ 10171
+ 1
+ 1479
+ circle
+ pg_catalog
+ 603
+ box
+ pg_catalog
+ 718
+ circle
+ pg_catalog
+
+
+ explicit
+ function
+ 10169
+ 1
+ 1541
+ lseg
+ pg_catalog
+ 603
+ box
+ pg_catalog
+ 601
+ lseg
+ pg_catalog
+
+
+ assignment
+ function
+ 10170
+ 1
+ 1448
+ polygon
+ pg_catalog
+ 603
+ box
+ pg_catalog
+ 604
+ polygon
+ pg_catalog
+
+
+ explicit
+ function
+ 10172
+ 1
+ 1540
+ point
+ pg_catalog
+ 604
+ polygon
+ pg_catalog
+ 600
+ point
+ pg_catalog
+
+
+ explicit
+ function
+ 10175
+ 1
+ 1474
+ circle
+ pg_catalog
+ 604
+ polygon
+ pg_catalog
+ 718
+ circle
+ pg_catalog
+
+
+ explicit
+ function
+ 10174
+ 1
+ 1446
+ box
+ pg_catalog
+ 604
+ polygon
+ pg_catalog
+ 603
+ box
+ pg_catalog
+
+
+ assignment
+ function
+ 10173
+ 1
+ 1447
+ path
+ pg_catalog
+ 604
+ polygon
+ pg_catalog
+ 602
+ path
+ pg_catalog
+
+
+ assignment
+ function
+ 10194
+ 1
+ 730
+ text
+ pg_catalog
+ 650
+ cidr
+ pg_catalog
+ 1043
+ varchar
+ pg_catalog
+
+
+ assignment
+ function
+ 10199
+ 1
+ 730
+ text
+ pg_catalog
+ 650
+ cidr
+ pg_catalog
+ 1042
+ bpchar
+ pg_catalog
+
+
+ assignment
+ function
+ 10189
+ 1
+ 730
+ text
+ pg_catalog
+ 650
+ cidr
+ pg_catalog
+ 25
+ text
+ pg_catalog
+
+
+ implicit
+ binary
+ 10181
+ 1
+ 650
+ cidr
+ pg_catalog
+ 869
+ inet
+ pg_catalog
+
+
+ assignment
+ function
+ 10016
+ 1
+ 238
+ int2
+ pg_catalog
+ 700
+ float4
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+
+
+ assignment
+ function
+ 10015
+ 1
+ 653
+ int8
+ pg_catalog
+ 700
+ float4
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ implicit
+ function
+ 10018
+ 1
+ 311
+ float8
+ pg_catalog
+ 700
+ float4
+ pg_catalog
+ 701
+ float8
+ pg_catalog
+
+
+ assignment
+ function
+ 10019
+ 1
+ 1742
+ numeric
+ pg_catalog
+ 700
+ float4
+ pg_catalog
+ 1700
+ numeric
+ pg_catalog
+
+
+ assignment
+ function
+ 10017
+ 1
+ 319
+ int4
+ pg_catalog
+ 700
+ float4
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ assignment
+ function
+ 10024
+ 1
+ 1743
+ numeric
+ pg_catalog
+ 701
+ float8
+ pg_catalog
+ 1700
+ numeric
+ pg_catalog
+
+
+ assignment
+ function
+ 10020
+ 1
+ 483
+ int8
+ pg_catalog
+ 701
+ float8
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ assignment
+ function
+ 10021
+ 1
+ 237
+ int2
+ pg_catalog
+ 701
+ float8
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+
+
+ assignment
+ function
+ 10022
+ 1
+ 317
+ int4
+ pg_catalog
+ 701
+ float8
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ assignment
+ function
+ 10023
+ 1
+ 312
+ float4
+ pg_catalog
+ 701
+ float8
+ pg_catalog
+ 700
+ float4
+ pg_catalog
+
+
+ explicit
+ function
+ 10178
+ 1
+ 1544
+ polygon
+ pg_catalog
+ 718
+ circle
+ pg_catalog
+ 604
+ polygon
+ pg_catalog
+
+
+ explicit
+ function
+ 10176
+ 1
+ 1416
+ point
+ pg_catalog
+ 718
+ circle
+ pg_catalog
+ 600
+ point
+ pg_catalog
+
+
+ explicit
+ function
+ 10177
+ 1
+ 1480
+ box
+ pg_catalog
+ 718
+ circle
+ pg_catalog
+ 603
+ box
+ pg_catalog
+
+
+ implicit
+ function
+ 10180
+ 1
+ 4124
+ macaddr
+ pg_catalog
+ 774
+ macaddr8
+ pg_catalog
+ 829
+ macaddr
+ pg_catalog
+
+
+ assignment
+ function
+ 10030
+ 1
+ 3823
+ numeric
+ pg_catalog
+ 790
+ money
+ pg_catalog
+ 1700
+ numeric
+ pg_catalog
+
+
+ implicit
+ function
+ 10179
+ 1
+ 4123
+ macaddr8
+ pg_catalog
+ 829
+ macaddr
+ pg_catalog
+ 774
+ macaddr8
+ pg_catalog
+
+
+ assignment
+ function
+ 10195
+ 1
+ 730
+ text
+ pg_catalog
+ 869
+ inet
+ pg_catalog
+ 1043
+ varchar
+ pg_catalog
+
+
+ assignment
+ function
+ 10190
+ 1
+ 730
+ text
+ pg_catalog
+ 869
+ inet
+ pg_catalog
+ 25
+ text
+ pg_catalog
+
+
+ assignment
+ function
+ 10182
+ 1
+ 1715
+ cidr
+ pg_catalog
+ 869
+ inet
+ pg_catalog
+ 650
+ cidr
+ pg_catalog
+
+
+ assignment
+ function
+ 10200
+ 1
+ 730
+ text
+ pg_catalog
+ 869
+ inet
+ pg_catalog
+ 1042
+ bpchar
+ pg_catalog
+
+
+ implicit
+ function
+ 10204
+ 1
+ 668
+ bpchar
+ pg_catalog
+ 1042
+ bpchar
+ pg_catalog
+ 1042
+ bpchar
+ pg_catalog
+
+
+ implicit
+ function
+ 10128
+ 1
+ 401
+ text
+ pg_catalog
+ 1042
+ bpchar
+ pg_catalog
+ 1043
+ varchar
+ pg_catalog
+
+
+ explicit
+ function
+ 10203
+ 1
+ 2896
+ xml
+ pg_catalog
+ 1042
+ bpchar
+ pg_catalog
+ 142
+ xml
+ pg_catalog
+
+
+ implicit
+ function
+ 10127
+ 1
+ 401
+ text
+ pg_catalog
+ 1042
+ bpchar
+ pg_catalog
+ 25
+ text
+ pg_catalog
+
+
+ assignment
+ function
+ 10138
+ 1
+ 944
+ char
+ pg_catalog
+ 1042
+ bpchar
+ pg_catalog
+ 18
+ char
+ pg_catalog
+
+
+ implicit
+ function
+ 10141
+ 1
+ 409
+ name
+ pg_catalog
+ 1042
+ bpchar
+ pg_catalog
+ 19
+ name
+ pg_catalog
+
+
+ implicit
+ binary
+ 10129
+ 1
+ 1043
+ varchar
+ pg_catalog
+ 25
+ text
+ pg_catalog
+
+
+ implicit
+ function
+ 10142
+ 1
+ 1400
+ name
+ pg_catalog
+ 1043
+ varchar
+ pg_catalog
+ 19
+ name
+ pg_catalog
+
+
+ implicit
+ binary
+ 10130
+ 1
+ 1043
+ varchar
+ pg_catalog
+ 1042
+ bpchar
+ pg_catalog
+
+
+ explicit
+ function
+ 10198
+ 1
+ 2896
+ xml
+ pg_catalog
+ 1043
+ varchar
+ pg_catalog
+ 142
+ xml
+ pg_catalog
+
+
+ implicit
+ function
+ 10110
+ 1
+ 1079
+ regclass
+ pg_catalog
+ 1043
+ varchar
+ pg_catalog
+ 2205
+ regclass
+ pg_catalog
+
+
+ implicit
+ function
+ 10205
+ 1
+ 669
+ varchar
+ pg_catalog
+ 1043
+ varchar
+ pg_catalog
+ 1043
+ varchar
+ pg_catalog
+
+
+ assignment
+ function
+ 10139
+ 1
+ 944
+ char
+ pg_catalog
+ 1043
+ varchar
+ pg_catalog
+ 18
+ char
+ pg_catalog
+
+
+ implicit
+ function
+ 10152
+ 1
+ 2024
+ timestamp
+ pg_catalog
+ 1082
+ date
+ pg_catalog
+ 1114
+ timestamp
+ pg_catalog
+
+
+ implicit
+ function
+ 10153
+ 1
+ 1174
+ timestamptz
+ pg_catalog
+ 1082
+ date
+ pg_catalog
+ 1184
+ timestamptz
+ pg_catalog
+
+
+ implicit
+ function
+ 10206
+ 1
+ 1968
+ time
+ pg_catalog
+ 1083
+ time
+ pg_catalog
+ 1083
+ time
+ pg_catalog
+
+
+ implicit
+ function
+ 10155
+ 1
+ 2047
+ timetz
+ pg_catalog
+ 1083
+ time
+ pg_catalog
+ 1266
+ timetz
+ pg_catalog
+
+
+ implicit
+ function
+ 10154
+ 1
+ 1370
+ interval
+ pg_catalog
+ 1083
+ time
+ pg_catalog
+ 1186
+ interval
+ pg_catalog
+
+
+ implicit
+ function
+ 10158
+ 1
+ 2028
+ timestamptz
+ pg_catalog
+ 1114
+ timestamp
+ pg_catalog
+ 1184
+ timestamptz
+ pg_catalog
+
+
+ assignment
+ function
+ 10156
+ 1
+ 2029
+ date
+ pg_catalog
+ 1114
+ timestamp
+ pg_catalog
+ 1082
+ date
+ pg_catalog
+
+
+ assignment
+ function
+ 10157
+ 1
+ 1316
+ time
+ pg_catalog
+ 1114
+ timestamp
+ pg_catalog
+ 1083
+ time
+ pg_catalog
+
+
+ implicit
+ function
+ 10207
+ 1
+ 1961
+ timestamp
+ pg_catalog
+ 1114
+ timestamp
+ pg_catalog
+ 1114
+ timestamp
+ pg_catalog
+
+
+ assignment
+ function
+ 10159
+ 1
+ 1178
+ date
+ pg_catalog
+ 1184
+ timestamptz
+ pg_catalog
+ 1082
+ date
+ pg_catalog
+
+
+ assignment
+ function
+ 10162
+ 1
+ 1388
+ timetz
+ pg_catalog
+ 1184
+ timestamptz
+ pg_catalog
+ 1266
+ timetz
+ pg_catalog
+
+
+ assignment
+ function
+ 10160
+ 1
+ 2019
+ time
+ pg_catalog
+ 1184
+ timestamptz
+ pg_catalog
+ 1083
+ time
+ pg_catalog
+
+
+ assignment
+ function
+ 10161
+ 1
+ 2027
+ timestamp
+ pg_catalog
+ 1184
+ timestamptz
+ pg_catalog
+ 1114
+ timestamp
+ pg_catalog
+
+
+ implicit
+ function
+ 10208
+ 1
+ 1967
+ timestamptz
+ pg_catalog
+ 1184
+ timestamptz
+ pg_catalog
+ 1184
+ timestamptz
+ pg_catalog
+
+
+ implicit
+ function
+ 10209
+ 1
+ 1200
+ interval
+ pg_catalog
+ 1186
+ interval
+ pg_catalog
+ 1186
+ interval
+ pg_catalog
+
+
+ assignment
+ function
+ 10163
+ 1
+ 1419
+ time
+ pg_catalog
+ 1186
+ interval
+ pg_catalog
+ 1083
+ time
+ pg_catalog
+
+
+ assignment
+ function
+ 10164
+ 1
+ 2046
+ time
+ pg_catalog
+ 1266
+ timetz
+ pg_catalog
+ 1083
+ time
+ pg_catalog
+
+
+ implicit
+ function
+ 10210
+ 1
+ 1969
+ timetz
+ pg_catalog
+ 1266
+ timetz
+ pg_catalog
+ 1266
+ timetz
+ pg_catalog
+
+
+ explicit
+ function
+ 10187
+ 1
+ 2076
+ int8
+ pg_catalog
+ 1560
+ bit
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ implicit
+ function
+ 10211
+ 1
+ 1685
+ bit
+ pg_catalog
+ 1560
+ bit
+ pg_catalog
+ 1560
+ bit
+ pg_catalog
+
+
+ implicit
+ binary
+ 10183
+ 1
+ 1560
+ bit
+ pg_catalog
+ 1562
+ varbit
+ pg_catalog
+
+
+ explicit
+ function
+ 10188
+ 1
+ 1684
+ int4
+ pg_catalog
+ 1560
+ bit
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ implicit
+ binary
+ 10184
+ 1
+ 1562
+ varbit
+ pg_catalog
+ 1560
+ bit
+ pg_catalog
+
+
+ implicit
+ function
+ 10212
+ 1
+ 1687
+ varbit
+ pg_catalog
+ 1562
+ varbit
+ pg_catalog
+ 1562
+ varbit
+ pg_catalog
+
+
+ assignment
+ function
+ 10025
+ 1
+ 1779
+ int8
+ pg_catalog
+ 1700
+ numeric
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ assignment
+ function
+ 10026
+ 1
+ 1783
+ int2
+ pg_catalog
+ 1700
+ numeric
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+
+
+ assignment
+ function
+ 10027
+ 1
+ 1744
+ int4
+ pg_catalog
+ 1700
+ numeric
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ implicit
+ function
+ 10213
+ 1
+ 1703
+ numeric
+ pg_catalog
+ 1700
+ numeric
+ pg_catalog
+ 1700
+ numeric
+ pg_catalog
+
+
+ implicit
+ function
+ 10029
+ 1
+ 1746
+ float8
+ pg_catalog
+ 1700
+ numeric
+ pg_catalog
+ 701
+ float8
+ pg_catalog
+
+
+ assignment
+ function
+ 10031
+ 1
+ 3824
+ money
+ pg_catalog
+ 1700
+ numeric
+ pg_catalog
+ 790
+ money
+ pg_catalog
+
+
+ implicit
+ function
+ 10028
+ 1
+ 1745
+ float4
+ pg_catalog
+ 1700
+ numeric
+ pg_catalog
+ 700
+ float4
+ pg_catalog
+
+
+ assignment
+ binary
+ 10057
+ 1
+ 2202
+ regprocedure
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ implicit
+ binary
+ 10052
+ 1
+ 2202
+ regprocedure
+ pg_catalog
+ 26
+ oid
+ pg_catalog
+
+
+ assignment
+ function
+ 10056
+ 1
+ 1288
+ int8
+ pg_catalog
+ 2202
+ regprocedure
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ implicit
+ binary
+ 10050
+ 1
+ 2202
+ regprocedure
+ pg_catalog
+ 24
+ regproc
+ pg_catalog
+
+
+ implicit
+ binary
+ 10065
+ 1
+ 2203
+ regoper
+ pg_catalog
+ 2204
+ regoperator
+ pg_catalog
+
+
+ assignment
+ function
+ 10063
+ 1
+ 1288
+ int8
+ pg_catalog
+ 2203
+ regoper
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ implicit
+ binary
+ 10059
+ 1
+ 2203
+ regoper
+ pg_catalog
+ 26
+ oid
+ pg_catalog
+
+
+ assignment
+ binary
+ 10064
+ 1
+ 2203
+ regoper
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ assignment
+ binary
+ 10073
+ 1
+ 2204
+ regoperator
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ implicit
+ binary
+ 10068
+ 1
+ 2204
+ regoperator
+ pg_catalog
+ 26
+ oid
+ pg_catalog
+
+
+ assignment
+ function
+ 10072
+ 1
+ 1288
+ int8
+ pg_catalog
+ 2204
+ regoperator
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ implicit
+ binary
+ 10066
+ 1
+ 2204
+ regoperator
+ pg_catalog
+ 2203
+ regoper
+ pg_catalog
+
+
+ assignment
+ function
+ 10079
+ 1
+ 1288
+ int8
+ pg_catalog
+ 2205
+ regclass
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ implicit
+ binary
+ 10075
+ 1
+ 2205
+ regclass
+ pg_catalog
+ 26
+ oid
+ pg_catalog
+
+
+ assignment
+ binary
+ 10080
+ 1
+ 2205
+ regclass
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ assignment
+ function
+ 10093
+ 1
+ 1288
+ int8
+ pg_catalog
+ 2206
+ regtype
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ assignment
+ binary
+ 10094
+ 1
+ 2206
+ regtype
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ implicit
+ binary
+ 10089
+ 1
+ 2206
+ regtype
+ pg_catalog
+ 26
+ oid
+ pg_catalog
+
+
+ implicit
+ binary
+ 10146
+ 1
+ 3361
+ pg_ndistinct
+ pg_catalog
+ 17
+ bytea
+ pg_catalog
+
+
+ implicit
+ io
+ 10147
+ 1
+ 3361
+ pg_ndistinct
+ pg_catalog
+ 25
+ text
+ pg_catalog
+
+
+ implicit
+ binary
+ 10148
+ 1
+ 3402
+ pg_dependencies
+ pg_catalog
+ 17
+ bytea
+ pg_catalog
+
+
+ implicit
+ io
+ 10149
+ 1
+ 3402
+ pg_dependencies
+ pg_catalog
+ 25
+ text
+ pg_catalog
+
+
+ implicit
+ binary
+ 10096
+ 1
+ 3734
+ regconfig
+ pg_catalog
+ 26
+ oid
+ pg_catalog
+
+
+ assignment
+ function
+ 10100
+ 1
+ 1288
+ int8
+ pg_catalog
+ 3734
+ regconfig
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ assignment
+ binary
+ 10101
+ 1
+ 3734
+ regconfig
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ implicit
+ binary
+ 10103
+ 1
+ 3769
+ regdictionary
+ pg_catalog
+ 26
+ oid
+ pg_catalog
+
+
+ assignment
+ binary
+ 10108
+ 1
+ 3769
+ regdictionary
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ assignment
+ function
+ 10107
+ 1
+ 1288
+ int8
+ pg_catalog
+ 3769
+ regdictionary
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ assignment
+ io
+ 10215
+ 1
+ 3802
+ jsonb
+ pg_catalog
+ 114
+ json
+ pg_catalog
+
+
+ explicit
+ function
+ 10218
+ 1
+ 3450
+ int2
+ pg_catalog
+ 3802
+ jsonb
+ pg_catalog
+ 21
+ int2
+ pg_catalog
+
+
+ explicit
+ function
+ 10220
+ 1
+ 3452
+ int8
+ pg_catalog
+ 3802
+ jsonb
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ explicit
+ function
+ 10219
+ 1
+ 3451
+ int4
+ pg_catalog
+ 3802
+ jsonb
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ explicit
+ function
+ 10216
+ 1
+ 3556
+ bool
+ pg_catalog
+ 3802
+ jsonb
+ pg_catalog
+ 16
+ bool
+ pg_catalog
+
+
+ explicit
+ function
+ 10221
+ 1
+ 3453
+ float4
+ pg_catalog
+ 3802
+ jsonb
+ pg_catalog
+ 700
+ float4
+ pg_catalog
+
+
+ explicit
+ function
+ 10217
+ 1
+ 3449
+ numeric
+ pg_catalog
+ 3802
+ jsonb
+ pg_catalog
+ 1700
+ numeric
+ pg_catalog
+
+
+ explicit
+ function
+ 10222
+ 1
+ 2580
+ float8
+ pg_catalog
+ 3802
+ jsonb
+ pg_catalog
+ 701
+ float8
+ pg_catalog
+
+
+ explicit
+ function
+ 10223
+ 1
+ 4281
+ int4multirange
+ pg_catalog
+ 3904
+ int4range
+ pg_catalog
+ 4451
+ int4multirange
+ pg_catalog
+
+
+ explicit
+ function
+ 10225
+ 1
+ 4284
+ nummultirange
+ pg_catalog
+ 3906
+ numrange
+ pg_catalog
+ 4532
+ nummultirange
+ pg_catalog
+
+
+ explicit
+ function
+ 10227
+ 1
+ 4287
+ tsmultirange
+ pg_catalog
+ 3908
+ tsrange
+ pg_catalog
+ 4533
+ tsmultirange
+ pg_catalog
+
+
+ explicit
+ function
+ 10228
+ 1
+ 4290
+ tstzmultirange
+ pg_catalog
+ 3910
+ tstzrange
+ pg_catalog
+ 4534
+ tstzmultirange
+ pg_catalog
+
+
+ explicit
+ function
+ 10226
+ 1
+ 4293
+ datemultirange
+ pg_catalog
+ 3912
+ daterange
+ pg_catalog
+ 4535
+ datemultirange
+ pg_catalog
+
+
+ explicit
+ function
+ 10224
+ 1
+ 4296
+ int8multirange
+ pg_catalog
+ 3926
+ int8range
+ pg_catalog
+ 4536
+ int8multirange
+ pg_catalog
+
+
+ assignment
+ binary
+ 10124
+ 1
+ 4089
+ regnamespace
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ assignment
+ function
+ 10123
+ 1
+ 1288
+ int8
+ pg_catalog
+ 4089
+ regnamespace
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ implicit
+ binary
+ 10119
+ 1
+ 4089
+ regnamespace
+ pg_catalog
+ 26
+ oid
+ pg_catalog
+
+
+ assignment
+ binary
+ 10117
+ 1
+ 4096
+ regrole
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ implicit
+ binary
+ 10112
+ 1
+ 4096
+ regrole
+ pg_catalog
+ 26
+ oid
+ pg_catalog
+
+
+ assignment
+ function
+ 10116
+ 1
+ 1288
+ int8
+ pg_catalog
+ 4096
+ regrole
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ assignment
+ function
+ 10086
+ 1
+ 1288
+ int8
+ pg_catalog
+ 4191
+ regcollation
+ pg_catalog
+ 20
+ int8
+ pg_catalog
+
+
+ assignment
+ binary
+ 10087
+ 1
+ 4191
+ regcollation
+ pg_catalog
+ 23
+ int4
+ pg_catalog
+
+
+ implicit
+ binary
+ 10082
+ 1
+ 4191
+ regcollation
+ pg_catalog
+ 26
+ oid
+ pg_catalog
+
+
+ implicit
+ binary
+ 10150
+ 1
+ 5017
+ pg_mcv_list
+ pg_catalog
+ 17
+ bytea
+ pg_catalog
+
+
+ implicit
+ io
+ 10151
+ 1
+ 5017
+ pg_mcv_list
+ pg_catalog
+ 25
+ text
+ pg_catalog
+
+
+ explicit
+ function
+ 10036
+ 1
+ 5071
+ xid
+ pg_catalog
+ 5069
+ xid8
+ pg_catalog
+ 28
+ xid
+ pg_catalog
+
+
+ PL/pgSQL procedural language
+ 13826
+ 683
+ 1.0
+ 11
+ pg_catalog
+ 13827
+13828
+13829
+13830
+
+
+ dynamically-loaded C functions
+ 13
+ 1
+ fmgr_c_validator
+ pg_catalog
+
+
+ built-in functions
+ 12
+ 1
+ fmgr_internal_validator
+ pg_catalog
+
+
+ PL/pgSQL procedural language
+ plpgsql_call_handler
+ pg_catalog
+ plpgsql_inline_handler
+ pg_catalog
+ 13830
+ 683
+ 1
+ plpgsql_validator
+ pg_catalog
+
+
+ SQL-language functions
+ 14
+ 1
+ 1
+ fmgr_sql_validator
+ pg_catalog
+
+
+ 13474
+ 529
+ littlesheep
+
+
+ system catalog schema
+ 11
+ 523
+ littlesheep
+
+
+ standard public schema
+ 1
+ 6258
+ 2024-04-20.06:09:48
+ 2200
+ 523
+ pg_database_owner
+
+
+ bigint|0s
+ 37369
+ 1
+ 1
+ 6233
+ littlesheep
+
+
+ bigint|0s
+ 37385
+ 1
+ 1
+ 6236
+ littlesheep
+
+
+ bigint|0s
+ 37354
+ 1
+ 1
+ 6231
+ littlesheep
+
+
+ bigint|0s
+ 37339
+ 1
+ 1
+ 6229
+ littlesheep
+
+
+ bigint|0s
+ 37313
+ 1
+ 1
+ 6224
+ littlesheep
+
+
+ bigint|0s
+ 37474
+ 1
+ 1
+ 6247
+ littlesheep
+
+
+ bigint|0s
+ 37439
+ 1
+ 1
+ 6243
+ littlesheep
+
+
+ bigint|0s
+ 37324
+ 1
+ 1
+ 6227
+ littlesheep
+
+
+ bigint|0s
+ 37419
+ 1
+ 1
+ 6241
+ littlesheep
+
+
+ bigint|0s
+ 37459
+ 1
+ 1
+ 6245
+ littlesheep
+
+
+ bigint|0s
+ 37509
+ 1
+ 1
+ 6251
+ littlesheep
+
+
+ bigint|0s
+ 37489
+ 1
+ 1
+ 6249
+ littlesheep
+
+
+ bigint|0s
+ 37403
+ 1
+ 1
+ 6238
+ littlesheep
+
+
+ 37370
+ 6233
+ 2
+ littlesheep
+
+
+ 37386
+ 6236
+ 2
+ littlesheep
+
+
+ 37355
+ 6231
+ 2
+ littlesheep
+
+
+ 37340
+ 6229
+ 2
+ littlesheep
+
+
+ 37314
+ 6227
+ 2
+ littlesheep
+
+
+ 37475
+ 6247
+ 2
+ littlesheep
+
+
+ 37440
+ 6243
+ 2
+ littlesheep
+
+
+ 37325
+ 6227
+ 2
+ littlesheep
+
+
+ 37420
+ 6241
+ 2
+ littlesheep
+
+
+ 37460
+ 6245
+ 2
+ littlesheep
+
+
+ 37510
+ 6251
+ 2
+ littlesheep
+
+
+ 37490
+ 6249
+ 2
+ littlesheep
+
+
+ 37404
+ 6238
+ 2
+ littlesheep
+
+
+ bigint|0s
+ nextval('passport_account_contacts_id_seq'::regclass)
+ 1
+ 1
+ 6233
+ 37369
+ 20
+
+
+ timestamp with time zone|0s
+ 2
+ 6233
+ 1184
+
+
+ timestamp with time zone|0s
+ 3
+ 6233
+ 1184
+
+
+ timestamp with time zone|0s
+ 4
+ 6233
+ 1184
+
+
+ smallint|0s
+ 5
+ 6233
+ 21
+
+
+ text|0s
+ 6
+ 6233
+ 25
+
+
+ boolean|0s
+ 7
+ 6233
+ 16
+
+
+ boolean|0s
+ 8
+ 6233
+ 16
+
+
+ timestamp with time zone|0s
+ 9
+ 6233
+ 1184
+
+
+ bigint|0s
+ 10
+ 6233
+ 20
+
+
+ account_id
+ 37378
+ 6233
+ 1
+ 37314
+
+
+ id
+ 1
+ 37376
+ 1
+ 6233
+ 1
+ 403
+
+
+ content
+ 37383
+ 6234
+ 1
+ 403
+ default
+ 100
+ pg_catalog
+
+
+ deleted_at
+ 37384
+ 6235
+ 403
+
+
+ 1
+ 37377
+ 1
+ 6233
+ 37376
+
+
+ bigint|0s
+ nextval('passport_account_friendships_id_seq'::regclass)
+ 1
+ 1
+ 6236
+ 37385
+ 20
+
+
+ timestamp with time zone|0s
+ 2
+ 6236
+ 1184
+
+
+ timestamp with time zone|0s
+ 3
+ 6236
+ 1184
+
+
+ timestamp with time zone|0s
+ 4
+ 6236
+ 1184
+
+
+ bigint|0s
+ 5
+ 6236
+ 20
+
+
+ bigint|0s
+ 6
+ 6236
+ 20
+
+
+ bigint|0s
+ 7
+ 6236
+ 20
+
+
+ smallint|0s
+ 8
+ 6236
+ 21
+
+
+ account_id
+ 37392
+ 6236
+ 1
+ 37314
+
+
+ related_id
+ 37397
+ 6236
+ 1
+ 37314
+
+
+ id
+ 1
+ 37390
+ 1
+ 6236
+ 1
+ 403
+
+
+ deleted_at
+ 37402
+ 6237
+ 403
+
+
+ 1
+ 37391
+ 1
+ 6236
+ 37390
+
+
+ bigint|0s
+ nextval('passport_account_pages_id_seq'::regclass)
+ 1
+ 1
+ 6231
+ 37354
+ 20
+
+
+ timestamp with time zone|0s
+ 2
+ 6231
+ 1184
+
+
+ timestamp with time zone|0s
+ 3
+ 6231
+ 1184
+
+
+ timestamp with time zone|0s
+ 4
+ 6231
+ 1184
+
+
+ text|0s
+ 5
+ 6231
+ 25
+
+
+ text|0s
+ 6
+ 6231
+ 25
+
+
+ text|0s
+ 7
+ 6231
+ 25
+
+
+ jsonb|0s
+ 8
+ 6231
+ 3802
+
+
+ bigint|0s
+ 9
+ 6231
+ 20
+
+
+ account_id
+ 37363
+ 6231
+ 1
+ 37314
+
+
+ id
+ 1
+ 37361
+ 1
+ 6231
+ 1
+ 403
+
+
+ deleted_at
+ 37368
+ 6232
+ 403
+
+
+ 1
+ 37362
+ 1
+ 6231
+ 37361
+
+
+ bigint|0s
+ nextval('passport_account_profiles_id_seq'::regclass)
+ 1
+ 1
+ 6229
+ 37339
+ 20
+
+
+ timestamp with time zone|0s
+ 2
+ 6229
+ 1184
+
+
+ timestamp with time zone|0s
+ 3
+ 6229
+ 1184
+
+
+ timestamp with time zone|0s
+ 4
+ 6229
+ 1184
+
+
+ text|0s
+ 5
+ 6229
+ 25
+
+
+ text|0s
+ 6
+ 6229
+ 25
+
+
+ bigint|0s
+ 7
+ 6229
+ 20
+
+
+ timestamp with time zone|0s
+ 8
+ 6229
+ 1184
+
+
+ bigint|0s
+ 9
+ 6229
+ 20
+
+
+ account_id
+ 37348
+ 6229
+ 1
+ 37314
+
+
+ id
+ 1
+ 37346
+ 1
+ 6229
+ 1
+ 403
+
+
+ deleted_at
+ 37353
+ 6230
+ 403
+
+
+ 1
+ 37347
+ 1
+ 6229
+ 37346
+
+
+ bigint|0s
+ nextval('passport_accounts_id_seq'::regclass)
+ 1
+ 1
+ 6224
+ 37313
+ 20
+
+
+ timestamp with time zone|0s
+ 2
+ 6224
+ 1184
+
+
+ timestamp with time zone|0s
+ 3
+ 6224
+ 1184
+
+
+ timestamp with time zone|0s
+ 4
+ 6224
+ 1184
+
+
+ text|0s
+ 5
+ 6224
+ 25
+
+
+ text|0s
+ 6
+ 6224
+ 25
+
+
+ text|0s
+ 7
+ 6224
+ 25
+
+
+ text|0s
+ 8
+ 6224
+ 25
+
+
+ text|0s
+ 9
+ 6224
+ 25
+
+
+ timestamp with time zone|0s
+ 10
+ 6224
+ 1184
+
+
+ bigint|0s
+ 11
+ 6224
+ 20
+
+
+ id
+ 1
+ 37320
+ 1
+ 6224
+ 1
+ 403
+
+
+ name
+ 37322
+ 6225
+ 1
+ 403
+ default
+ 100
+ pg_catalog
+
+
+ deleted_at
+ 37323
+ 6226
+ 403
+
+
+ 1
+ 37321
+ 1
+ 6224
+ 37320
+
+
+ bigint|0s
+ nextval('passport_action_events_id_seq'::regclass)
+ 1
+ 1
+ 6247
+ 37474
+ 20
+
+
+ timestamp with time zone|0s
+ 2
+ 6247
+ 1184
+
+
+ timestamp with time zone|0s
+ 3
+ 6247
+ 1184
+
+
+ timestamp with time zone|0s
+ 4
+ 6247
+ 1184
+
+
+ text|0s
+ 5
+ 6247
+ 25
+
+
+ text|0s
+ 6
+ 6247
+ 25
+
+
+ text|0s
+ 7
+ 6247
+ 25
+
+
+ text|0s
+ 8
+ 6247
+ 25
+
+
+ text|0s
+ 9
+ 6247
+ 25
+
+
+ bigint|0s
+ 10
+ 6247
+ 20
+
+
+ account_id
+ 37483
+ 6247
+ 1
+ 37314
+
+
+ id
+ 1
+ 37481
+ 1
+ 6247
+ 1
+ 403
+
+
+ deleted_at
+ 37488
+ 6248
+ 403
+
+
+ 1
+ 37482
+ 1
+ 6247
+ 37481
+
+
+ bigint|0s
+ nextval('passport_auth_challenges_id_seq'::regclass)
+ 1
+ 1
+ 6243
+ 37439
+ 20
+
+
+ timestamp with time zone|0s
+ 2
+ 6243
+ 1184
+
+
+ timestamp with time zone|0s
+ 3
+ 6243
+ 1184
+
+
+ timestamp with time zone|0s
+ 4
+ 6243
+ 1184
+
+
+ text|0s
+ 5
+ 6243
+ 25
+
+
+ text|0s
+ 6
+ 6243
+ 25
+
+
+ text|0s
+ 7
+ 6243
+ 25
+
+
+ bigint|0s
+ 8
+ 6243
+ 20
+
+
+ bigint|0s
+ 9
+ 6243
+ 20
+
+
+ bigint|0s
+ 10
+ 6243
+ 20
+
+
+ jsonb|0s
+ 11
+ 6243
+ 3802
+
+
+ smallint|0s
+ 12
+ 6243
+ 21
+
+
+ timestamp with time zone|0s
+ 13
+ 6243
+ 1184
+
+
+ bigint|0s
+ 14
+ 6243
+ 20
+
+
+ bigint|0s
+ 15
+ 6243
+ 20
+
+
+ session_id
+ 37448
+ 6243
+ 1
+ 37420
+
+
+ account_id
+ 37453
+ 6243
+ 1
+ 37314
+
+
+ id
+ 1
+ 37446
+ 1
+ 6243
+ 1
+ 403
+
+
+ deleted_at
+ 37458
+ 6244
+ 403
+
+
+ 1
+ 37447
+ 1
+ 6243
+ 37446
+
+
+ bigint|0s
+ nextval('passport_auth_factors_id_seq'::regclass)
+ 1
+ 1
+ 6227
+ 37324
+ 20
+
+
+ timestamp with time zone|0s
+ 2
+ 6227
+ 1184
+
+
+ timestamp with time zone|0s
+ 3
+ 6227
+ 1184
+
+
+ timestamp with time zone|0s
+ 4
+ 6227
+ 1184
+
+
+ smallint|0s
+ 5
+ 6227
+ 21
+
+
+ text|0s
+ 6
+ 6227
+ 25
+
+
+ jsonb|0s
+ 7
+ 6227
+ 3802
+
+
+ bigint|0s
+ 8
+ 6227
+ 20
+
+
+ account_id
+ 37333
+ 6227
+ 1
+ 37314
+
+
+ id
+ 1
+ 37331
+ 1
+ 6227
+ 1
+ 403
+
+
+ deleted_at
+ 37338
+ 6228
+ 403
+
+
+ 1
+ 37332
+ 1
+ 6227
+ 37331
+
+
+ bigint|0s
+ nextval('passport_auth_sessions_id_seq'::regclass)
+ 1
+ 1
+ 6241
+ 37419
+ 20
+
+
+ timestamp with time zone|0s
+ 2
+ 6241
+ 1184
+
+
+ timestamp with time zone|0s
+ 3
+ 6241
+ 1184
+
+
+ timestamp with time zone|0s
+ 4
+ 6241
+ 1184
+
+
+ jsonb|0s
+ 5
+ 6241
+ 3802
+
+
+ jsonb|0s
+ 6
+ 6241
+ 3802
+
+
+ text|0s
+ 7
+ 6241
+ 25
+
+
+ text|0s
+ 8
+ 6241
+ 25
+
+
+ text|0s
+ 9
+ 6241
+ 25
+
+
+ timestamp with time zone|0s
+ 10
+ 6241
+ 1184
+
+
+ timestamp with time zone|0s
+ 11
+ 6241
+ 1184
+
+
+ timestamp with time zone|0s
+ 12
+ 6241
+ 1184
+
+
+ bigint|0s
+ 13
+ 6241
+ 20
+
+
+ bigint|0s
+ 14
+ 6241
+ 20
+
+
+ client_id
+ 37428
+ 6241
+ 1
+ 37404
+
+
+ account_id
+ 37433
+ 6241
+ 1
+ 37314
+
+
+ id
+ 1
+ 37426
+ 1
+ 6241
+ 1
+ 403
+
+
+ deleted_at
+ 37438
+ 6242
+ 403
+
+
+ 1
+ 37427
+ 1
+ 6241
+ 37426
+
+
+ bigint|0s
+ nextval('passport_magic_tokens_id_seq'::regclass)
+ 1
+ 1
+ 6245
+ 37459
+ 20
+
+
+ timestamp with time zone|0s
+ 2
+ 6245
+ 1184
+
+
+ timestamp with time zone|0s
+ 3
+ 6245
+ 1184
+
+
+ timestamp with time zone|0s
+ 4
+ 6245
+ 1184
+
+
+ text|0s
+ 5
+ 6245
+ 25
+
+
+ smallint|0s
+ 6
+ 6245
+ 21
+
+
+ bigint|0s
+ 7
+ 6245
+ 20
+
+
+ timestamp with time zone|0s
+ 8
+ 6245
+ 1184
+
+
+ assign_to
+ 37468
+ 6245
+ 1
+ 37314
+
+
+ id
+ 1
+ 37466
+ 1
+ 6245
+ 1
+ 403
+
+
+ deleted_at
+ 37473
+ 6246
+ 403
+
+
+ 1
+ 37467
+ 1
+ 6245
+ 37466
+
+
+ bigint|0s
+ nextval('passport_notification_subscribers_id_seq'::regclass)
+ 1
+ 1
+ 6251
+ 37509
+ 20
+
+
+ timestamp with time zone|0s
+ 2
+ 6251
+ 1184
+
+
+ timestamp with time zone|0s
+ 3
+ 6251
+ 1184
+
+
+ timestamp with time zone|0s
+ 4
+ 6251
+ 1184
+
+
+ text|0s
+ 5
+ 6251
+ 25
+
+
+ text|0s
+ 6
+ 6251
+ 25
+
+
+ text|0s
+ 7
+ 6251
+ 25
+
+
+ bigint|0s
+ 8
+ 6251
+ 20
+
+
+ account_id
+ 37518
+ 6251
+ 1
+ 37314
+
+
+ id
+ 1
+ 37516
+ 1
+ 6251
+ 1
+ 403
+
+
+ device_id
+ 37523
+ 6252
+ 1
+ 403
+ default
+ 100
+ pg_catalog
+
+
+ deleted_at
+ 37524
+ 6253
+ 403
+
+
+ 1
+ 37517
+ 1
+ 6251
+ 37516
+
+
+ bigint|0s
+ nextval('passport_notifications_id_seq'::regclass)
+ 1
+ 1
+ 6249
+ 37489
+ 20
+
+
+ timestamp with time zone|0s
+ 2
+ 6249
+ 1184
+
+
+ timestamp with time zone|0s
+ 3
+ 6249
+ 1184
+
+
+ timestamp with time zone|0s
+ 4
+ 6249
+ 1184
+
+
+ text|0s
+ 5
+ 6249
+ 25
+
+
+ text|0s
+ 6
+ 6249
+ 25
+
+
+ jsonb|0s
+ 7
+ 6249
+ 3802
+
+
+ boolean|0s
+ 8
+ 6249
+ 16
+
+
+ timestamp with time zone|0s
+ 9
+ 6249
+ 1184
+
+
+ bigint|0s
+ 10
+ 6249
+ 20
+
+
+ bigint|0s
+ 11
+ 6249
+ 20
+
+
+ sender_id
+ 37498
+ 6249
+ 1
+ 37404
+
+
+ recipient_id
+ 37503
+ 6249
+ 1
+ 37314
+
+
+ id
+ 1
+ 37496
+ 1
+ 6249
+ 1
+ 403
+
+
+ deleted_at
+ 37508
+ 6250
+ 403
+
+
+ 1
+ 37497
+ 1
+ 6249
+ 37496
+
+
+ bigint|0s
+ nextval('passport_third_clients_id_seq'::regclass)
+ 1
+ 1
+ 6238
+ 37403
+ 20
+
+
+ timestamp with time zone|0s
+ 2
+ 6238
+ 1184
+
+
+ timestamp with time zone|0s
+ 3
+ 6238
+ 1184
+
+
+ timestamp with time zone|0s
+ 4
+ 6238
+ 1184
+
+
+ text|0s
+ 5
+ 6238
+ 25
+
+
+ text|0s
+ 6
+ 6238
+ 25
+
+
+ text|0s
+ 7
+ 6238
+ 25
+
+
+ text|0s
+ 8
+ 6238
+ 25
+
+
+ jsonb|0s
+ 9
+ 6238
+ 3802
+
+
+ jsonb|0s
+ 10
+ 6238
+ 3802
+
+
+ boolean|0s
+ 11
+ 6238
+ 16
+
+
+ bigint|0s
+ 12
+ 6238
+ 20
+
+
+ account_id
+ 37412
+ 6238
+ 1
+ 37314
+
+
+ id
+ 1
+ 37410
+ 1
+ 6238
+ 1
+ 403
+
+
+ alias
+ 37417
+ 6239
+ 1
+ 403
+ default
+ 100
+ pg_catalog
+
+
+ deleted_at
+ 37418
+ 6240
+ 403
+
+
+ 1
+ 37411
+ 1
+ 6238
+ 37410
+
+
+
\ No newline at end of file
diff --git a/.idea/dataSources/74bcf3ef-a2b9-435b-b9e5-f32902a33b25/storage_v2/_src_/database/hy_passport.gNOKQQ.meta b/.idea/dataSources/74bcf3ef-a2b9-435b-b9e5-f32902a33b25/storage_v2/_src_/database/hy_passport.gNOKQQ.meta
new file mode 100644
index 0000000..951b9f0
--- /dev/null
+++ b/.idea/dataSources/74bcf3ef-a2b9-435b-b9e5-f32902a33b25/storage_v2/_src_/database/hy_passport.gNOKQQ.meta
@@ -0,0 +1 @@
+#n:hy_passport
\ No newline at end of file
diff --git a/.idea/dataSources/74bcf3ef-a2b9-435b-b9e5-f32902a33b25/storage_v2/_src_/database/hy_passport.gNOKQQ/schema/public.abK9xQ.meta b/.idea/dataSources/74bcf3ef-a2b9-435b-b9e5-f32902a33b25/storage_v2/_src_/database/hy_passport.gNOKQQ/schema/public.abK9xQ.meta
new file mode 100644
index 0000000..a9e0cff
--- /dev/null
+++ b/.idea/dataSources/74bcf3ef-a2b9-435b-b9e5-f32902a33b25/storage_v2/_src_/database/hy_passport.gNOKQQ/schema/public.abK9xQ.meta
@@ -0,0 +1,2 @@
+#n:public
+! [6258, 0, null, null, -2147483648, -2147483648]
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index c7f72d7..4695490 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,73 +4,12 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -93,10 +32,13 @@
-
+
+
+
+ {
+ "customColor": "",
+ "associatedIndex": 6
+}
@@ -118,14 +60,23 @@
"node.js.detected.package.eslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
+ "run.code.analysis.last.selected.profile": "pProject Default",
"settings.editor.selected.configurable": "preferences.lookFeel",
"vue.rearranger.settings.migration": "true"
+ },
+ "keyToStringList": {
+ "DatabaseDriversLRU": [
+ "postgresql"
+ ]
}
}]]>
+
+
+
@@ -161,6 +112,11 @@
+
+
+
+
+
true
diff --git a/pkg/database/migrator.go b/pkg/database/migrator.go
index 3631ec7..195d50f 100644
--- a/pkg/database/migrator.go
+++ b/pkg/database/migrator.go
@@ -12,8 +12,7 @@ var DatabaseAutoActionRange = []any{
&models.AccountPage{},
&models.AccountContact{},
&models.AccountFriendship{},
- &models.AuthSession{},
- &models.AuthChallenge{},
+ &models.AuthTicket{},
&models.MagicToken{},
&models.ThirdClient{},
&models.ActionEvent{},
diff --git a/pkg/models/accounts.go b/pkg/models/accounts.go
index 6d43937..ab8c96a 100644
--- a/pkg/models/accounts.go
+++ b/pkg/models/accounts.go
@@ -23,9 +23,8 @@ type Account struct {
PersonalPage AccountPage `json:"personal_page"`
Contacts []AccountContact `json:"contacts"`
- Sessions []AuthSession `json:"sessions"`
- Challenges []AuthChallenge `json:"challenges"`
- Factors []AuthFactor `json:"factors"`
+ Sessions []AuthTicket `json:"sessions"`
+ Factors []AuthFactor `json:"factors"`
Events []ActionEvent `json:"events"`
MagicTokens []MagicToken `json:"-" gorm:"foreignKey:AssignTo"`
diff --git a/pkg/models/auth.go b/pkg/models/auth.go
index e74d5a3..913789f 100644
--- a/pkg/models/auth.go
+++ b/pkg/models/auth.go
@@ -23,23 +23,30 @@ type AuthFactor struct {
AccountID uint `json:"account_id"`
}
-type AuthSession struct {
+type AuthTicket struct {
BaseModel
- Claims datatypes.JSONSlice[string] `json:"claims"`
- Audiences datatypes.JSONSlice[string] `json:"audiences"`
- Challenge AuthChallenge `json:"challenge" gorm:"foreignKey:SessionID"`
- GrantToken string `json:"grant_token"`
- AccessToken string `json:"access_token"`
- RefreshToken string `json:"refresh_token"`
- ExpiredAt *time.Time `json:"expired_at"`
- AvailableAt *time.Time `json:"available_at"`
- LastGrantAt *time.Time `json:"last_grant_at"`
- ClientID *uint `json:"client_id"`
- AccountID uint `json:"account_id"`
+ Location string `json:"location"`
+ IpAddress string `json:"ip_address"`
+ UserAgent string `json:"user_agent"`
+ RequireMFA bool `json:"require_mfa"`
+ RequireAuthenticate bool `json:"require_authenticate"`
+ Claims datatypes.JSONSlice[string] `json:"claims"`
+ Audiences datatypes.JSONSlice[string] `json:"audiences"`
+ GrantToken *string `json:"grant_token"`
+ AccessToken *string `json:"access_token"`
+ RefreshToken *string `json:"refresh_token"`
+ ExpiredAt *time.Time `json:"expired_at"`
+ AvailableAt *time.Time `json:"available_at"`
+ LastGrantAt *time.Time `json:"last_grant_at"`
+ ClientID *uint `json:"client_id"`
+ AccountID uint `json:"account_id"`
}
-func (v AuthSession) IsAvailable() error {
+func (v AuthTicket) IsAvailable() error {
+ if v.RequireMFA || v.RequireAuthenticate {
+ return fmt.Errorf("session isn't authenticated yet")
+ }
if v.AvailableAt != nil && time.Now().Unix() < v.AvailableAt.Unix() {
return fmt.Errorf("session isn't available yet")
}
@@ -50,40 +57,8 @@ func (v AuthSession) IsAvailable() error {
return nil
}
-type AuthChallengeState = int8
-
-const (
- ActiveChallengeState = AuthChallengeState(iota)
- ExpiredChallengeState
- FinishChallengeState
-)
-
-type AuthChallenge struct {
- BaseModel
-
- Location string `json:"location"`
- IpAddress string `json:"ip_address"`
- UserAgent string `json:"user_agent"`
- RiskLevel int `json:"risk_level"`
- Progress int `json:"progress"`
- Requirements int `json:"requirements"`
- BlacklistFactors datatypes.JSONType[[]uint] `json:"blacklist_factors"`
- State int8 `json:"state"`
- ExpiredAt time.Time `json:"expired_at"`
- SessionID *uint `json:"session_id"`
- AccountID uint `json:"account_id"`
-}
-
-func (v AuthChallenge) IsAvailable() error {
- if time.Now().Unix() > v.ExpiredAt.Unix() {
- return fmt.Errorf("challenge expired")
- }
-
- return nil
-}
-
type AuthContext struct {
- Session AuthSession `json:"session"`
- Account Account `json:"account"`
- ExpiredAt time.Time `json:"expired_at"`
+ Ticket AuthTicket `json:"session"`
+ Account Account `json:"account"`
+ ExpiredAt time.Time `json:"expired_at"`
}
diff --git a/pkg/models/clients.go b/pkg/models/clients.go
index 5eafc53..01ae4ee 100644
--- a/pkg/models/clients.go
+++ b/pkg/models/clients.go
@@ -11,7 +11,7 @@ type ThirdClient struct {
Secret string `json:"secret"`
Urls datatypes.JSONSlice[string] `json:"urls"`
Callbacks datatypes.JSONSlice[string] `json:"callbacks"`
- Sessions []AuthSession `json:"sessions" gorm:"foreignKey:ClientID"`
+ Sessions []AuthTicket `json:"sessions" gorm:"foreignKey:ClientID"`
Notifications []Notification `json:"notifications" gorm:"foreignKey:SenderID"`
IsDraft bool `json:"is_draft"`
AccountID *uint `json:"account_id"`
diff --git a/pkg/security/challanges.go b/pkg/security/challanges.go
deleted file mode 100644
index 0db0200..0000000
--- a/pkg/security/challanges.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package security
-
-import (
- "fmt"
- "strings"
- "time"
-
- "github.com/google/uuid"
-
- "git.solsynth.dev/hydrogen/passport/pkg/database"
- "git.solsynth.dev/hydrogen/passport/pkg/models"
- "github.com/samber/lo"
- "gorm.io/datatypes"
-)
-
-func CalcRisk(user models.Account, ip, ua string) int {
- risk := 3
- var secureFactor int64
- if err := database.C.Where(models.AuthChallenge{
- AccountID: user.ID,
- IpAddress: ip,
- }).Model(models.AuthChallenge{}).Count(&secureFactor).Error; err == nil {
- if secureFactor >= 3 {
- risk -= 3
- } else if secureFactor >= 1 {
- risk -= 2
- }
- }
-
- return risk
-}
-
-func NewChallenge(user models.Account, factors []models.AuthFactor, ip, ua string) (models.AuthChallenge, error) {
- var challenge models.AuthChallenge
- // Pickup any challenge if possible
- if err := database.C.Where(models.AuthChallenge{
- AccountID: user.ID,
- }).Where("state = ?", models.ActiveChallengeState).First(&challenge).Error; err == nil {
- return challenge, nil
- }
-
- // Calculate the risk level
- risk := CalcRisk(user, ip, ua)
-
- // Clamp risk in the exists requirements factor count
- requirements := lo.Clamp(risk, 1, len(factors))
-
- challenge = models.AuthChallenge{
- IpAddress: ip,
- UserAgent: ua,
- RiskLevel: risk,
- Requirements: requirements,
- BlacklistFactors: datatypes.NewJSONType([]uint{}),
- State: models.ActiveChallengeState,
- ExpiredAt: time.Now().Add(2 * time.Hour),
- AccountID: user.ID,
- }
-
- err := database.C.Save(&challenge).Error
-
- return challenge, err
-}
-
-func DoChallenge(challenge models.AuthChallenge, factor models.AuthFactor, code string) error {
- if err := challenge.IsAvailable(); err != nil {
- challenge.State = models.ExpiredChallengeState
- database.C.Save(&challenge)
- return err
- }
- if challenge.Progress >= challenge.Requirements {
- return fmt.Errorf("challenge already passed")
- }
-
- blacklist := challenge.BlacklistFactors.Data()
- if lo.Contains(blacklist, factor.ID) {
- return fmt.Errorf("factor in blacklist, please change another factor to challenge")
- }
- if err := VerifyFactor(factor, code); err != nil {
- return err
- }
-
- challenge.Progress++
- challenge.BlacklistFactors = datatypes.NewJSONType(append(blacklist, factor.ID))
-
- if err := database.C.Save(&challenge).Error; err != nil {
- return err
- }
-
- // Revoke some factor passwords
- if factor.Type == models.EmailPasswordFactor {
- factor.Secret = strings.ReplaceAll(uuid.NewString(), "-", "")
- database.C.Save(&factor)
- }
-
- return nil
-}
diff --git a/pkg/security/factors.go b/pkg/security/factors.go
deleted file mode 100644
index 5d86876..0000000
--- a/pkg/security/factors.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package security
-
-import (
- "fmt"
-
- "git.solsynth.dev/hydrogen/passport/pkg/models"
- "github.com/samber/lo"
-)
-
-func VerifyFactor(factor models.AuthFactor, code string) error {
- switch factor.Type {
- case models.PasswordAuthFactor:
- return lo.Ternary(
- VerifyPassword(code, factor.Secret),
- nil,
- fmt.Errorf("invalid password"),
- )
- case models.EmailPasswordFactor:
- return lo.Ternary(
- code == factor.Secret,
- nil,
- fmt.Errorf("invalid verification code"),
- )
- }
-
- return nil
-}
diff --git a/pkg/security/sessions.go b/pkg/security/sessions.go
deleted file mode 100644
index abfc4fe..0000000
--- a/pkg/security/sessions.go
+++ /dev/null
@@ -1,165 +0,0 @@
-package security
-
-import (
- "fmt"
- "strconv"
- "time"
-
- "github.com/spf13/viper"
-
- "git.solsynth.dev/hydrogen/passport/pkg/database"
- "git.solsynth.dev/hydrogen/passport/pkg/models"
- "github.com/google/uuid"
- "github.com/samber/lo"
-)
-
-func GrantSession(challenge models.AuthChallenge, claims, audiences []string, expired, available *time.Time) (models.AuthSession, error) {
- var session models.AuthSession
- if err := challenge.IsAvailable(); err != nil {
- return session, err
- }
- if challenge.Progress < challenge.Requirements {
- return session, fmt.Errorf("challenge haven't passed")
- }
-
- challenge.State = models.FinishChallengeState
-
- session = models.AuthSession{
- Claims: claims,
- Audiences: audiences,
- Challenge: challenge,
- GrantToken: uuid.NewString(),
- AccessToken: uuid.NewString(),
- RefreshToken: uuid.NewString(),
- ExpiredAt: expired,
- AvailableAt: available,
- AccountID: challenge.AccountID,
- }
-
- if err := database.C.Save(&challenge).Error; err != nil {
- return session, err
- } else if err := database.C.Save(&session).Error; err != nil {
- return session, err
- }
-
- return session, nil
-}
-
-func GrantOauthSession(user models.Account, client models.ThirdClient, claims, audiences []string, expired, available *time.Time, ip, ua string) (models.AuthSession, error) {
- session := models.AuthSession{
- Claims: claims,
- Audiences: audiences,
- Challenge: models.AuthChallenge{
- IpAddress: ip,
- UserAgent: ua,
- RiskLevel: CalcRisk(user, ip, ua),
- State: models.FinishChallengeState,
- AccountID: user.ID,
- },
- GrantToken: uuid.NewString(),
- AccessToken: uuid.NewString(),
- RefreshToken: uuid.NewString(),
- ExpiredAt: expired,
- AvailableAt: available,
- ClientID: &client.ID,
- AccountID: user.ID,
- }
-
- if err := database.C.Save(&session).Error; err != nil {
- return session, err
- }
-
- return session, nil
-}
-
-func RegenSession(session models.AuthSession) (models.AuthSession, error) {
- session.GrantToken = uuid.NewString()
- session.AccessToken = uuid.NewString()
- session.RefreshToken = uuid.NewString()
- err := database.C.Save(&session).Error
- return session, err
-}
-
-func GetToken(session models.AuthSession) (string, string, error) {
- var refresh, access string
- if err := session.IsAvailable(); err != nil {
- return refresh, access, err
- }
-
- accessDuration := time.Duration(viper.GetInt64("security.access_token_duration")) * time.Second
- refreshDuration := time.Duration(viper.GetInt64("security.refresh_token_duration")) * time.Second
-
- var err error
- sub := strconv.Itoa(int(session.AccountID))
- sed := strconv.Itoa(int(session.ID))
- access, err = EncodeJwt(session.AccessToken, JwtAccessType, sub, sed, session.Audiences, time.Now().Add(accessDuration))
- if err != nil {
- return refresh, access, err
- }
- refresh, err = EncodeJwt(session.RefreshToken, JwtRefreshType, sub, sed, session.Audiences, time.Now().Add(refreshDuration))
- if err != nil {
- return refresh, access, err
- }
-
- session.LastGrantAt = lo.ToPtr(time.Now())
- database.C.Save(&session)
-
- return access, refresh, nil
-}
-
-func ExchangeToken(token string) (string, string, error) {
- var session models.AuthSession
- if err := database.C.Where(models.AuthSession{GrantToken: token}).First(&session).Error; err != nil {
- return "404", "403", err
- } else if session.LastGrantAt != nil {
- return "404", "403", fmt.Errorf("session was granted the first token, use refresh token instead")
- } else if len(session.Audiences) > 1 {
- return "404", "403", fmt.Errorf("should use authorization code grant type")
- }
-
- return GetToken(session)
-}
-
-func ExchangeOauthToken(clientId, clientSecret, redirectUri, token string) (string, string, error) {
- var client models.ThirdClient
- if err := database.C.Where(models.ThirdClient{Alias: clientId}).First(&client).Error; err != nil {
- return "404", "403", err
- } else if client.Secret != clientSecret {
- return "404", "403", fmt.Errorf("invalid client secret")
- } else if !client.IsDraft && !lo.Contains(client.Callbacks, redirectUri) {
- return "404", "403", fmt.Errorf("invalid redirect uri")
- }
-
- var session models.AuthSession
- if err := database.C.Where(models.AuthSession{GrantToken: token}).First(&session).Error; err != nil {
- return "404", "403", err
- } else if session.LastGrantAt != nil {
- return "404", "403", fmt.Errorf("session was granted the first token, use refresh token instead")
- }
-
- return GetToken(session)
-}
-
-func RefreshToken(token string) (string, string, error) {
- parseInt := func(str string) int {
- val, _ := strconv.Atoi(str)
- return val
- }
-
- var session models.AuthSession
- if claims, err := DecodeJwt(token); err != nil {
- return "404", "403", err
- } else if claims.Type != JwtRefreshType {
- return "404", "403", fmt.Errorf("invalid token type, expected refresh token")
- } else if err := database.C.Where(models.AuthSession{
- BaseModel: models.BaseModel{ID: uint(parseInt(claims.SessionID))},
- }).First(&session).Error; err != nil {
- return "404", "403", err
- }
-
- if session, err := RegenSession(session); err != nil {
- return "404", "403", err
- } else {
- return GetToken(session)
- }
-}
diff --git a/pkg/server/accounts_api.go b/pkg/server/accounts_api.go
index 96858fa..47683cf 100644
--- a/pkg/server/accounts_api.go
+++ b/pkg/server/accounts_api.go
@@ -114,7 +114,7 @@ func killSession(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
id, _ := c.ParamsInt("sessionId", 0)
- if err := database.C.Delete(&models.AuthSession{}, &models.AuthSession{
+ if err := database.C.Delete(&models.AuthTicket{}, &models.AuthTicket{
BaseModel: models.BaseModel{ID: uint(id)},
AccountID: user.ID,
}).Error; err != nil {
diff --git a/pkg/server/auth_api.go b/pkg/server/auth_api.go
new file mode 100644
index 0000000..596242f
--- /dev/null
+++ b/pkg/server/auth_api.go
@@ -0,0 +1,145 @@
+package server
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/gofiber/fiber/v2"
+
+ "git.solsynth.dev/hydrogen/passport/pkg/services"
+)
+
+func doAuthenticate(c *fiber.Ctx) error {
+ var data struct {
+ Username string `json:"username"`
+ Password string `json:"password" validate:"required"`
+ }
+
+ if err := BindAndValidate(c, &data); err != nil {
+ return err
+ }
+
+ user, err := services.LookupAccount(data.Username)
+ if err != nil {
+ return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("account was not found: %v", err.Error()))
+ }
+
+ ticket, err := services.NewTicket(user, c.IP(), c.Get(fiber.HeaderUserAgent))
+ if err != nil {
+ return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("unable setup ticket: %v", err.Error()))
+ }
+
+ ticket, err = services.ActiveTicketWithPassword(ticket, data.Password)
+ if err != nil {
+ return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("invalid password: %v", err.Error()))
+ }
+
+ return c.JSON(fiber.Map{
+ "is_finished": ticket.IsAvailable(),
+ "ticket": ticket,
+ })
+}
+
+func doMultiFactorAuthenticate(c *fiber.Ctx) error {
+ var data struct {
+ TicketID uint `json:"ticket_id" validate:"required"`
+ FactorID uint `json:"factor_id" validate:"required"`
+ Code string `json:"code" validate:"required"`
+ }
+
+ if err := BindAndValidate(c, &data); err != nil {
+ return err
+ }
+
+ ticket, err := services.GetTicket(data.TicketID)
+ if err != nil {
+ return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("ticket was not found: %v", err.Error()))
+ }
+
+ factor, err := services.GetFactor(data.FactorID)
+ if err != nil {
+ return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("factor was not found: %v", err.Error()))
+ }
+
+ ticket, err = services.ActiveTicketWithMFA(ticket, factor, data.Code)
+ if err != nil {
+ return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("invalid code: %v", err.Error()))
+ }
+
+ return c.JSON(fiber.Map{
+ "is_finished": ticket.IsAvailable(),
+ "ticket": ticket,
+ })
+}
+
+func getToken(c *fiber.Ctx) error {
+ var data struct {
+ Code string `json:"code" form:"code"`
+ RefreshToken string `json:"refresh_token" form:"refresh_token"`
+ ClientID string `json:"client_id" form:"client_id"`
+ ClientSecret string `json:"client_secret" form:"client_secret"`
+ Username string `json:"username" form:"username"`
+ Password string `json:"password" form:"password"`
+ RedirectUri string `json:"redirect_uri" form:"redirect_uri"`
+ GrantType string `json:"grant_type" form:"grant_type"`
+ }
+
+ if err := BindAndValidate(c, &data); err != nil {
+ return err
+ }
+
+ var err error
+ var access, refresh string
+ switch data.GrantType {
+ case "refresh_token":
+ // Refresh Token
+ access, refresh, err = services.RefreshToken(data.RefreshToken)
+ if err != nil {
+ return fiber.NewError(fiber.StatusBadRequest, err.Error())
+ }
+ case "authorization_code":
+ // Authorization Code Mode
+ access, refresh, err = services.ExchangeOauthToken(data.ClientID, data.ClientSecret, data.RedirectUri, data.Code)
+ if err != nil {
+ return fiber.NewError(fiber.StatusBadRequest, err.Error())
+ }
+ case "password":
+ // Password Mode
+ user, err := services.LookupAccount(data.Username)
+ if err != nil {
+ return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("account was not found: %v", err.Error()))
+ }
+ ticket, err := services.NewTicket(user, c.IP(), c.Get(fiber.HeaderUserAgent))
+ if err != nil {
+ return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("unable setup ticket: %v", err.Error()))
+ }
+ ticket, err = services.ActiveTicketWithPassword(ticket, data.Password)
+ if err != nil {
+ return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("invalid password: %v", err.Error()))
+ } else if ticket.GrantToken == nil {
+ return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("unable to get grant token to get token"))
+ }
+ access, refresh, err = services.ExchangeOauthToken(data.ClientID, data.ClientSecret, data.RedirectUri, *ticket.GrantToken)
+ if err != nil {
+ return fiber.NewError(fiber.StatusBadRequest, err.Error())
+ }
+ case "grant_token":
+ // Internal Usage
+ access, refresh, err = services.ExchangeToken(data.Code)
+ if err != nil {
+ return fiber.NewError(fiber.StatusBadRequest, err.Error())
+ }
+ default:
+ return fiber.NewError(fiber.StatusBadRequest, "unsupported exchange token type")
+ }
+
+ services.SetJwtCookieSet(c, access, refresh)
+
+ return c.JSON(fiber.Map{
+ "id_token": access,
+ "access_token": access,
+ "refresh_token": refresh,
+ "token_type": "Bearer",
+ "expires_in": (30 * time.Minute).Seconds(),
+ })
+}
diff --git a/pkg/server/auth_middleware.go b/pkg/server/auth_middleware.go
index 284c2ce..adb133b 100644
--- a/pkg/server/auth_middleware.go
+++ b/pkg/server/auth_middleware.go
@@ -3,14 +3,13 @@ package server
import (
"strings"
- "git.solsynth.dev/hydrogen/passport/pkg/security"
"git.solsynth.dev/hydrogen/passport/pkg/services"
"github.com/gofiber/fiber/v2"
)
func authMiddleware(c *fiber.Ctx) error {
var token string
- if cookie := c.Cookies(security.CookieAccessKey); len(cookie) > 0 {
+ if cookie := c.Cookies(services.CookieAccessKey); len(cookie) > 0 {
token = cookie
}
if header := c.Get(fiber.HeaderAuthorization); len(header) > 0 {
@@ -42,10 +41,10 @@ func authFunc(c *fiber.Ctx, overrides ...string) error {
}
}
- rtk := c.Cookies(security.CookieRefreshKey)
+ rtk := c.Cookies(services.CookieRefreshKey)
if user, atk, rtk, err := services.Authenticate(token, rtk, 0); err == nil {
if atk != token {
- security.SetJwtCookieSet(c, atk, rtk)
+ services.SetJwtCookieSet(c, atk, rtk)
}
c.Locals("principal", user)
return nil
diff --git a/pkg/server/challanges_api.go b/pkg/server/challanges_api.go
deleted file mode 100644
index 826724e..0000000
--- a/pkg/server/challanges_api.go
+++ /dev/null
@@ -1,140 +0,0 @@
-package server
-
-import (
- "time"
-
- "github.com/gofiber/fiber/v2"
-
- "git.solsynth.dev/hydrogen/passport/pkg/security"
- "git.solsynth.dev/hydrogen/passport/pkg/services"
- "github.com/samber/lo"
-)
-
-func startChallenge(c *fiber.Ctx) error {
- var data struct {
- ID string `json:"id" validate:"required"`
- }
-
- if err := BindAndValidate(c, &data); err != nil {
- return err
- }
-
- user, err := services.LookupAccount(data.ID)
- if err != nil {
- return fiber.NewError(fiber.StatusNotFound, err.Error())
- }
- factors, err := services.LookupFactorsByUser(user.ID)
- if err != nil {
- return fiber.NewError(fiber.StatusNotFound, err.Error())
- }
-
- challenge, err := security.NewChallenge(user, factors, c.IP(), c.Get(fiber.HeaderUserAgent))
- if err != nil {
- return fiber.NewError(fiber.StatusNotFound, err.Error())
- }
-
- services.AddEvent(user, "challenges.start", data.ID, c.IP(), c.Get(fiber.HeaderUserAgent))
- return c.JSON(fiber.Map{
- "display_name": user.Nick,
- "challenge": challenge,
- "factors": factors,
- })
-}
-
-func doChallenge(c *fiber.Ctx) error {
- var data struct {
- ChallengeID uint `json:"challenge_id" validate:"required"`
- FactorID uint `json:"factor_id" validate:"required"`
- Secret string `json:"secret" validate:"required"`
- }
-
- if err := BindAndValidate(c, &data); err != nil {
- return err
- }
-
- challenge, err := services.LookupChallengeWithFingerprint(data.ChallengeID, c.IP(), c.Get(fiber.HeaderUserAgent))
- if err != nil {
- return fiber.NewError(fiber.StatusNotFound, err.Error())
- }
-
- factor, err := services.LookupFactor(data.FactorID)
- if err != nil {
- return fiber.NewError(fiber.StatusNotFound, err.Error())
- }
-
- if err := security.DoChallenge(challenge, factor, data.Secret); err != nil {
- return fiber.NewError(fiber.StatusBadRequest, err.Error())
- }
-
- challenge, err = services.LookupChallenge(data.ChallengeID)
- if err != nil {
- return fiber.NewError(fiber.StatusBadRequest, err.Error())
- } else if challenge.Progress >= challenge.Requirements {
- session, err := security.GrantSession(challenge, []string{"*"}, []string{"passport"}, nil, lo.ToPtr(time.Now()))
- if err != nil {
- return fiber.NewError(fiber.StatusBadRequest, err.Error())
- }
-
- return c.JSON(fiber.Map{
- "is_finished": true,
- "challenge": challenge,
- "session": session,
- })
- }
-
- return c.JSON(fiber.Map{
- "is_finished": false,
- "challenge": challenge,
- "session": nil,
- })
-}
-
-func exchangeToken(c *fiber.Ctx) error {
- var data struct {
- Code string `json:"code" form:"code"`
- RefreshToken string `json:"refresh_token" form:"refresh_token"`
- ClientID string `json:"client_id" form:"client_id"`
- ClientSecret string `json:"client_secret" form:"client_secret"`
- RedirectUri string `json:"redirect_uri" form:"redirect_uri"`
- GrantType string `json:"grant_type" form:"grant_type"`
- }
-
- if err := BindAndValidate(c, &data); err != nil {
- return err
- }
-
- var err error
- var access, refresh string
- switch data.GrantType {
- case "authorization_code":
- // Authorization Code Mode
- access, refresh, err = security.ExchangeOauthToken(data.ClientID, data.ClientSecret, data.RedirectUri, data.Code)
- if err != nil {
- return fiber.NewError(fiber.StatusBadRequest, err.Error())
- }
- case "grant_token":
- // Internal Usage
- access, refresh, err = security.ExchangeToken(data.Code)
- if err != nil {
- return fiber.NewError(fiber.StatusBadRequest, err.Error())
- }
- case "refresh_token":
- // Refresh Token
- access, refresh, err = security.RefreshToken(data.RefreshToken)
- if err != nil {
- return fiber.NewError(fiber.StatusBadRequest, err.Error())
- }
- default:
- return fiber.NewError(fiber.StatusBadRequest, "unsupported exchange token type")
- }
-
- security.SetJwtCookieSet(c, access, refresh)
-
- return c.JSON(fiber.Map{
- "id_token": access,
- "access_token": access,
- "refresh_token": refresh,
- "token_type": "Bearer",
- "expires_in": (30 * time.Minute).Seconds(),
- })
-}
diff --git a/pkg/server/factors_api.go b/pkg/server/factors_api.go
index 301547a..ae655bf 100644
--- a/pkg/server/factors_api.go
+++ b/pkg/server/factors_api.go
@@ -8,7 +8,7 @@ import (
func requestFactorToken(c *fiber.Ctx) error {
id, _ := c.ParamsInt("factorId", 0)
- factor, err := services.LookupFactor(uint(id))
+ factor, err := services.GetFactor(uint(id))
if err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
diff --git a/pkg/server/oauth_api.go b/pkg/server/oauth_api.go
index c30c578..db7c653 100644
--- a/pkg/server/oauth_api.go
+++ b/pkg/server/oauth_api.go
@@ -6,7 +6,6 @@ import (
"git.solsynth.dev/hydrogen/passport/pkg/database"
"git.solsynth.dev/hydrogen/passport/pkg/models"
- "git.solsynth.dev/hydrogen/passport/pkg/security"
"git.solsynth.dev/hydrogen/passport/pkg/services"
"github.com/gofiber/fiber/v2"
"github.com/samber/lo"
@@ -29,8 +28,8 @@ func preConnect(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
- var session models.AuthSession
- if err := database.C.Where(&models.AuthSession{
+ var session models.AuthTicket
+ if err := database.C.Where(&models.AuthTicket{
AccountID: user.ID,
ClientID: &client.ID,
}).Where("last_grant_at IS NULL").First(&session).Error; err == nil {
@@ -40,7 +39,7 @@ func preConnect(c *fiber.Ctx) error {
"session": nil,
})
} else {
- session, err = security.RegenSession(session)
+ session, err = services.RegenSession(session)
}
return c.JSON(fiber.Map{
@@ -73,13 +72,11 @@ func doConnect(c *fiber.Ctx) error {
switch response {
case "code":
// OAuth Authorization Mode
- session, err := security.GrantOauthSession(
+ ticket, err := services.NewOauthTicket(
user,
client,
strings.Split(scope, " "),
[]string{"passport", client.Alias},
- nil,
- lo.ToPtr(time.Now()),
c.IP(),
c.Get(fiber.HeaderUserAgent),
)
@@ -89,26 +86,24 @@ func doConnect(c *fiber.Ctx) error {
} else {
services.AddEvent(user, "oauth.connect", client.Alias, c.IP(), c.Get(fiber.HeaderUserAgent))
return c.JSON(fiber.Map{
- "session": session,
+ "session": ticket,
"redirect_uri": redirect,
})
}
case "token":
// OAuth Implicit Mode
- session, err := security.GrantOauthSession(
+ ticket, err := services.NewOauthTicket(
user,
client,
strings.Split(scope, " "),
[]string{"passport", client.Alias},
- nil,
- lo.ToPtr(time.Now()),
c.IP(),
c.Get(fiber.HeaderUserAgent),
)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
- } else if access, refresh, err := security.GetToken(session); err != nil {
+ } else if access, refresh, err := services.GetToken(ticket); err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
} else {
services.AddEvent(user, "oauth.connect", client.Alias, c.IP(), c.Get(fiber.HeaderUserAgent))
@@ -116,7 +111,7 @@ func doConnect(c *fiber.Ctx) error {
"access_token": access,
"refresh_token": refresh,
"redirect_uri": redirect,
- "session": session,
+ "ticket": ticket,
})
}
default:
diff --git a/pkg/server/security_api.go b/pkg/server/security_api.go
index d9a6730..f5a442f 100644
--- a/pkg/server/security_api.go
+++ b/pkg/server/security_api.go
@@ -6,52 +6,23 @@ import (
"github.com/gofiber/fiber/v2"
)
-func getChallenges(c *fiber.Ctx) error {
+func getTickets(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
take := c.QueryInt("take", 0)
offset := c.QueryInt("offset", 0)
var count int64
- var challenges []models.AuthChallenge
+ var sessions []models.AuthTicket
if err := database.C.
- Where(&models.AuthChallenge{AccountID: user.ID}).
- Model(&models.AuthChallenge{}).
+ Where(&models.AuthTicket{AccountID: user.ID}).
+ Model(&models.AuthTicket{}).
Count(&count).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
if err := database.C.
Order("created_at desc").
- Where(&models.AuthChallenge{AccountID: user.ID}).
- Limit(take).
- Offset(offset).
- Find(&challenges).Error; err != nil {
- return fiber.NewError(fiber.StatusInternalServerError, err.Error())
- }
-
- return c.JSON(fiber.Map{
- "count": count,
- "data": challenges,
- })
-}
-
-func getSessions(c *fiber.Ctx) error {
- user := c.Locals("principal").(models.Account)
- take := c.QueryInt("take", 0)
- offset := c.QueryInt("offset", 0)
-
- var count int64
- var sessions []models.AuthSession
- if err := database.C.
- Where(&models.AuthSession{AccountID: user.ID}).
- Model(&models.AuthSession{}).
- Count(&count).Error; err != nil {
- return fiber.NewError(fiber.StatusInternalServerError, err.Error())
- }
-
- if err := database.C.
- Order("created_at desc").
- Where(&models.AuthSession{AccountID: user.ID}).
+ Where(&models.AuthTicket{AccountID: user.ID}).
Limit(take).
Offset(offset).
Find(&sessions).Error; err != nil {
diff --git a/pkg/server/startup.go b/pkg/server/startup.go
index c727925..186d5ac 100644
--- a/pkg/server/startup.go
+++ b/pkg/server/startup.go
@@ -87,8 +87,7 @@ func NewServer() {
me.Put("/", authMiddleware, editUserinfo)
me.Put("/page", authMiddleware, editPersonalPage)
me.Get("/events", authMiddleware, getEvents)
- me.Get("/challenges", authMiddleware, getChallenges)
- me.Get("/sessions", authMiddleware, getSessions)
+ me.Get("/tickets", authMiddleware, getTickets)
me.Delete("/sessions/:sessionId", authMiddleware, killSession)
me.Post("/confirm", doRegisterConfirm)
@@ -112,9 +111,8 @@ func NewServer() {
api.Post("/users", doRegister)
- api.Put("/auth", startChallenge)
- api.Post("/auth", doChallenge)
- api.Post("/auth/token", exchangeToken)
+ api.Post("/auth", doAuthenticate)
+ api.Post("/auth/token", getToken)
api.Post("/auth/factors/:factorId", requestFactorToken)
api.Get("/auth/o/connect", authMiddleware, preConnect)
diff --git a/pkg/server/userinfo.go b/pkg/server/userinfo_api.go
similarity index 100%
rename from pkg/server/userinfo.go
rename to pkg/server/userinfo_api.go
diff --git a/pkg/services/accounts.go b/pkg/services/accounts.go
index 44fec1e..63801aa 100644
--- a/pkg/services/accounts.go
+++ b/pkg/services/accounts.go
@@ -6,7 +6,6 @@ import (
"git.solsynth.dev/hydrogen/passport/pkg/database"
"git.solsynth.dev/hydrogen/passport/pkg/models"
- "git.solsynth.dev/hydrogen/passport/pkg/security"
"github.com/google/uuid"
"github.com/samber/lo"
"gorm.io/gorm"
@@ -23,14 +22,14 @@ func GetAccount(id uint) (models.Account, error) {
return account, nil
}
-func LookupAccount(id string) (models.Account, error) {
+func LookupAccount(probe string) (models.Account, error) {
var account models.Account
- if err := database.C.Where(models.Account{Name: id}).First(&account).Error; err == nil {
+ if err := database.C.Where(models.Account{Name: probe}).First(&account).Error; err == nil {
return account, nil
}
var contact models.AccountContact
- if err := database.C.Where(models.AccountContact{Content: id}).First(&contact).Error; err == nil {
+ if err := database.C.Where(models.AccountContact{Content: probe}).First(&contact).Error; err == nil {
if err := database.C.
Where(models.Account{
BaseModel: models.BaseModel{ID: contact.AccountID},
@@ -52,7 +51,7 @@ func CreateAccount(name, nick, email, password string) (models.Account, error) {
Factors: []models.AuthFactor{
{
Type: models.PasswordAuthFactor,
- Secret: security.HashPassword(password),
+ Secret: HashPassword(password),
},
{
Type: models.EmailPasswordFactor,
diff --git a/pkg/services/auth.go b/pkg/services/auth.go
index 250e7b3..6eb22c0 100644
--- a/pkg/services/auth.go
+++ b/pkg/services/auth.go
@@ -6,7 +6,6 @@ import (
"git.solsynth.dev/hydrogen/passport/pkg/database"
"git.solsynth.dev/hydrogen/passport/pkg/models"
- "git.solsynth.dev/hydrogen/passport/pkg/security"
"github.com/gofiber/fiber/v2"
jsoniter "github.com/json-iterator/go"
"github.com/rs/zerolog/log"
@@ -16,12 +15,12 @@ import (
const authContextBucket = "AuthContext"
func Authenticate(access, refresh string, depth int) (user models.Account, newAccess, newRefresh string, err error) {
- var claims security.PayloadClaims
- claims, err = security.DecodeJwt(access)
+ var claims PayloadClaims
+ claims, err = DecodeJwt(access)
if err != nil {
if len(refresh) > 0 && depth < 1 {
// Auto refresh and retry
- newAccess, newRefresh, err = security.RefreshToken(refresh)
+ newAccess, newRefresh, err = RefreshToken(refresh)
if err == nil {
return Authenticate(newAccess, newRefresh, depth+1)
}
@@ -74,7 +73,7 @@ func GetAuthContext(jti string) (models.AuthContext, error) {
})
if err == nil && time.Now().Unix() >= ctx.ExpiredAt.Unix() {
- RevokeAuthContext(jti)
+ _ = RevokeAuthContext(jti)
return ctx, fmt.Errorf("auth context has been expired")
}
@@ -86,7 +85,7 @@ func GrantAuthContext(jti string) (models.AuthContext, error) {
var ctx models.AuthContext
// Query data from primary database
- session, err := LookupSessionWithToken(jti)
+ session, err := GetTicketWithToken(jti)
if err != nil {
return ctx, fmt.Errorf("invalid auth session: %v", err)
} else if err := session.IsAvailable(); err != nil {
@@ -101,7 +100,7 @@ func GrantAuthContext(jti string) (models.AuthContext, error) {
// Every context should expires in some while
// Once user update their account info, this will have delay to update
ctx = models.AuthContext{
- Session: session,
+ Ticket: session,
Account: user,
ExpiredAt: time.Now().Add(5 * time.Minute),
}
diff --git a/pkg/services/challanges.go b/pkg/services/challanges.go
deleted file mode 100644
index 4b33a20..0000000
--- a/pkg/services/challanges.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package services
-
-import (
- "git.solsynth.dev/hydrogen/passport/pkg/database"
- "git.solsynth.dev/hydrogen/passport/pkg/models"
-)
-
-func LookupChallenge(id uint) (models.AuthChallenge, error) {
- var challenge models.AuthChallenge
- err := database.C.Where(models.AuthChallenge{
- BaseModel: models.BaseModel{ID: id},
- }).First(&challenge).Error
-
- return challenge, err
-}
-
-func LookupChallengeWithFingerprint(id uint, ip string, ua string) (models.AuthChallenge, error) {
- var challenge models.AuthChallenge
- err := database.C.Where(models.AuthChallenge{
- BaseModel: models.BaseModel{ID: id},
- IpAddress: ip,
- UserAgent: ua,
- }).First(&challenge).Error
-
- return challenge, err
-}
diff --git a/pkg/security/encryptor.go b/pkg/services/encryptor.go
similarity index 94%
rename from pkg/security/encryptor.go
rename to pkg/services/encryptor.go
index 6cebde0..8700731 100644
--- a/pkg/security/encryptor.go
+++ b/pkg/services/encryptor.go
@@ -1,4 +1,4 @@
-package security
+package services
import "golang.org/x/crypto/bcrypt"
diff --git a/pkg/services/factors.go b/pkg/services/factors.go
index f7a6b41..011aa02 100644
--- a/pkg/services/factors.go
+++ b/pkg/services/factors.go
@@ -2,6 +2,7 @@ package services
import (
"fmt"
+ "github.com/samber/lo"
"git.solsynth.dev/hydrogen/passport/pkg/database"
"git.solsynth.dev/hydrogen/passport/pkg/models"
@@ -24,7 +25,17 @@ Thank you for your cooperation in helping us maintain the security of your accou
Best regards,
%s`
-func LookupFactor(id uint) (models.AuthFactor, error) {
+func GetPasswordFactor(userId uint) (models.AuthFactor, error) {
+ var factor models.AuthFactor
+ err := database.C.Where(models.AuthFactor{
+ Type: models.PasswordAuthFactor,
+ AccountID: userId,
+ }).First(&factor).Error
+
+ return factor, err
+}
+
+func GetFactor(id uint) (models.AuthFactor, error) {
var factor models.AuthFactor
err := database.C.Where(models.AuthFactor{
BaseModel: models.BaseModel{ID: id},
@@ -33,10 +44,10 @@ func LookupFactor(id uint) (models.AuthFactor, error) {
return factor, err
}
-func LookupFactorsByUser(uid uint) ([]models.AuthFactor, error) {
+func ListUserFactor(userId uint) ([]models.AuthFactor, error) {
var factors []models.AuthFactor
err := database.C.Where(models.AuthFactor{
- AccountID: uid,
+ AccountID: userId,
}).Find(&factors).Error
return factors, err
@@ -68,3 +79,22 @@ func GetFactorCode(factor models.AuthFactor) (bool, error) {
return false, nil
}
}
+
+func CheckFactor(factor models.AuthFactor, code string) error {
+ switch factor.Type {
+ case models.PasswordAuthFactor:
+ return lo.Ternary(
+ VerifyPassword(code, factor.Secret),
+ nil,
+ fmt.Errorf("invalid password"),
+ )
+ case models.EmailPasswordFactor:
+ return lo.Ternary(
+ code == factor.Secret,
+ nil,
+ fmt.Errorf("invalid verification code"),
+ )
+ }
+
+ return nil
+}
diff --git a/pkg/security/jwt.go b/pkg/services/jwt.go
similarity index 99%
rename from pkg/security/jwt.go
rename to pkg/services/jwt.go
index 8d77545..de83bd5 100644
--- a/pkg/security/jwt.go
+++ b/pkg/services/jwt.go
@@ -1,4 +1,4 @@
-package security
+package services
import (
"fmt"
diff --git a/pkg/services/sessions.go b/pkg/services/ticker_maintainer.go
similarity index 80%
rename from pkg/services/sessions.go
rename to pkg/services/ticker_maintainer.go
index 47b0347..487398e 100644
--- a/pkg/services/sessions.go
+++ b/pkg/services/ticker_maintainer.go
@@ -1,28 +1,15 @@
package services
import (
- "time"
-
"git.solsynth.dev/hydrogen/passport/pkg/database"
"git.solsynth.dev/hydrogen/passport/pkg/models"
jsoniter "github.com/json-iterator/go"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
"go.etcd.io/bbolt"
+ "time"
)
-func LookupSessionWithToken(tokenId string) (models.AuthSession, error) {
- var session models.AuthSession
- if err := database.C.
- Where(models.AuthSession{AccessToken: tokenId}).
- Or(models.AuthSession{RefreshToken: tokenId}).
- First(&session).Error; err != nil {
- return session, err
- }
-
- return session, nil
-}
-
func DoAutoSignoff() {
duration := time.Duration(viper.GetInt64("security.auto_signoff_duration")) * time.Second
divider := time.Now().Add(-duration)
@@ -31,7 +18,7 @@ func DoAutoSignoff() {
if tx := database.C.
Where("last_grant_at < ?", divider).
- Delete(&models.AuthSession{}); tx.Error != nil {
+ Delete(&models.AuthTicket{}); tx.Error != nil {
log.Error().Err(tx.Error).Msg("An error occurred when running auto sign off...")
} else {
log.Debug().Int64("affected", tx.RowsAffected).Msg("Auto sign off accomplished.")
diff --git a/pkg/services/ticket.go b/pkg/services/ticket.go
new file mode 100644
index 0000000..5379a2e
--- /dev/null
+++ b/pkg/services/ticket.go
@@ -0,0 +1,136 @@
+package services
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/google/uuid"
+
+ "git.solsynth.dev/hydrogen/passport/pkg/database"
+ "git.solsynth.dev/hydrogen/passport/pkg/models"
+ "github.com/samber/lo"
+)
+
+func DetectRisk(user models.Account, ip, ua string) bool {
+ var secureFactor int64
+ if err := database.C.Where(models.AuthTicket{
+ AccountID: user.ID,
+ IpAddress: ip,
+ }).Model(models.AuthTicket{}).Count(&secureFactor).Error; err == nil {
+ if secureFactor >= 1 {
+ return false
+ }
+ }
+
+ return true
+}
+
+func NewTicket(user models.Account, ip, ua string) (models.AuthTicket, error) {
+ var ticket models.AuthTicket
+ if err := database.C.Where(models.AuthTicket{
+ AccountID: user.ID,
+ }).First(&ticket).Error; err == nil {
+ return ticket, nil
+ }
+
+ ticket = models.AuthTicket{
+ Claims: []string{"*"},
+ Audiences: []string{"passport"},
+ IpAddress: ip,
+ UserAgent: ua,
+ RequireMFA: DetectRisk(user, ip, ua),
+ ExpiredAt: lo.ToPtr(time.Now().Add(2 * time.Hour)),
+ AvailableAt: nil,
+ AccountID: user.ID,
+ }
+
+ err := database.C.Save(&ticket).Error
+
+ return ticket, err
+}
+
+func NewOauthTicket(
+ user models.Account,
+ client models.ThirdClient,
+ claims, audiences []string,
+ ip, ua string,
+) (models.AuthTicket, error) {
+ ticket := models.AuthTicket{
+ Claims: claims,
+ Audiences: audiences,
+ IpAddress: ip,
+ UserAgent: ua,
+ RequireMFA: DetectRisk(user, ip, ua),
+ GrantToken: lo.ToPtr(uuid.NewString()),
+ AccessToken: lo.ToPtr(uuid.NewString()),
+ RefreshToken: lo.ToPtr(uuid.NewString()),
+ AvailableAt: lo.ToPtr(time.Now()),
+ ExpiredAt: lo.ToPtr(time.Now()),
+ ClientID: &client.ID,
+ AccountID: user.ID,
+ }
+
+ if err := database.C.Save(&ticket).Error; err != nil {
+ return ticket, err
+ }
+
+ return ticket, nil
+}
+
+func ActiveTicketWithPassword(ticket models.AuthTicket, password string) (models.AuthTicket, error) {
+ if ticket.AvailableAt != nil {
+ return ticket, nil
+ } else if !ticket.RequireAuthenticate {
+ return ticket, fmt.Errorf("detected risk, multi factor authentication required")
+ }
+
+ if factor, err := GetPasswordFactor(ticket.AccountID); err != nil {
+ return ticket, fmt.Errorf("unable to active ticket: %v", err)
+ } else if err = CheckFactor(factor, password); err != nil {
+ return ticket, err
+ }
+
+ ticket.AvailableAt = lo.ToPtr(time.Now())
+
+ if !ticket.RequireAuthenticate && !ticket.RequireMFA {
+ ticket.AvailableAt = lo.ToPtr(time.Now())
+ }
+
+ if err := database.C.Save(&ticket).Error; err != nil {
+ return ticket, err
+ }
+
+ return ticket, nil
+}
+
+func ActiveTicketWithMFA(ticket models.AuthTicket, factor models.AuthFactor, code string) (models.AuthTicket, error) {
+ if ticket.AvailableAt != nil {
+ return ticket, nil
+ } else if !ticket.RequireMFA {
+ return ticket, nil
+ }
+
+ if err := CheckFactor(factor, code); err != nil {
+ return ticket, fmt.Errorf("invalid code: %v", err)
+ }
+
+ ticket.RequireMFA = false
+
+ if !ticket.RequireAuthenticate && !ticket.RequireMFA {
+ ticket.AvailableAt = lo.ToPtr(time.Now())
+ }
+
+ if err := database.C.Save(&ticket).Error; err != nil {
+ return ticket, err
+ }
+
+ return ticket, nil
+}
+
+func RegenSession(session models.AuthTicket) (models.AuthTicket, error) {
+ session.GrantToken = lo.ToPtr(uuid.NewString())
+ session.AccessToken = lo.ToPtr(uuid.NewString())
+ session.RefreshToken = lo.ToPtr(uuid.NewString())
+ err := database.C.Save(&session).Error
+ return session, err
+}
diff --git a/pkg/services/ticket_queries.go b/pkg/services/ticket_queries.go
new file mode 100644
index 0000000..6412868
--- /dev/null
+++ b/pkg/services/ticket_queries.go
@@ -0,0 +1,29 @@
+package services
+
+import (
+ "git.solsynth.dev/hydrogen/passport/pkg/database"
+ "git.solsynth.dev/hydrogen/passport/pkg/models"
+)
+
+func GetTicket(id uint) (models.AuthTicket, error) {
+ var ticket models.AuthTicket
+ if err := database.C.
+ Where(&models.AuthTicket{BaseModel: models.BaseModel{ID: id}}).
+ First(&ticket).Error; err != nil {
+ return ticket, err
+ }
+
+ return ticket, nil
+}
+
+func GetTicketWithToken(tokenId string) (models.AuthTicket, error) {
+ var ticket models.AuthTicket
+ if err := database.C.
+ Where(models.AuthTicket{AccessToken: &tokenId}).
+ Or(models.AuthTicket{RefreshToken: &tokenId}).
+ First(&ticket).Error; err != nil {
+ return ticket, err
+ }
+
+ return ticket, nil
+}
diff --git a/pkg/services/ticket_token.go b/pkg/services/ticket_token.go
new file mode 100644
index 0000000..7e08998
--- /dev/null
+++ b/pkg/services/ticket_token.go
@@ -0,0 +1,98 @@
+package services
+
+import (
+ "fmt"
+ "git.solsynth.dev/hydrogen/passport/pkg/database"
+ "git.solsynth.dev/hydrogen/passport/pkg/models"
+ "github.com/samber/lo"
+ "github.com/spf13/viper"
+ "strconv"
+ "time"
+)
+
+func GetToken(ticket models.AuthTicket) (string, string, error) {
+ var refresh, access string
+ if err := ticket.IsAvailable(); err != nil {
+ return refresh, access, err
+ }
+ if ticket.AccessToken == nil || ticket.RefreshToken == nil {
+ return refresh, access, fmt.Errorf("unable to encode token, access or refresh token id missing")
+ }
+
+ accessDuration := time.Duration(viper.GetInt64("access_token_duration")) * time.Second
+ refreshDuration := time.Duration(viper.GetInt64("refresh_token_duration")) * time.Second
+
+ var err error
+ sub := strconv.Itoa(int(ticket.AccountID))
+ sed := strconv.Itoa(int(ticket.ID))
+ access, err = EncodeJwt(*ticket.AccessToken, JwtAccessType, sub, sed, ticket.Audiences, time.Now().Add(accessDuration))
+ if err != nil {
+ return refresh, access, err
+ }
+ refresh, err = EncodeJwt(*ticket.RefreshToken, JwtRefreshType, sub, sed, ticket.Audiences, time.Now().Add(refreshDuration))
+ if err != nil {
+ return refresh, access, err
+ }
+
+ ticket.LastGrantAt = lo.ToPtr(time.Now())
+ database.C.Save(&ticket)
+
+ return access, refresh, nil
+}
+
+func ExchangeToken(token string) (string, string, error) {
+ var ticket models.AuthTicket
+ if err := database.C.Where(models.AuthTicket{GrantToken: &token}).First(&ticket).Error; err != nil {
+ return "", "", err
+ } else if ticket.LastGrantAt != nil {
+ return "", "", fmt.Errorf("ticket was granted the first token, use refresh token instead")
+ } else if len(ticket.Audiences) > 1 {
+ return "", "", fmt.Errorf("should use authorization code grant type")
+ }
+
+ return GetToken(ticket)
+}
+
+func ExchangeOauthToken(clientId, clientSecret, redirectUri, token string) (string, string, error) {
+ var client models.ThirdClient
+ if err := database.C.Where(models.ThirdClient{Alias: clientId}).First(&client).Error; err != nil {
+ return "", "", err
+ } else if client.Secret != clientSecret {
+ return "", "", fmt.Errorf("invalid client secret")
+ } else if !client.IsDraft && !lo.Contains(client.Callbacks, redirectUri) {
+ return "", "", fmt.Errorf("invalid redirect uri")
+ }
+
+ var ticket models.AuthTicket
+ if err := database.C.Where(models.AuthTicket{GrantToken: &token}).First(&ticket).Error; err != nil {
+ return "", "", err
+ } else if ticket.LastGrantAt != nil {
+ return "", "", fmt.Errorf("ticket was granted the first token, use refresh token instead")
+ }
+
+ return GetToken(ticket)
+}
+
+func RefreshToken(token string) (string, string, error) {
+ parseInt := func(str string) int {
+ val, _ := strconv.Atoi(str)
+ return val
+ }
+
+ var ticket models.AuthTicket
+ if claims, err := DecodeJwt(token); err != nil {
+ return "404", "403", err
+ } else if claims.Type != JwtRefreshType {
+ return "404", "403", fmt.Errorf("invalid token type, expected refresh token")
+ } else if err := database.C.Where(models.AuthTicket{
+ BaseModel: models.BaseModel{ID: uint(parseInt(claims.SessionID))},
+ }).First(&ticket).Error; err != nil {
+ return "404", "403", err
+ }
+
+ if ticket, err := RegenSession(ticket); err != nil {
+ return "404", "403", err
+ } else {
+ return GetToken(ticket)
+ }
+}