11//
2- // Copyright 2024-2025 The Chainloop Authors.
2+ // Copyright 2024-2026 The Chainloop Authors.
33//
44// Licensed under the Apache License, Version 2.0 (the "License");
55// you may not use this file except in compliance with the License.
@@ -229,12 +229,12 @@ func (s *membershipIntegrationTestSuite) TestDeleteOther() {
229229 s .NoError (err )
230230
231231 s .T ().Run ("I can not delete my own membership" , func (t * testing.T ) {
232- err := s .Membership .DeleteOther (ctx , sharedOrg .ID , user2 .ID , mUser2SharedOrg .ID .String ())
232+ err := s .Membership .DeleteOther (ctx , sharedOrg .ID , user2 .ID , mUser2SharedOrg .ID .String (), authz . RoleOwner )
233233 s .ErrorContains (err , "cannot delete yourself from the org" )
234234 })
235235
236236 s .T ().Run ("I can't find the membership" , func (t * testing.T ) {
237- err := s .Membership .DeleteOther (ctx , otherOrg .ID , user2 .ID , mUser2SharedOrg .ID .String ())
237+ err := s .Membership .DeleteOther (ctx , otherOrg .ID , user2 .ID , mUser2SharedOrg .ID .String (), authz . RoleOwner )
238238 s .True (biz .IsNotFound (err ))
239239 })
240240
@@ -244,7 +244,7 @@ func (s *membershipIntegrationTestSuite) TestDeleteOther() {
244244 s .Len (memberships , 1 )
245245 s .Equal (1 , count )
246246
247- err = s .Membership .DeleteOther (ctx , sharedOrg .ID , user .ID , mUser2SharedOrg .ID .String ())
247+ err = s .Membership .DeleteOther (ctx , sharedOrg .ID , user .ID , mUser2SharedOrg .ID .String (), authz . RoleOwner )
248248 s .NoError (err )
249249
250250 memberships , count , err = s .Membership .ByOrg (ctx , sharedOrg .ID , & biz.ListByOrgOpts {}, nil )
@@ -270,17 +270,17 @@ func (s *membershipIntegrationTestSuite) TestUpdateRole() {
270270
271271 // empty role
272272 s .T ().Run ("a role is required" , func (t * testing.T ) {
273- _ , err := s .Membership .UpdateRole (ctx , sharedOrg .ID , user .ID , mUser2SharedOrg .ID .String (), "" )
273+ _ , err := s .Membership .UpdateRole (ctx , sharedOrg .ID , user .ID , mUser2SharedOrg .ID .String (), "" , authz . RoleOwner )
274274 s .ErrorContains (err , "role is required" )
275275 })
276276
277277 s .T ().Run ("I can not update my own membership" , func (t * testing.T ) {
278- _ , err := s .Membership .UpdateRole (ctx , sharedOrg .ID , user2 .ID , mUser2SharedOrg .ID .String (), authz .RoleAdmin )
278+ _ , err := s .Membership .UpdateRole (ctx , sharedOrg .ID , user2 .ID , mUser2SharedOrg .ID .String (), authz .RoleAdmin , authz . RoleOwner )
279279 s .ErrorContains (err , "cannot update yourself" )
280280 })
281281
282282 s .T ().Run ("I can't find the membership in another org" , func (t * testing.T ) {
283- _ , err := s .Membership .UpdateRole (ctx , otherOrg .ID , user .ID , mUser2SharedOrg .ID .String (), authz .RoleAdmin )
283+ _ , err := s .Membership .UpdateRole (ctx , otherOrg .ID , user .ID , mUser2SharedOrg .ID .String (), authz .RoleAdmin , authz . RoleOwner )
284284 s .True (biz .IsNotFound (err ))
285285 })
286286
@@ -291,12 +291,81 @@ func (s *membershipIntegrationTestSuite) TestUpdateRole() {
291291 s .Equal (1 , count )
292292 s .Equal (authz .RoleViewer , memberships [0 ].Role )
293293
294- got , err := s .Membership .UpdateRole (ctx , sharedOrg .ID , user .ID , mUser2SharedOrg .ID .String (), authz .RoleAdmin )
294+ got , err := s .Membership .UpdateRole (ctx , sharedOrg .ID , user .ID , mUser2SharedOrg .ID .String (), authz .RoleAdmin , authz . RoleOwner )
295295 s .NoError (err )
296296 s .Equal (authz .RoleAdmin , got .Role )
297297 })
298298}
299299
300+ func (s * membershipIntegrationTestSuite ) TestOwnerGuards () {
301+ ctx := context .Background ()
302+
303+ owner , err := s .User .UpsertByEmail (ctx , "owner@test.com" , nil )
304+ s .NoError (err )
305+ admin , err := s .User .UpsertByEmail (ctx , "admin@test.com" , nil )
306+ s .NoError (err )
307+ target , err := s .User .UpsertByEmail (ctx , "target@test.com" , nil )
308+ s .NoError (err )
309+
310+ org , err := s .Organization .CreateWithRandomName (ctx )
311+ s .NoError (err )
312+
313+ // owner is org owner, admin is org admin, target starts as viewer
314+ _ , err = s .Membership .Create (ctx , org .ID , owner .ID , biz .WithMembershipRole (authz .RoleOwner ))
315+ s .NoError (err )
316+ _ , err = s .Membership .Create (ctx , org .ID , admin .ID , biz .WithMembershipRole (authz .RoleAdmin ))
317+ s .NoError (err )
318+ mTarget , err := s .Membership .Create (ctx , org .ID , target .ID , biz .WithMembershipRole (authz .RoleViewer ))
319+ s .NoError (err )
320+
321+ s .Run ("admin cannot promote user to owner" , func () {
322+ _ , err := s .Membership .UpdateRole (ctx , org .ID , admin .ID , mTarget .ID .String (), authz .RoleOwner , authz .RoleAdmin )
323+ s .Error (err )
324+ s .True (biz .IsErrUnauthorized (err ))
325+ s .Contains (err .Error (), "only organization owners can manage owner memberships" )
326+ })
327+
328+ s .Run ("owner can promote user to owner" , func () {
329+ got , err := s .Membership .UpdateRole (ctx , org .ID , owner .ID , mTarget .ID .String (), authz .RoleOwner , authz .RoleOwner )
330+ s .NoError (err )
331+ s .Equal (authz .RoleOwner , got .Role )
332+ })
333+
334+ s .Run ("admin cannot demote an owner" , func () {
335+ _ , err := s .Membership .UpdateRole (ctx , org .ID , admin .ID , mTarget .ID .String (), authz .RoleAdmin , authz .RoleAdmin )
336+ s .Error (err )
337+ s .True (biz .IsErrUnauthorized (err ))
338+ s .Contains (err .Error (), "only organization owners can manage owner memberships" )
339+ })
340+
341+ s .Run ("owner can demote another owner" , func () {
342+ got , err := s .Membership .UpdateRole (ctx , org .ID , owner .ID , mTarget .ID .String (), authz .RoleAdmin , authz .RoleOwner )
343+ s .NoError (err )
344+ s .Equal (authz .RoleAdmin , got .Role )
345+ })
346+
347+ s .Run ("admin cannot delete an owner membership" , func () {
348+ // First promote target back to owner
349+ _ , err := s .Membership .UpdateRole (ctx , org .ID , owner .ID , mTarget .ID .String (), authz .RoleOwner , authz .RoleOwner )
350+ s .NoError (err )
351+
352+ err = s .Membership .DeleteOther (ctx , org .ID , admin .ID , mTarget .ID .String (), authz .RoleAdmin )
353+ s .Error (err )
354+ s .True (biz .IsErrUnauthorized (err ))
355+ s .Contains (err .Error (), "only organization owners can manage owner memberships" )
356+ })
357+
358+ s .Run ("owner can delete another owner membership" , func () {
359+ err := s .Membership .DeleteOther (ctx , org .ID , owner .ID , mTarget .ID .String (), authz .RoleOwner )
360+ s .NoError (err )
361+
362+ memberships , count , err := s .Membership .ByOrg (ctx , org .ID , & biz.ListByOrgOpts {}, nil )
363+ s .NoError (err )
364+ s .Equal (2 , count ) // only owner and admin remain
365+ s .Len (memberships , 2 )
366+ })
367+ }
368+
300369func (s * membershipIntegrationTestSuite ) TestCreateMembership () {
301370 assert := assert .New (s .T ())
302371 ctx := context .Background ()
0 commit comments