Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6fc8040
Add Groups service client with subject-lookup support
slr71 Jul 2, 2026
0e3094c
Add collaborator-list support and backend-dispatch facades
slr71 Jul 3, 2026
8971384
Avoid a redundant round trip in get-collaborator-list
slr71 Jul 3, 2026
ece9e4c
Use source_id/subject_name from Groups membership results
slr71 Jul 3, 2026
750dda3
Add Groups-backed team support (Phase B)
slr71 Jul 3, 2026
ef5b952
Add Groups-backed community support (Phase C)
slr71 Jul 3, 2026
4946665
Address groups by identity instead of packed names
Jul 27, 2026
39e3bf0
Pass community IDs through the app community-tag routes
Aug 4, 2026
6dce881
Pass the member-list half of public_privileges through
Aug 5, 2026
b772248
Restore the guard against admitting the administrative account
Aug 5, 2026
39298d8
Gate joining on optin rather than on being public
Aug 5, 2026
9928b27
Revoke the member read grant when leaving a group
Aug 5, 2026
b51f367
Bump the common-swagger-api to 3.4.22-SNAPSHOT
Aug 5, 2026
4a3f54c
Reject the admin account in join and leave operations
Aug 5, 2026
bd78b23
Remove membership before revoking read when leaving a group
Aug 5, 2026
a1602a8
Pick the acting admin account per group backend
Aug 5, 2026
0e73882
Harden groups client tests
Aug 5, 2026
3c8f7e5
Page group listings past the service's 1000-group cap
Aug 5, 2026
06765be
Attach creator details to listings when details=true
Aug 5, 2026
5ba3aa9
Note the 403-vs-404 lookup semantics at the create-if-missing site
Aug 5, 2026
2162fa5
Pin common-swagger-api to the released 3.4.22
Aug 6, 2026
35ab5f8
Merge remote-tracking branch 'origin/main' into new-groups
Sep 3, 2026
7429df6
Address review feedback on the Groups service client
Sep 3, 2026
57d44f6
Report the caller's privileges in the community listing
Sep 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/terrain/auth/user_attributes.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[clojure-commons.response :as resp]
[clojure-commons.exception-util :as cxu]
[slingshot.slingshot :refer [try+]]
[terrain.clients.iplant-groups.subjects :as subjects]
[terrain.clients.grouping.subjects :as subjects]
[terrain.util.config :as cfg]
[terrain.util.jwt :as jwt]
[terrain.util.keycloak-oidc :as keycloak-oidc-util]))
Expand Down Expand Up @@ -59,14 +59,15 @@
"Looks up the user with the given username."
[username]
(try+
(let [subject (subjects/lookup-subject (cfg/grouper-user) username)]
(if-let [subject (subjects/lookup-subject username)]
{:username (str (:id subject) "@" (cfg/uid-domain))
:password nil
:email (:email subject)
:shortUsername (:id subject)
:firstName (:first_name subject)
:lastName (:last_name subject)
:commonName (:description subject)})
:commonName (:description subject)}
(cxu/internal-system-error (str "fake user " username " not found")))
(catch [:status 404] _
(cxu/internal-system-error (str "fake user " username " not found")))
(catch Object _
Expand Down
8 changes: 8 additions & 0 deletions src/terrain/clients/apps/raw.clj
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,14 @@
:content-type :json
:as :json}))))

(defn remove-app-from-community
[app-id community-id]
(:body
(client/delete (apps-url "apps" app-id "communities" community-id)
(disable-redirects
{:query-params (secured-params)
:as :json}))))

