You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by la...@apache.org on 2026/06/29 19:28:58 UTC

(airavata-custos) branch identity-fixes updated (f24cbcfc5 -> f0fa32b52)

This is an automated email from the ASF dual-hosted git repository.

lahirujayathilake pushed a change to branch identity-fixes
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git


    from f24cbcfc5 Reshape identity error format and privilege requirement for the compute cluster extractions
     new 960a0e633 Updated the project_id and user_id to VARCHAR types
     new d9cb82719 Add endpoint to get the caller's user profile and effective privileges
     new f0fa32b52 Add bootstrap admin email to `.env.example

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .env.example                                       |  3 +++
 api/core.openapi.yaml                              | 30 +++++++++++++++++++++
 .../migrations/000014_project_memberships.up.sql   |  4 +--
 internal/server/privilege.go                       | 31 ++++++++++++++++++++++
 internal/server/privilege_integration_test.go      | 31 ++++++++++++++++++++++
 internal/server/server.go                          |  3 ++-
 6 files changed, 99 insertions(+), 3 deletions(-)


(airavata-custos) 01/03: Updated the project_id and user_id to VARCHAR types

Posted by la...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lahirujayathilake pushed a commit to branch identity-fixes
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 960a0e633d82e8ed325e8918bfd23c9e3fb0f457
Author: lahiruj <la...@gmail.com>
AuthorDate: Mon Jun 29 14:17:20 2026 -0400

    Updated the project_id and user_id to VARCHAR types
---
 internal/db/migrations/000014_project_memberships.up.sql | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/internal/db/migrations/000014_project_memberships.up.sql b/internal/db/migrations/000014_project_memberships.up.sql
index 7626447e6..cd791373e 100644
--- a/internal/db/migrations/000014_project_memberships.up.sql
+++ b/internal/db/migrations/000014_project_memberships.up.sql
@@ -19,8 +19,8 @@
 -- status is derived from compute_allocation_memberships, so it is not stored
 -- here. One row per (project, user).
 CREATE TABLE project_memberships (
-    project_id CHAR(36) NOT NULL,
-    user_id    CHAR(36) NOT NULL,
+    project_id VARCHAR(255) NOT NULL,
+    user_id    VARCHAR(255) NOT NULL,
     role       VARCHAR(32) NOT NULL,
     added_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
     PRIMARY KEY (project_id, user_id),


(airavata-custos) 03/03: Add bootstrap admin email to `.env.example

