Compare commits

...

237 Commits

Author SHA1 Message Date
d35e5813b5 feat: beta version of role management for single user 2025-07-20 17:59:54 +02:00
533e6ea6af fix: no avatar handling 2025-06-30 00:08:25 +02:00
0c24ed9382 feat: check role assignment 2025-06-30 00:08:14 +02:00
f5c61bb6a0 feat: show roles 2025-06-29 23:20:59 +02:00
eb05f830fe feat: roles & group state 2025-06-29 23:20:50 +02:00
d86a9de388 feat: assign system roles 2025-06-29 23:19:05 +02:00
d80caac81b feat: roles API + type def 2025-06-25 11:56:06 +02:00
5d49a661ed feat: add roles & groups page 2025-06-25 11:55:52 +02:00
635a2d6058 feat: roles & groups page 2025-06-25 11:55:41 +02:00
1eb96e906d feat: create system roles 2025-06-25 11:55:27 +02:00
58974d9789 feat: add scope to the role 2025-06-25 11:55:04 +02:00
329fac415f feat: get all roles endpoint 2025-06-25 11:54:45 +02:00
a83ec61fae fix: avoid null value 2025-06-25 11:54:19 +02:00
d5e86acacf fix: import of user permissions API 2025-06-24 19:05:21 +02:00
221ef192bc feat: permissions store 2025-06-24 19:05:07 +02:00
18664dbd8b feat: remove image borders for service 2025-06-24 19:04:59 +02:00
d097735965 fix: remove unnecessary test fethcing 2025-06-24 19:04:48 +02:00
992776b8a6 feat: new nav item for app permissions 2025-06-24 19:04:26 +02:00
3f260b9029 feat: add API for fetching ALL permissions 2025-06-24 19:04:15 +02:00
65f40d0897 feat: app permissions admin page 2025-06-24 19:04:01 +02:00
6d482c784f feat: admin fetch all permissions 2025-06-24 19:03:45 +02:00
dc07868d15 feat: call permissions ensure 2025-06-24 19:01:26 +02:00
09a2f05ee5 feat: fetch permissions + grouped fetching 2025-06-24 19:01:16 +02:00
5cec1cf561 feat: ensure system permissions 2025-06-24 19:00:36 +02:00
3281764eff feat: display user's raw permissions 2025-06-24 14:37:25 +02:00
868337134d feat: get permissions call 2025-06-24 12:59:10 +02:00
9372673bf1 test: get user permissions endpoint 2025-06-24 12:58:29 +02:00
0eea81b42f feat: update repo with group, roles and permissions 2025-06-24 12:58:14 +02:00
7468303e41 feat: make name + scope combi unique 2025-06-24 12:18:54 +02:00
8745e7d8bc feat: group role permission 2025-06-17 12:21:02 +02:00
bdc42beb27 Merge pull request 'sessions' (#2) from sessions into main
Reviewed-on: #2
2025-06-16 19:03:01 +02:00
72083fa0a4 hot+fix: round expires in number 2025-06-16 19:01:28 +02:00
b3ad13e55d feat: ignore .env.remote 2025-06-15 22:16:17 +02:00
0db54e0268 feat: update service session on refresh 2025-06-15 21:13:33 +02:00
b3ef96a0ce feat: update service session's tokens 2025-06-15 21:13:23 +02:00
a773f1f8b4 feat: signed token type 2025-06-15 21:05:09 +02:00
1a71f50914 fix: 'boolean' instead of 'bool' 2025-06-15 21:05:03 +02:00
d17e154e42 feat: logical columns for service sessions table 2025-06-15 21:04:50 +02:00
bad26775eb fix: don't redirect due to credentials modal 2025-06-15 21:04:40 +02:00
c3fd6637a5 fix: numeration 2025-06-15 21:04:22 +02:00
20173ea140 fix: numeration 2025-06-15 21:04:07 +02:00
41d439beab feat: create service session 2025-06-15 21:02:38 +02:00
b36b6e18ca feat: use signed token from types 2025-06-15 21:02:22 +02:00
1765485027 feat: decrease inner spacing 2025-06-15 19:41:05 +02:00
c5fb5e674a feat: revoke service session 2025-06-15 19:40:54 +02:00
03bf655051 feat: service sessions feature 2025-06-15 19:36:02 +02:00
f8589fce5d feat: refetch sessions 2025-06-15 19:29:03 +02:00
ac62158de9 feat: pagination + session revoking UI 2025-06-15 19:27:02 +02:00
44e1a18e9a feat: user sessions state 2025-06-15 19:26:52 +02:00
e0f2c3219f feat: message populating from responswe 2025-06-15 19:26:43 +02:00
d2d52dc041 feat: pagination + revoke session API 2025-06-15 19:26:27 +02:00
d7d142152c feat: repo update 2025-06-15 19:26:18 +02:00
5c321311cd feat: pagination support + fix: able to get inactive session 2025-06-15 19:26:01 +02:00
ffc8a5f44d feat: session verification 2025-06-15 19:25:21 +02:00
dbff94e7b3 feat: pass repo access to middleware 2025-06-15 19:25:13 +02:00
0b1ef77689 feat: user/service sessions type 2025-06-15 18:10:47 +02:00
b0005c6702 feat: add new nav item for user sessions 2025-06-15 18:10:31 +02:00
c2abf1a5ba feat: add admin sessions page 2025-06-15 18:10:21 +02:00
ac50929e6e feat: ui pagination component draft 2025-06-15 18:10:11 +02:00
32785398ca feat: user sessions list page 2025-06-15 18:10:00 +02:00
cc497b6016 feat: sessions API 2025-06-15 18:09:48 +02:00
97ffcbdaa4 feat: sessions endpoints 2025-06-15 18:09:26 +02:00
d09bf8ff02 feat: install moment 2025-06-15 18:09:08 +02:00
c0814093e5 feat: get user sessions joined with user data 2025-06-15 18:08:58 +02:00
d48519741d feat: add user-sessions routes 2025-06-15 18:08:44 +02:00
213991126d feat: move api services dto into separate types file 2025-06-15 18:08:34 +02:00
c7e88606e3 feat: return status of request 2025-06-13 21:46:06 +02:00
a0d506fb76 feat: navigate to list page after successful create 2025-06-13 21:45:53 +02:00
0ec7743fca feat: delimiter handling/support 2025-06-13 21:45:39 +02:00
a8a0fa55b7 feat: delimiter def 2025-06-13 21:45:32 +02:00
7321448ce7 fix: reload accounts 2025-06-11 21:06:36 +02:00
6d5e0fc9a9 feat: signout API 2025-06-11 21:00:22 +02:00
ef05d66787 feat: register signout route 2025-06-11 20:52:30 +02:00
b3296c45ad feat: get request JTI helper 2025-06-11 20:52:22 +02:00
7fd163f957 feat: signout endpoitn 2025-06-11 20:51:39 +02:00
0f0d50a684 feat: set token jti in request 2025-06-11 20:39:34 +02:00
68074e02bc feat: jti request key 2025-06-11 20:39:20 +02:00
8d38a86f86 feat: get client ip util 2025-06-11 20:35:38 +02:00
e0c095c24d feat: create/update session when refreshing 2025-06-11 20:34:56 +02:00
4c318b15cd feat: refactor login 2025-06-11 20:33:49 +02:00
5ea6bc4251 feat: build device info util 2025-06-11 20:33:34 +02:00
1cbe908489 fix: use printf 2025-06-11 18:49:39 +02:00
53ee156e67 feat: get location util 2025-06-11 18:48:54 +02:00
07a936acc7 feat: device info type 2025-06-11 18:48:48 +02:00
f892f0da24 feat: update session + ps type overriding 2025-06-11 18:48:41 +02:00
38955ee4e6 fix: use the token 2025-06-11 18:48:08 +02:00
7fa7e87e88 feat: token sign with meta data 2025-06-11 18:47:59 +02:00
f085f2e271 feat: create user session 2025-06-11 18:47:45 +02:00
08add259a4 feat: install uasurfer 2025-06-11 18:47:34 +02:00
5b6142dfa6 feat: user/service sessions repo 2025-06-10 19:46:37 +02:00
dc41521a99 fix+feat: use verify oauth client helper in token as well 2025-06-09 15:55:36 +02:00
299e7eddc4 feat: verify api service in code generation 2025-06-09 15:39:56 +02:00
b4699e987c feat: service and user session queries 2025-06-08 22:59:33 +02:00
be9d4f2a1b feat: user and service sessions 2025-06-08 22:59:24 +02:00
db99236501 Merge branch 'main' of git.adalspace.com:admin/hspguard 2025-06-08 17:00:36 +02:00
e33fb04c99 feat: specify supported grant tyoes 2025-06-08 16:59:49 +02:00
c3d4208e12 Update README.md 2025-06-08 16:43:13 +02:00
3f945fa329 Update README.md 2025-06-08 16:38:26 +02:00
93a5cd7c70 feat: center icon 2025-06-07 22:08:23 +02:00
951de989af fix: remove unnecessary double message 2025-06-07 22:07:38 +02:00
c5cf253a15 feat: log error when failing to put object 2025-06-07 22:07:28 +02:00
d70032e36d feat: copy redis.conf 2025-06-07 22:07:16 +02:00
445ac50537 feat: list of permissions 2025-06-07 19:50:51 +02:00
c13e564b01 feat: icon_url field in api service 2025-06-07 19:47:31 +02:00
5d3a77133d feat: fetch api service state 2025-06-07 19:47:22 +02:00
44592ebc08 feat: show api service's information 2025-06-07 19:47:12 +02:00
1b941cb0c3 fix: correct uri for fetching api service 2025-06-07 19:47:02 +02:00
1cb520c2b6 feat: expose api services fetching by client id 2025-06-07 19:46:34 +02:00
b3fdd3bc18 feat: expose icon url 2025-06-07 19:46:25 +02:00
9110db2f08 feat: api service fetch by client id api 2025-06-07 19:23:42 +02:00
5972735102 fix: separate pathname for getting api service by client id 2025-06-07 19:23:34 +02:00
14c69349cc feat: get api service by client handler and endpoint 2025-06-07 19:22:44 +02:00
3ceeab04e1 feat: add icon url to api service 2025-06-07 19:21:14 +02:00
b7a67c208f feat: pass client id by code generation 2025-06-07 19:17:48 +02:00
cee885a84d feat: remove unused access to user profile 2025-06-07 19:17:39 +02:00
4b496ea9bd feat: split claims into 2 2025-06-07 19:17:29 +02:00
570ae6ac8c feat: generate 3 tokens for api service 2025-06-07 19:17:15 +02:00
f0d3a61e7b feat: accept access to cache 2025-06-07 19:16:57 +02:00
b09567620f feat: generate auth code and save 2025-06-07 19:16:45 +02:00
2209846525 feat: check for redirect uris allowed 2025-06-07 19:16:28 +02:00
108ed61961 feat: save & get auth code cache 2025-06-07 19:16:14 +02:00
b73bfd590b feat: allow custom claims for verify 2025-06-07 19:15:59 +02:00
19d56159ba feat: pass access to cache 2025-06-07 19:15:00 +02:00
b9ccf6adac feat: require client id for code API 2025-06-07 18:46:46 +02:00
9a0870dbbc fix: let user see verify welcome message 2025-06-07 11:41:52 +02:00
70429f69a2 feat: reset verify states after completion 2025-06-07 11:17:10 +02:00
ad635008eb fix: scrolling + background + height on devices 2025-06-07 02:37:03 +02:00
eacc8fdd89 fix: no need for h-full 2025-06-07 02:21:17 +02:00
d309fb3f57 fix: better scrolling on mobile 2025-06-07 02:21:06 +02:00
f4fd993679 feat: admin default page is api-services 2025-06-07 02:20:41 +02:00
016879b53f feat: upload avatar API 2025-06-07 02:10:55 +02:00
70bba15cda feat: redirect + upload avatar store 2025-06-07 02:10:46 +02:00
57daf175ab fix: fallback to empty array 2025-06-07 02:10:35 +02:00
0817a65272 feat: final verify trigger 2025-06-07 02:10:24 +02:00
13f9da1a67 feat: handle avatar uploading on verify 2025-06-07 02:10:11 +02:00
83535acf1c feat: no users found text in table 2025-06-07 02:09:59 +02:00
441ce2daca feat: redirection after verification 2025-06-07 02:09:44 +02:00
f9848d2110 feat: set redirect in state 2025-06-07 02:09:35 +02:00
7f0511b0d4 feat: finish verification API 2025-06-07 02:09:20 +02:00
a27f2ad593 feat: verify user's avatar + full url for profile picture 2025-06-07 02:09:10 +02:00
715a984241 feat: register finish verify route 2025-06-07 02:08:52 +02:00
66e1756ade feat: finish verification handler 2025-06-07 02:08:43 +02:00
849403a137 feat: confirm OTP integration 2025-06-07 01:34:48 +02:00
8d15c9b8b2 feat: request OTP query 2025-06-07 01:34:38 +02:00
87af1834cf feat: email verify state 2025-06-07 01:34:28 +02:00
357583f54d feat: navigate to current stage of verification 2025-06-07 01:34:13 +02:00
aa6de76ded feat: redirect to verify pages 2025-06-07 01:33:50 +02:00
ab3c2d1eb0 feat: stepper improvements 2025-06-07 01:33:40 +02:00
644cf2a358 feat: email verify APIs 2025-06-07 01:33:21 +02:00
5b1ed9925d feat: verify routes 2025-06-07 01:33:14 +02:00
4071a50a37 feat: split auth routes in files 2025-06-07 01:32:56 +02:00
dd7c51efd8 feat: update user verifications 2025-06-07 01:32:37 +02:00
8902f4d187 feat: redis configuration & client 2025-06-07 01:32:22 +02:00
6666b20464 feat: go-redis pkg 2025-06-07 01:31:55 +02:00
c5f288ba1e feat: redis configuration 2025-06-07 01:31:47 +02:00
cc49ab1655 feat: verify state 2025-06-07 00:14:09 +02:00
06c60b3491 feat: verification layout 2025-06-07 00:14:03 +02:00
b584a7b07f feat: UI stepper component 2025-06-07 00:13:54 +02:00
410e420a46 feat: use dark background overlay 2025-06-07 00:13:45 +02:00
eeb0f6eac1 feat: user profile verification fields 2025-06-07 00:13:29 +02:00
fb622f918a feat: redirect to verify when required 2025-06-07 00:13:17 +02:00
a50bad417f feat: mask email util 2025-06-07 00:12:56 +02:00
c395729446 feat: register verification pages 2025-06-07 00:12:50 +02:00
eaa92d2fe4 feat: verification pages 2025-06-07 00:12:41 +02:00
a9e382d713 feat: new dark overlay img 2025-06-07 00:12:22 +02:00
974244025e feat: remove unused @emotion/react 2025-06-07 00:12:04 +02:00
ae41076673 feat: move UserDTO logic into single file 2025-06-07 00:11:46 +02:00
cc7f7f40c4 feat: add verification levels 2025-06-07 00:11:02 +02:00
e2ae03f2a6 feat: use avatar directly as URI 2025-06-06 12:37:34 +02:00
9319564dea feat: image full URI in avatar 2025-06-06 12:12:03 +02:00
83e3e5a2e9 feat: new env variable for server URI 2025-06-06 12:04:25 +02:00
2b40e4e922 feat: specify user creator + list only users related to admin 2025-06-06 11:58:47 +02:00
ed33d03fda feat: find users related to specific admin repo 2025-06-06 11:58:20 +02:00
34c152a459 fix: correct uri for user creation page 2025-06-05 20:50:44 +02:00
ad09e98bba feat: create user page 2025-06-05 20:50:27 +02:00
d3fd5cba16 feat: create user state 2025-06-05 20:49:52 +02:00
64dbb4368c feat: create user API 2025-06-05 20:49:43 +02:00
cb3a6ddc58 feat: add support for new fields in user table 2025-06-05 20:49:35 +02:00
e774f415d8 feat: craete user route 2025-06-05 20:49:20 +02:00
d5a22895e7 feat: user creator and verified columns 2025-06-05 20:49:05 +02:00
9983c51e3a fix: use fetch for single item 2025-06-04 21:12:30 +02:00
6a1fc193f4 fix: double scrollbar on desktop 2025-06-04 20:07:58 +02:00
118877f727 feat: user update last login 2025-06-04 20:07:48 +02:00
7b8fe6baf2 fix: set content-type to json 2025-06-04 20:07:39 +02:00
e85b23b3e8 feat: admin view user page 2025-06-04 19:33:41 +02:00
6164b77bee fix: avatar explicit color for icon 2025-06-04 19:33:28 +02:00
8b5a5744ab feat: get single user state 2025-06-04 19:33:12 +02:00
d9e9c5ab38 feat: get single user API 2025-06-04 19:33:07 +02:00
0dcef81b59 feat: get single user endpoint 2025-06-04 19:32:56 +02:00
426b70a1de feat: AdminUsersPage implementation 2025-06-04 19:17:20 +02:00
912973cdb5 feat: admin users state 2025-06-04 19:17:08 +02:00
e4ff799f05 feat: background enhancements 2025-06-04 19:16:48 +02:00
320715f5aa feat: move api services related pages to own folder 2025-06-04 19:11:51 +02:00
a3b04b6243 fix: correct spelling for is_admin 2025-06-04 19:11:28 +02:00
f610d7480f feat: register user related pages 2025-06-04 19:11:10 +02:00
11ac92a026 feat: dynamic user based roles 2025-06-04 19:11:01 +02:00
98ae3e06e9 feat: longer live time for token + correct user based role 2025-06-04 19:10:32 +02:00
a1146ce371 feat: separate file for api services store 2025-06-04 12:49:57 +02:00
a67ec7e78c fix: rename useAdmin to useApiServices 2025-06-04 12:46:48 +02:00
9895392b50 feat: fetch users API 2025-06-04 12:46:36 +02:00
c6998f33e1 feat: move user dto outside 2025-06-04 12:46:24 +02:00
81659181e4 feat: add get users route 2025-06-04 12:33:58 +02:00
849b5935c2 fix: type overriding 2025-06-04 12:33:22 +02:00
c27d837ab0 feat: get users endpoint 2025-06-04 12:33:06 +02:00
92e9b87227 feat: better types overriding 2025-06-04 12:32:47 +02:00
243b7cce33 fix: avatars width setting 2025-06-03 13:03:02 +02:00
76d960619f feat: api service edit page 2025-06-03 12:59:05 +02:00
3e59c78287 feat: api services updated modal 2025-06-03 12:58:57 +02:00
6cd9da69ab feat: update api service state 2025-06-03 12:58:40 +02:00
29b97a87b3 feat: link to edit page 2025-06-03 12:57:36 +02:00
8bc4603274 feat: update api service API 2025-06-03 12:54:20 +02:00
cc60a1ba86 feat: register edit service page in router 2025-06-03 12:54:10 +02:00
89c7dc43e5 feat: use updateapiservice endpoint 2025-06-03 12:53:57 +02:00
95c330568d feat: UI bind api service toggling 2025-06-03 00:07:09 +02:00
900d314a95 feat: don't require view service id 2025-06-03 00:07:00 +02:00
0d8a3b1b39 feat: store toggle active api service 2025-06-03 00:05:43 +02:00
4b7396c210 feat: toggle api service API 2025-06-03 00:05:32 +02:00
d4e2cbdd4f feat: activate api service query 2025-06-03 00:05:20 +02:00
5024ac8151 feat: register toggle endpoint 2025-06-03 00:05:05 +02:00
3bf08c5933 feat: toggle api service endpoint 2025-06-03 00:04:36 +02:00
b42da50306 feat: logout on error when refreshing token 2025-06-02 23:45:03 +02:00
0efc90567b feat: integrate oauth store in authorize page 2025-06-02 23:16:11 +02:00
a5466f1b10 feat: remove use of non existent oauth provider 2025-06-02 23:16:00 +02:00
8e946cbee5 feat: parse url search params on load in authlayout 2025-06-02 23:15:49 +02:00
a3a6b5e4d7 feat: create oauth store 2025-06-02 23:15:35 +02:00
ad0a0f5626 feat: remove context use 2025-06-02 23:15:29 +02:00
2389058ddc fix: use logout in both waiting and actual refreshing for/of the token 2025-06-02 23:15:21 +02:00
ce44ef3e62 feat: protect required endpoints by oauth 2025-06-02 23:15:02 +02:00
9ee30d1e23 feat: register /authorize route 2025-06-02 20:35:03 +02:00
886d0a7f5c feat: create middleware endpoint before accessing web ui interface for
authorize
2025-06-02 20:34:36 +02:00
cfdf419460 fix: use slices.Contains 2025-06-02 20:34:20 +02:00
930e069aee feat: authorize middleware to check api service activity 2025-06-02 13:00:19 +02:00
1ef261660f feat: split oauth endpoints into files 2025-06-02 12:31:01 +02:00
5d9e5d27bf fix: correct authorize uri 2025-06-01 21:22:16 +02:00
3f8a4024ce feat: .env file is optional 2025-06-01 12:55:05 +02:00
e8a74999c3 feat: dockerfile for image building 2025-06-01 12:54:44 +02:00
dc2ce1f349 fix: finish update service endpoint 2025-06-01 08:29:30 +02:00
132 changed files with 8393 additions and 1281 deletions

View File

@@ -1,9 +1,12 @@
GUARD_PORT=3001 GUARD_PORT=3001
GUARD_HOST="127.0.0.1" GUARD_HOST="127.0.0.1"
GUARD_URI="http://localhost:3001"
GUARD_DB_URL="postgres://<user>:<user>@<host>:<port>/<db>?sslmode=disable" GUARD_DB_URL="postgres://<user>:<user>@<host>:<port>/<db>?sslmode=disable"
GUARD_REDIS_URL="redis://guard:guard@localhost:6379/0"
GUARD_ADMIN_NAME="admin" GUARD_ADMIN_NAME="admin"
GUARD_ADMIN_EMAIL="admin@test.net" GUARD_ADMIN_EMAIL="admin@test.net"
GUARD_ADMIN_PASSWORD="secret" GUARD_ADMIN_PASSWORD="secret"
@@ -11,7 +14,6 @@ GUARD_ADMIN_PASSWORD="secret"
GUARD_JWT_PRIVATE="rsa" GUARD_JWT_PRIVATE="rsa"
GUARD_JWT_PUBLIC="rsa" GUARD_JWT_PUBLIC="rsa"
GUARD_JWT_KID="my-rsa-key-1" GUARD_JWT_KID="my-rsa-key-1"
GUARD_JWT_ISSUER="http://localhost:3001"
GUARD_MINIO_ENDPOINT="localhost:9000" GUARD_MINIO_ENDPOINT="localhost:9000"
GUARD_MINIO_ACCESS_KEY="" GUARD_MINIO_ACCESS_KEY=""
@@ -20,5 +22,3 @@ GUARD_MINIO_SECRET_KEY=""
GOOSE_DRIVER="postgres" GOOSE_DRIVER="postgres"
GOOSE_DBSTRING=$DATABASE_URL GOOSE_DBSTRING=$DATABASE_URL
GOOSE_MIGRATION_DIR="./migrations" GOOSE_MIGRATION_DIR="./migrations"

1
.gitignore vendored
View File

@@ -27,6 +27,7 @@ go.work.sum
# env file # env file
.env .env
.env.remote
# key files # key files
*.pem *.pem

55
Dockerfile Normal file
View File

@@ -0,0 +1,55 @@
# Stage 1: Build frontend
FROM node:22 AS frontend-builder
WORKDIR /app/web
COPY web/ .
RUN npm install && npm run build
# Stage 2: Build backend
FROM golang:1.24 AS backend-builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# Copy built frontend into Go embed path (adjust if needed)
# COPY --from=frontend-builder /app/web/dist ./web/dist
RUN CGO_ENABLED=0 GOOS=linux make build
# Stage 3: Final image
FROM debian:bookworm-slim
WORKDIR /app
# Install CA certificates for HTTPS
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=backend-builder /app/bin/hspguard .
COPY --from=frontend-builder /app/dist ./dist
COPY redis.conf /config/redis.conf
# Optional: copy default .env file if used
# COPY .env .env
# Set environment variables (can be overridden at runtime)
ENV ENV=production \
GUARD_PORT=3001 \
GUARD_HOST="127.0.0.1" \
GUARD_URI="http://localhost:3001" \
GUARD_DB_URL="postgres://user:user@localhost:5432/db?sslmode=disable" \
GUARD_ADMIN_NAME="admin" \
GUARD_ADMIN_EMAIL="admin@test.net" \
GUARD_ADMIN_PASSWORD="secret" \
GUARD_JWT_PRIVATE="rsa" \
GUARD_JWT_PUBLIC="rsa" \
GUARD_JWT_KID="my-rsa-key-1" \
GUARD_MINIO_ENDPOINT="localhost:9000" \
GUARD_MINIO_ACCESS_KEY="" \
GUARD_MINIO_SECRET_KEY="" \
GOOSE_DRIVER="postgres" \
GOOSE_DBSTRING=$GUARD_DB_URL \
GOOSE_MIGRATION_DIR="./migrations"
EXPOSE 3001
CMD ["./hspguard"]

168
README.md
View File

@@ -1,123 +1,139 @@
# HSP Guard
**HSP Guard** is an internal security service for your home lab, designed to manage user access to various home services and tools. It dynamically controls permissions and prevents unauthorized or unexpected users from accessing sensitive services. # 🛡️ HSP Guard
**HSP Guard** is a modern OpenID Connect (OIDC) identity provider and access management system for home labs. It provides secure authentication and granular authorization for all your self-hosted services, combining ease of use with enterprise-level control — without any vendor lock-in.
--- ---
## 📌 Overview ## ✨ Features
HSP Guard authorizes user requests and provides an efficient way to: - **OIDC Provider**: Central login for your home lab apps
- **Admin UI**: Manage apps, users, roles, permissions, and sessions
- Manage permissions for individual services/tools - **API Tokens**: Issue access tokens with embedded roles and permissions
- Define roles for easier access control - **Flexible Authorization**: Support for roles, permissions, and groups (future)
- Validate and authorize users via JWT tokens - **App Registration**: Register OAuth/OIDC clients with custom permissions
- Securely integrate with new services during installation - **Automatic Permission Sync**: Optionally fetch app permissions from `/.well-known/guard-configuration`
- **User & Admin Sessions**: See and revoke active user/app sessions
- **Pluggable**: Easily integrate new apps and services
- **Audit Logging**: Track actions for security and troubleshooting (planned)
--- ---
## 📚 Concepts ## 🚀 Getting Started
### 🔐 Permission ### 1. **Run HSP Guard**
Permissions define access to specific features or tools. You can run HSP Guard via Docker, Docker Compose, or natively (see below).
By default, HSP Guard includes predefined administrative permissions that allow an admin to log in and configure the system.
Once logged in, the admin can: ### 2. **Register Your First App**
- Manually create new permissions for specific applications 1. **Login as admin**
- Allow new applications to register their own permissions 2. Go to **Apps → Register New App**
- Assign permissions to users, granting them access to corresponding tools 3. Enter:
- **Name** of your app
- **Redirect URIs** (for OIDC/OAuth callbacks)
- (Optional) **Permissions** (manual or auto-discovered from the app)
4. Save to receive a `client_id` and `client_secret`
5. Configure your app to use these for OIDC login
### 3. **Assign Permissions & Roles**
- Assign **default roles** to new users automatically (configurable)
- Create custom **roles** to bundle permissions (e.g., `FAMILY_MEMBER`)
- Assign users to roles and/or groups for flexible access control
--- ---
### 🧩 Role ## 🏗️ Concepts
A **Role** is a named collection of permissions (e.g., `GUEST`, `FRIEND`, `FAMILY_MEMBER`) created by the admin. ### 🔑 **Permissions**
Roles simplify user management by allowing bulk assignment of permissions. Instead of assigning multiple permissions individually, a role bundles them under one label. Fine-grained controls for app features (e.g., `music.play`, `dashboard.edit`).
Can be manually defined or auto-discovered from an apps `.well-known/guard-configuration` endpoint.
### 🧩 **Roles**
Named bundles of permissions (e.g., `GUEST`, `FAMILY_MEMBER`, `ADMIN`).
Assign to users/groups for easier management.
### 👥 **Groups**
(Planned) Logical user collections (e.g., “Family”, “Guests”, “Admins”) for batch management of roles/permissions.
### 👤 **Users**
Each user has a unique profile, roles, and group memberships.
--- ---
### 👥 Group *(Coming Soon)* ## 🔗 OIDC/OAuth Integration
This feature is planned for future releases. Groups will help organize users or services into logical clusters for simplified access control. **HSP Guard** is a standard-compliant OIDC Provider. Any app supporting OIDC/OAuth can integrate.
- Register app in admin panel to get `client_id` & `client_secret`
- Configure your apps OIDC integration (see your apps docs)
- Token claims include `permissions` and `roles` for easy authorization
#### **Example Token Claims**
```json
{
"sub": "123456",
"name": "Alex Example",
"email": "alex@example.com",
"roles": ["GUEST"],
"permissions": ["dashboard.view", "music.play"]
}
```
--- ---
## 📡 API ## 📡 **App Permission Discovery**
### ✅ User Authorization If your app supports permission discovery:
- Expose `/.well-known/guard-configuration` endpoint listing available permissions
- When registering in HSP Guard, auto-fetch and display for approval
To verify whether a request is made by a valid and authorized user, applications can require a **JWT token** as part of the request. #### **Example guard-configuration JSON**
This token is sent to HSP Guard, which: ```json
{
- Validates the token "permissions": [
- Returns user details (e.g., ID, name, email) for logging, auditing, or request tracing "dashboard.view",
"dashboard.edit",
"dashboard.admin"
]
}
```
--- ---
### 🔑 Permission Checking ## 🔄 **User & Admin Sessions**
Applications can also verify whether a user holds specific permissions before granting access to certain services or features. - List all active sessions (browser, app, device, timestamp)
- Revoke sessions (logout) from user or admin panel
To do this, an app sends:
- The user's JWT token
- A list of required permissions
HSP Guard checks the users assigned permissions and responds with the authorization status.
> **Best Practice:** Applications should directly integrate with HSP Guard to enforce permission-based access control.
--- ---
## 🔄 User Authorization Flow ## 📦 **Planned Features & Roadmap**
When a user tries to access a home lab service that requires authentication: - [ ] **Group Management** for batch assignments
- [ ] **Audit Logging** of all admin/user actions
1. The application will **offer an authorization URL** to the user. - [ ] **Permission Expiry** (time-limited access)
2. The user follows the URL and is taken to the **HSP Guard login page**. - [ ] **Advanced Web UI** (dark mode, mobile)
3. The user selects or signs into an account they wish to use for that service. - [ ] **External Identity Providers** (login with Google, GitHub, etc.)
4. Once authenticated and authorized, the user is redirected to the **application-defined redirect URL**.
5. The application can now:
- Retrieve a **JWT token** from the redirect callback
- **Optionally cache the session/token** to avoid prompting the user every time
This process is similar to how external identity providers like **Google Sign-In** or **GitHub OAuth** work — providing a seamless and secure authentication experience for the user.
--- ---
## Integrating New Services & Tools ## 🛠 **Development**
When a new service or tool is installed: - See [CONTRIBUTING.md](CONTRIBUTING.md) for how to contribute!
- Pull requests and issues are welcome.
1. It provides a configuration file to HSP Guard
2. Guard extracts and registers any defined permissions
3. These permissions are **isolated** — even if a name overlaps with existing permissions, a prefix is added to avoid conflicts
--- ---
## 👤 User Registration & Onboarding ## 📝 **License**
New users (e.g., family, friends, guests) must complete a registration process to access your home lab. MIT — open source, for the home lab community.
They can:
- Visit a user-friendly registration webpage
- Fill out a form with basic information (name, email, password, etc.)
Once registered, the admin can assign roles or individual permissions as needed.
--- ---
## 🚧 Roadmap ## 💬 **Feedback**
- [ ] Group Management Open an [issue](https://github.com/yourusername/hsp-guard/issues) or join the discussion!
- [ ] Web UI Enhancements
- [ ] Audit Logging
- [ ] Permission Expiry & Time-Based Access
--- ---
## 📬 Feedback & Contribution
Feel free to open an issue or pull request if youd like to contribute or report bugs. HSP Guard is a personal home lab project, but feedback is always welcome!

View File

@@ -8,6 +8,7 @@ import (
"gitea.local/admin/hspguard/internal/admin" "gitea.local/admin/hspguard/internal/admin"
"gitea.local/admin/hspguard/internal/auth" "gitea.local/admin/hspguard/internal/auth"
"gitea.local/admin/hspguard/internal/cache"
"gitea.local/admin/hspguard/internal/config" "gitea.local/admin/hspguard/internal/config"
"gitea.local/admin/hspguard/internal/oauth" "gitea.local/admin/hspguard/internal/oauth"
"gitea.local/admin/hspguard/internal/repository" "gitea.local/admin/hspguard/internal/repository"
@@ -21,14 +22,16 @@ type APIServer struct {
addr string addr string
repo *repository.Queries repo *repository.Queries
storage *storage.FileStorage storage *storage.FileStorage
cache *cache.Client
cfg *config.AppConfig cfg *config.AppConfig
} }
func NewAPIServer(addr string, db *repository.Queries, minio *storage.FileStorage, cfg *config.AppConfig) *APIServer { func NewAPIServer(addr string, db *repository.Queries, minio *storage.FileStorage, cache *cache.Client, cfg *config.AppConfig) *APIServer {
return &APIServer{ return &APIServer{
addr: addr, addr: addr,
repo: db, repo: db,
storage: minio, storage: minio,
cache: cache,
cfg: cfg, cfg: cfg,
} }
} }
@@ -41,13 +44,13 @@ func (s *APIServer) Run() error {
// staticDir := http.Dir(filepath.Join(workDir, "static")) // staticDir := http.Dir(filepath.Join(workDir, "static"))
// FileServer(router, "/static", staticDir) // FileServer(router, "/static", staticDir)
oauthHandler := oauth.NewOAuthHandler(s.repo, s.cfg) oauthHandler := oauth.NewOAuthHandler(s.repo, s.cache, s.cfg)
router.Route("/api/v1", func(r chi.Router) { router.Route("/api/v1", func(r chi.Router) {
userHandler := user.NewUserHandler(s.repo, s.storage, s.cfg) userHandler := user.NewUserHandler(s.repo, s.storage, s.cfg)
userHandler.RegisterRoutes(r) userHandler.RegisterRoutes(r)
authHandler := auth.NewAuthHandler(s.repo, s.cfg) authHandler := auth.NewAuthHandler(s.repo, s.cache, s.cfg)
authHandler.RegisterRoutes(r) authHandler.RegisterRoutes(r)
oauthHandler.RegisterRoutes(r) oauthHandler.RegisterRoutes(r)

View File

@@ -4,8 +4,10 @@ import (
"context" "context"
"fmt" "fmt"
"log" "log"
"os"
"gitea.local/admin/hspguard/cmd/hspguard/api" "gitea.local/admin/hspguard/cmd/hspguard/api"
"gitea.local/admin/hspguard/internal/cache"
"gitea.local/admin/hspguard/internal/config" "gitea.local/admin/hspguard/internal/config"
"gitea.local/admin/hspguard/internal/repository" "gitea.local/admin/hspguard/internal/repository"
"gitea.local/admin/hspguard/internal/storage" "gitea.local/admin/hspguard/internal/storage"
@@ -16,9 +18,8 @@ import (
func main() { func main() {
err := godotenv.Load() err := godotenv.Load()
if err != nil { if err != nil && os.Getenv("ENV") != "production" {
log.Fatalln("ERR: Failed to load environment variables:", err) log.Fatalln("WARNING: .env file not found. Skipping...")
return
} }
var cfg config.AppConfig var cfg config.AppConfig
@@ -41,9 +42,12 @@ func main() {
fStorage := storage.New(&cfg) fStorage := storage.New(&cfg)
user.EnsureAdminUser(ctx, &cfg, repo) cache := cache.NewClient(&cfg)
server := api.NewAPIServer(fmt.Sprintf("%s:%s", cfg.Host, cfg.Port), repo, fStorage, &cfg) user.EnsureAdminUser(ctx, &cfg, repo)
user.EnsureSystemPermissions(ctx, repo)
server := api.NewAPIServer(fmt.Sprintf("%s:%s", cfg.Host, cfg.Port), repo, fStorage, cache, &cfg)
if err := server.Run(); err != nil { if err := server.Run(); err != nil {
log.Fatalln("ERR: Failed to start server:", err) log.Fatalln("ERR: Failed to start server:", err)
} }

View File

@@ -1,4 +1,3 @@
services: services:
db: db:
image: postgres image: postgres
@@ -7,6 +6,24 @@ services:
environment: environment:
POSTGRES_USER: guard POSTGRES_USER: guard
POSTGRES_PASSWORD: guard POSTGRES_PASSWORD: guard
volumes:
- postgres-data:/var/lib/postgresql/data
ports: ports:
- "5432:5432" - "5432:5432"
cache:
image: redis:7.2 # or newer
container_name: guard-redis
ports:
- "6379:6379"
volumes:
- redis-data:/data
- ./redis.conf:/usr/local/etc/redis/redis.conf
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
restart: unless-stopped
volumes:
redis-data:
driver: local
postgres-data:
driver: local

4
go.mod
View File

@@ -11,6 +11,9 @@ require (
) )
require ( require (
github.com/avct/uasurfer v0.0.0-20250506104815-f2613aa2d406 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/go-ini/ini v1.67.0 // indirect github.com/go-ini/ini v1.67.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-json v0.10.5 // indirect
@@ -23,6 +26,7 @@ require (
github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/minio-go/v7 v7.0.92 // indirect github.com/minio/minio-go/v7 v7.0.92 // indirect
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
github.com/redis/go-redis/v9 v9.10.0 // indirect
github.com/rs/xid v1.6.0 // indirect github.com/rs/xid v1.6.0 // indirect
github.com/tinylib/msgp v1.3.0 // indirect github.com/tinylib/msgp v1.3.0 // indirect
golang.org/x/crypto v0.38.0 // indirect golang.org/x/crypto v0.38.0 // indirect

8
go.sum
View File

@@ -1,6 +1,12 @@
github.com/avct/uasurfer v0.0.0-20250506104815-f2613aa2d406 h1:5/KfwL9TS8yNtUSunutqifcSC8rdX9PNdvbSsw/X/lQ=
github.com/avct/uasurfer v0.0.0-20250506104815-f2613aa2d406/go.mod h1:s+GCtuP4kZNxh1WGoqdWI1+PbluBcycrMMWuKQ9e5Nk=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
@@ -38,6 +44,8 @@ github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1Gsh
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.10.0 h1:FxwK3eV8p/CQa0Ch276C7u2d0eNC9kCmAYQ7mCXCzVs=
github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

View File

@@ -4,44 +4,15 @@ import (
"encoding/json" "encoding/json"
"log" "log"
"net/http" "net/http"
"time"
"gitea.local/admin/hspguard/internal/repository" "gitea.local/admin/hspguard/internal/repository"
"gitea.local/admin/hspguard/internal/types"
"gitea.local/admin/hspguard/internal/util" "gitea.local/admin/hspguard/internal/util"
"gitea.local/admin/hspguard/internal/web" "gitea.local/admin/hspguard/internal/web"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
) )
type ApiServiceDTO struct {
ID uuid.UUID `json:"id"`
ClientID string `json:"client_id"`
Name string `json:"name"`
Description string `json:"description"`
RedirectUris []string `json:"redirect_uris"`
Scopes []string `json:"scopes"`
GrantTypes []string `json:"grant_types"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
IsActive bool `json:"is_active"`
}
func NewApiServiceDTO(service repository.ApiService) ApiServiceDTO {
return ApiServiceDTO{
ID: service.ID,
ClientID: service.ClientID,
Name: service.Name,
Description: service.Description.String,
RedirectUris: service.RedirectUris,
Scopes: service.Scopes,
GrantTypes: service.GrantTypes,
CreatedAt: service.CreatedAt,
UpdatedAt: service.UpdatedAt,
IsActive: service.IsActive,
}
}
func (h *AdminHandler) GetApiServices(w http.ResponseWriter, r *http.Request) { func (h *AdminHandler) GetApiServices(w http.ResponseWriter, r *http.Request) {
services, err := h.repo.ListApiServices(r.Context()) services, err := h.repo.ListApiServices(r.Context())
if err != nil { if err != nil {
@@ -50,19 +21,21 @@ func (h *AdminHandler) GetApiServices(w http.ResponseWriter, r *http.Request) {
return return
} }
apiServices := make([]ApiServiceDTO, 0) apiServices := make([]types.ApiServiceDTO, 0)
for _, service := range services { for _, service := range services {
apiServices = append(apiServices, NewApiServiceDTO(service)) apiServices = append(apiServices, types.NewApiServiceDTO(service))
} }
type Response struct { type Response struct {
Items []ApiServiceDTO `json:"items"` Items []types.ApiServiceDTO `json:"items"`
Count int `json:"count"` Count int `json:"count"`
} }
encoder := json.NewEncoder(w) encoder := json.NewEncoder(w)
w.Header().Set("Content-Type", "application/json")
if err := encoder.Encode(Response{ if err := encoder.Encode(Response{
Items: apiServices, Items: apiServices,
Count: len(apiServices), Count: len(apiServices),
@@ -129,10 +102,7 @@ func (h *AdminHandler) AddApiService(w http.ResponseWriter, r *http.Request) {
} }
if req.Description != "" { if req.Description != "" {
params.Description = pgtype.Text{ params.Description = &req.Description
String: req.Description,
Valid: true,
}
} }
service, err := h.repo.CreateApiService(r.Context(), params) service, err := h.repo.CreateApiService(r.Context(), params)
@@ -146,13 +116,16 @@ func (h *AdminHandler) AddApiService(w http.ResponseWriter, r *http.Request) {
service.ClientSecret = clientSecret service.ClientSecret = clientSecret
type Response struct { type Response struct {
Service ApiServiceDTO `json:"service"` Service types.ApiServiceDTO `json:"service"`
Credentials ApiServiceCredentials `json:"credentials"` Credentials ApiServiceCredentials `json:"credentials"`
} }
encoder := json.NewEncoder(w) encoder := json.NewEncoder(w)
w.Header().Set("Content-Type", "application/json")
if err := encoder.Encode(Response{ if err := encoder.Encode(Response{
Service: NewApiServiceDTO(service), Service: types.NewApiServiceDTO(service),
Credentials: ApiServiceCredentials{ Credentials: ApiServiceCredentials{
ClientId: service.ClientID, ClientId: service.ClientID,
ClientSecret: service.ClientSecret, ClientSecret: service.ClientSecret,
@@ -178,7 +151,27 @@ func (h *AdminHandler) GetApiService(w http.ResponseWriter, r *http.Request) {
encoder := json.NewEncoder(w) encoder := json.NewEncoder(w)
if err := encoder.Encode(NewApiServiceDTO(service)); err != nil { w.Header().Set("Content-Type", "application/json")
if err := encoder.Encode(types.NewApiServiceDTO(service)); err != nil {
web.Error(w, "failed to encode response", http.StatusInternalServerError)
}
}
func (h *AdminHandler) GetApiServiceCID(w http.ResponseWriter, r *http.Request) {
clientId := chi.URLParam(r, "client_id")
service, err := h.repo.GetApiServiceCID(r.Context(), clientId)
if err != nil {
web.Error(w, "service with provided client id not found", http.StatusNotFound)
return
}
encoder := json.NewEncoder(w)
w.Header().Set("Content-Type", "application/json")
if err := encoder.Encode(types.NewApiServiceDTO(service)); err != nil {
web.Error(w, "failed to encode response", http.StatusInternalServerError) web.Error(w, "failed to encode response", http.StatusInternalServerError)
} }
} }
@@ -213,6 +206,8 @@ func (h *AdminHandler) RegenerateApiServiceSecret(w http.ResponseWriter, r *http
encoder := json.NewEncoder(w) encoder := json.NewEncoder(w)
w.Header().Set("Content-Type", "application/json")
if err := encoder.Encode(ApiServiceCredentials{ if err := encoder.Encode(ApiServiceCredentials{
ClientId: service.ClientID, ClientId: service.ClientID,
ClientSecret: clientSecret, ClientSecret: clientSecret,
@@ -250,8 +245,8 @@ func (h *AdminHandler) UpdateApiService(w http.ResponseWriter, r *http.Request)
return return
} }
if req.Description == "" { if len(req.Scopes) == 0 {
web.Error(w, "service name is required", http.StatusBadRequest) web.Error(w, "at least 1 scope is required", http.StatusBadRequest)
return return
} }
@@ -261,15 +256,57 @@ func (h *AdminHandler) UpdateApiService(w http.ResponseWriter, r *http.Request)
return return
} }
if err := h.repo.UpdateApiService(r.Context(), repository.UpdateApiServiceParams{ updated, err := h.repo.UpdateApiService(r.Context(), repository.UpdateApiServiceParams{
ClientID: service.ClientID, ClientID: service.ClientID,
Name: , Name: req.Name,
Description: pgtype.Text{}, Description: &req.Description,
RedirectUris: []string{}, RedirectUris: req.RedirectUris,
Scopes: []string{}, Scopes: req.Scopes,
GrantTypes: []string{}, GrantTypes: req.GrantTypes,