(defn update-app-communities
[app-id body]
(:body
Expand Down
181 changes: 181 additions & 0 deletions src/terrain/clients/grouping.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
(ns terrain.clients.grouping
"Facade over the two group backends. Dispatches each operation to either the legacy
iplant-groups client or the new Groups service client based on the configured backend.
This namespace exists to support a config-gated cutover and is intended to be removed
once the migration to the Groups service is complete, at which point callers can depend
on terrain.clients.groups directly."
(:require [terrain.clients.groups :as groups]
[terrain.clients.grouping.subjects :refer [new-backend?]]
[terrain.clients.iplant-groups :as ipg]))

;; Subjects.

(defn find-subjects [user search]
(if (new-backend?)
(groups/find-subjects user search)
(ipg/find-subjects user search)))

(defn lookup-subject [user short-username]
(if (new-backend?)
(groups/lookup-subject user short-username)
(ipg/lookup-subject user short-username)))

(defn lookup-subject-add-empty [user short-username]
(if (new-backend?)
(groups/lookup-subject-add-empty user short-username)
(ipg/lookup-subject-add-empty user short-username)))

(defn list-groups-for-user [subject-id details]
(if (new-backend?)
(groups/list-groups-for-user subject-id details)
(ipg/list-groups-for-user subject-id details)))

(defn remove-de-user [subject-id]
(if (new-backend?)
(groups/remove-de-user subject-id)
(ipg/remove-de-user subject-id)))

;; Collaborator lists.

(defn get-collaborator-lists
([user details]
(if (new-backend?)
(groups/get-collaborator-lists user details)
(ipg/get-collaborator-lists user details)))
([user details search]
(if (new-backend?)
(groups/get-collaborator-lists user details search)
(ipg/get-collaborator-lists user details search))))

(defn add-collaborator-list [user body]
(if (new-backend?)
(groups/add-collaborator-list user body)
(ipg/add-collaborator-list user body)))

(defn get-collaborator-list [user name]
(if (new-backend?)
(groups/get-collaborator-list user name)
(ipg/get-collaborator-list user name)))

(defn update-collaborator-list [user name body]
(if (new-backend?)
(groups/update-collaborator-list user name body)
(ipg/update-collaborator-list user name body)))

(defn delete-collaborator-list [user name]
(if (new-backend?)
(groups/delete-collaborator-list user name)
(ipg/delete-collaborator-list user name)))

(defn get-collaborator-list-members [user name]
(if (new-backend?)
(groups/get-collaborator-list-members user name)
(ipg/get-collaborator-list-members user name)))

(defn add-collaborator-list-members [user name members]
(if (new-backend?)
(groups/add-collaborator-list-members user name members)
(ipg/add-collaborator-list-members user name members)))

(defn remove-collaborator-list-members [user name members]
(if (new-backend?)
(groups/remove-collaborator-list-members user name members)
(ipg/remove-collaborator-list-members user name members)))

;; Teams.

(defn get-teams [user params]
(if (new-backend?) (groups/get-teams user params) (ipg/get-teams user params)))

(defn add-team [user body]
(if (new-backend?) (groups/add-team user body) (ipg/add-team user body)))

(defn get-team [user name]
(if (new-backend?) (groups/get-team user name) (ipg/get-team user name)))

(defn update-team [user name body]
(if (new-backend?) (groups/update-team user name body) (ipg/update-team user name body)))

(defn delete-team [user name]
(if (new-backend?) (groups/delete-team user name) (ipg/delete-team user name)))

(defn verify-team-exists [user name]
(if (new-backend?) (groups/verify-team-exists user name) (ipg/verify-team-exists user name)))

(defn get-team-members [user name]
(if (new-backend?) (groups/get-team-members user name) (ipg/get-team-members user name)))

(defn add-team-members [user name members]
(if (new-backend?) (groups/add-team-members user name members) (ipg/add-team-members user name members)))

(defn remove-team-members [user name members]
(if (new-backend?)
(groups/remove-team-members user name members)
(ipg/remove-team-members user name members)))

(defn list-team-privileges [user name]
(if (new-backend?) (groups/list-team-privileges user name) (ipg/list-team-privileges user name)))

(defn update-team-privileges [user name updates]
(if (new-backend?)
(groups/update-team-privileges user name updates)
(ipg/update-team-privileges user name updates)))

(defn get-team-admins [user name]
(if (new-backend?) (groups/get-team-admins user name) (ipg/get-team-admins user name)))

(defn join-team [user name]
(if (new-backend?) (groups/join-team user name) (ipg/join-team user name)))

(defn leave-team [user name]
(if (new-backend?) (groups/leave-team user name) (ipg/leave-team user name)))

;; Communities.

(defn get-communities [user params]
(if (new-backend?) (groups/get-communities user params) (ipg/get-communities user params)))

(defn admin-get-communities [user params]
(if (new-backend?) (groups/admin-get-communities user params) (ipg/admin-get-communities user params)))

(defn add-community [user body]
(if (new-backend?) (groups/add-community user body) (ipg/add-community user body)))

(defn get-community [user name]
(if (new-backend?) (groups/get-community user name) (ipg/get-community user name)))

;; The retag-apps and force-rename flags apply only to the legacy backend. Community app tags
;; there are AVUs whose value is the community's name, so a rename had to rewrite them or be
;; blocked. The paired apps image tags a community by ID instead, leaving a rename with nothing
;; to rewrite and nothing to block, so the flags are accepted and ignored here. That is a
;; property of the deployed apps image rather than of terrain: against an older apps image a
;; rename silently orphans every tag, which is why terrain.groups.backend documents the pairing.
(defn update-community [user name retag-apps? force-rename? body]
(if (new-backend?)
(groups/update-community user name body)
(ipg/update-community user name retag-apps? force-rename? body)))

(defn delete-community [user name]
(if (new-backend?) (groups/delete-community user name) (ipg/delete-community user name)))

(defn get-community-members [user name]
(if (new-backend?) (groups/get-community-members user name) (ipg/get-community-members user name)))

(defn get-community-admins [user name]
(if (new-backend?) (groups/get-community-admins user name) (ipg/get-community-admins user name)))

(defn add-community-admins [user name members]
(if (new-backend?)
(groups/add-community-admins user name members)
(ipg/add-community-admins user name members)))

(defn remove-community-admins [user name members]
(if (new-backend?)
(groups/remove-community-admins user name members)
(ipg/remove-community-admins user name members)))

(defn join-community [user name]
(if (new-backend?) (groups/join-community user name) (ipg/join-community user name)))

(defn leave-community [user name]
(if (new-backend?) (groups/leave-community user name) (ipg/leave-community user name)))
39 changes: 39 additions & 0 deletions src/terrain/clients/grouping/subjects.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
(ns terrain.clients.grouping.subjects
"Lightweight facade for subject lookups. Kept separate from terrain.clients.grouping so
that terrain.auth.user-attributes can depend on it without pulling in the legacy
iplant-groups client, which transitively requires user-attributes and would create a
circular dependency. Both backends reached from here (terrain.clients.groups and
terrain.clients.iplant-groups.subjects) are dependency-cycle safe."
(:require [terrain.clients.groups :as groups]
[terrain.clients.iplant-groups.subjects :as ipg-subjects]
[terrain.util.config :as config]))

(defn new-backend?
"True when group operations should be routed to the Groups service. Defined here rather than
in terrain.clients.grouping so that both facades test the backend the same way."
[]
(= (config/groups-backend) config/groups-backend-groups))

(defn admin-user
"The administrative account for the active group backend. Callers acting as the group
administrator must use this rather than a specific backend's configured account."
[]
(if (new-backend?)
(config/groups-admin-user)
(config/grouper-user)))

(defn lookup-subject
([short-username]
(lookup-subject (admin-user) short-username))
([user short-username]
(if (new-backend?)
(groups/lookup-subject user short-username)
(ipg-subjects/lookup-subject user short-username))))

(defn lookup-subjects
([subject-ids]
(lookup-subjects (admin-user) subject-ids))
([user subject-ids]
(if (new-backend?)
(groups/lookup-subjects user subject-ids)
(ipg-subjects/lookup-subjects user subject-ids))))
Loading