Posted by la...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lahirujayathilake pushed a commit to branch identity-fixes
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit f0fa32b5224cb75351b1a85dbeadef0bf25ba56d
Author: lahiruj <la...@gmail.com>
AuthorDate: Mon Jun 29 15:28:52 2026 -0400

    Add bootstrap admin email to `.env.example
---
 .env.example | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/.env.example b/.env.example
index 96fbaea15..15f253d95 100644
--- a/.env.example
+++ b/.env.example
@@ -36,6 +36,9 @@ OIDC_AUDIENCE=custos-api
 # Shared across connectors that need to know which cluster they belong to.
 CUSTOS_CLUSTER_ID=00000000-0000-0000-0000-000000000001
 
+# Grants super_admin to the user with this email on first boot. Idempotent.
+CUSTOS_BOOTSTRAP_ADMIN_EMAIL=admin@custos.local
+
 # --- AMIE connector (ACCESS-CI) ------------------------------------------
 
 AMIE_BASE_URL=http://localhost:8180


(airavata-custos) 02/03: Add endpoint to get the caller's user profile and effective privileges

Posted by la...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lahirujayathilake pushed a commit to branch identity-fixes
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit d9cb827199abb1fab5a29de4412df111d0ddd38b
Author: lahiruj <la...@gmail.com>
AuthorDate: Mon Jun 29 14:28:44 2026 -0400

    Add endpoint to get the caller's user profile and effective privileges
---
 api/core.openapi.yaml                         | 30 ++++++++++++++++++++++++++
 internal/server/privilege.go                  | 31 +++++++++++++++++++++++++++
 internal/server/privilege_integration_test.go | 31 +++++++++++++++++++++++++++
 internal/server/server.go                     |  3 ++-
 4 files changed, 94 insertions(+), 1 deletion(-)

diff --git a/api/core.openapi.yaml b/api/core.openapi.yaml
index c7808fbb9..65e4604b9 100644
--- a/api/core.openapi.yaml
+++ b/api/core.openapi.yaml
@@ -40,6 +40,15 @@ definitions:
     - ACTIVE
     - INACTIVE
     - DELETED
+  CallerProfileResponse:
+    properties:
+      privileges:
+        items:
+          $ref: '#/definitions/PrivilegeKey'
+        type: array
+      user:
+        $ref: '#/definitions/User'
+    type: object
   ComputeAllocation:
     properties:
       compute_cluster_id:
@@ -2571,6 +2580,27 @@ paths:
       summary: Get a compute cluster user by (cluster, user) pair
       tags:
       - Compute Cluster Users
+  /me:
+    get:
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/CallerProfileResponse'
+        "401":
+          description: Missing authenticated caller
+          schema:
+            properties:
+              error:
+                type: string
+            type: object
+      security:
+      - BearerAuth: []
+      summary: Get the caller's user record and effective privileges
+      tags:
+      - Caller
   /organizations:
     post:
       consumes:
diff --git a/internal/server/privilege.go b/internal/server/privilege.go
index 3f1553cbb..7ecf25f71 100644
--- a/internal/server/privilege.go
+++ b/internal/server/privilege.go
@@ -45,6 +45,37 @@ func (s *Server) getCallerPrivileges(w http.ResponseWriter, r *http.Request) {
 	common.WriteJSON(w, http.StatusOK, map[string]any{"privileges": keys})
 }
 
+// CallerProfileResponse bundles the caller's user row with the effective
+// privilege set.
+type CallerProfileResponse struct {
+	User       *models.User          `json:"user"`
+	Privileges []models.PrivilegeKey `json:"privileges"`
+}
+
+// @Summary	Get the caller's user record and effective privileges
+// @Tags	Caller
+// @Security	BearerAuth
+// @Produce	json
+// @Success	200	{object}	CallerProfileResponse
+// @Failure	401	{object}	object{error=string}	"Missing authenticated caller"
+// @Router	/me [get]
+func (s *Server) getCallerProfile(w http.ResponseWriter, r *http.Request) {
+	caller := requireCaller(w, r)
+	if caller == nil {
+		return
+	}
+	user, err := s.svc.GetUser(r.Context(), caller.UserID)
+	if err != nil {
+		common.WriteServiceError(w, err)
+		return
+	}
+	keys := identity.PrivilegesFromContext(r.Context())
+	if keys == nil {
+		keys = []models.PrivilegeKey{}
+	}
+	common.WriteJSON(w, http.StatusOK, CallerProfileResponse{User: user, Privileges: keys})
+}
+
 // @Summary	List the declared privilege catalog
 // @Tags	Privileges
 // @Security	BearerAuth
diff --git a/internal/server/privilege_integration_test.go b/internal/server/privilege_integration_test.go
index c8a595aab..de7f26514 100644
--- a/internal/server/privilege_integration_test.go
+++ b/internal/server/privilege_integration_test.go
@@ -83,6 +83,37 @@ func TestGetCallerPrivileges_WithGrants(t *testing.T) {
 	}
 }
 
+func TestGetCallerProfile_NoCaller_401(t *testing.T) {
+	_, _, srv := setupTestStack(t)
+	rr := httptest.NewRecorder()
+	srv.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/me", nil))
+	if rr.Code != http.StatusUnauthorized {
+		t.Errorf("status: got %d, want 401", rr.Code)
+	}
+}
+
+func TestGetCallerProfile_ReturnsUserAndPrivileges(t *testing.T) {
+	database, _, srv := setupTestStack(t)
+	user := seedUser(t, database, "user@example.edu")
+	rr := httptest.NewRecorder()
+	req := httptest.NewRequest(http.MethodGet, "/me", nil)
+	req = withTestCaller(req, user, models.PrivilegeGrant)
+	srv.ServeHTTP(rr, req)
+	if rr.Code != http.StatusOK {
+		t.Fatalf("status: got %d, want 200; body=%s", rr.Code, rr.Body.String())
+	}
+	var body CallerProfileResponse
+	if err := json.NewDecoder(rr.Body).Decode(&body); err != nil {
+		t.Fatalf("decode: %v", err)
+	}
+	if body.User == nil || body.User.ID != user {
+		t.Errorf("user.id: got %+v, want %s", body.User, user)
+	}
+	if len(body.Privileges) != 1 || body.Privileges[0] != models.PrivilegeGrant {
+		t.Errorf("privileges: got %v, want [%s]", body.Privileges, models.PrivilegeGrant)
+	}
+}
+
 func TestRequirePrivilege_NoGrants_403(t *testing.T) {
 	database, _, srv := setupTestStack(t)
 	user := seedUser(t, database, "user@example.edu")
diff --git a/internal/server/server.go b/internal/server/server.go
index 93860b604..4b5c1b4ac 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -164,8 +164,9 @@ func (s *Server) routes() {
 	s.router.RequireAuth("GET /user-identities/oidc-subjects/{oidcSub}", s.getUserIdentityByOIDCSub)
 	s.router.RequireAuth("GET /users/{id}/user-identities", s.listUserIdentitiesForUser)
 
-	// Any authenticated caller may read their own effective privilege set with no privilege check.
+	// Any authenticated caller may read their own profile and effective privilege set with no privilege check.
 	s.router.RequireAuth("GET /user/privileges", s.getCallerPrivileges)
+	s.router.RequireAuth("GET /me", s.getCallerProfile)
 	s.router.RequirePrivilege("GET /privileges/catalog", models.PrivilegeGrant, s.getPrivilegeCatalog)
 	s.router.RequirePrivilege("GET /users/{id}/privileges", models.PrivilegeGrant, s.listUserPrivileges)
 	s.router.RequirePrivilege("GET /privileges/{key}/holders", models.PrivilegeGrant, s.listPrivilegeHolders)