@@ -496,6 +496,10 @@ function _prepare_auth_request(req) {
496496 req . has_bucket_action_permission = async function ( bucket , action , bucket_path , req_query ) {
497497 return has_bucket_action_permission ( bucket , req . account , action , req_query , bucket_path ) ;
498498 } ;
499+
500+ req . has_bucket_ownership_permission = function ( bucket ) {
501+ return has_bucket_ownership_permission ( bucket , req . account , req . auth && req . auth . role ) ;
502+ } ;
499503}
500504
501505function _get_auth_info ( account , system , authorized_by , role , extra ) {
@@ -520,6 +524,73 @@ function _get_auth_info(account, system, authorized_by, role, extra) {
520524 return response ;
521525}
522526
527+ /**
528+ * is_system_owner checks if the account is the system owner
529+ * @param {Record<string, any> } bucket
530+ * @param {Record<string, any> } account
531+ * @returns {boolean }
532+ */
533+ function is_system_owner ( bucket , account ) {
534+ if ( ! bucket ?. system ?. owner ?. email || ! account ?. email ) return false ;
535+ return bucket . system . owner . email . unwrap ( ) === account . email . unwrap ( ) ;
536+ }
537+
538+ /**
539+ * is_bucket_owner checks if the account is the direct owner of the bucket
540+ * @param {Record<string, any> } bucket
541+ * @param {Record<string, any> } account
542+ * @returns {boolean }
543+ */
544+ function is_bucket_owner ( bucket , account ) {
545+ if ( ! bucket ?. owner_account ?. email || ! account ?. email ) return false ;
546+ return bucket . owner_account . email . unwrap ( ) === account . email . unwrap ( ) ;
547+ }
548+
549+ /**
550+ * is_bucket_claim_owner checks if the account is the OBC (ObjectBucketClaim) owner of the bucket
551+ * @param {Record<string, any> } bucket
552+ * @param {Record<string, any> } account
553+ * @returns {boolean }
554+ */
555+ function is_bucket_claim_owner ( bucket , account ) {
556+ if ( ! account ?. bucket_claim_owner || ! bucket ?. name ) return false ;
557+ return account . bucket_claim_owner . name . unwrap ( ) === bucket . name . unwrap ( ) ;
558+ }
559+
560+ /**
561+ * has_bucket_ownership_permission returns true if the account can list the bucket in ListBuckets operation
562+ *
563+ * aws-compliant behavior:
564+ * - System owner can list all the buckets
565+ * - Operator account (noobaa cli) can list all the buckets
566+ * - Root accounts can list buckets they own
567+ * - OBC owner can list their buckets
568+ * - IAM users can list their owner buckets
569+ *
570+ * @param {Record<string, any> } bucket
571+ * @param {Record<string, any> } account
572+ * @param {string } role
573+ * @returns {Promise<boolean> }
574+ */
575+ async function has_bucket_ownership_permission ( bucket , account , role ) {
576+ // system owner can list all the buckets
577+ if ( is_system_owner ( bucket , account ) ) return true ;
578+
579+ // operator account (noobaa cli) can list all the buckets
580+ if ( role === 'operator' ) return true ;
581+
582+ // check direct ownership
583+ if ( is_bucket_owner ( bucket , account ) ) return true ;
584+
585+ // special case: check bucket claim ownership (OBC)
586+ if ( is_bucket_claim_owner ( bucket , account ) ) return true ;
587+
588+ // special case: iam user can list the buckets of their owner
589+ // TODO: handle iam user
590+
591+ return false ;
592+ }
593+
523594/**
524595 * has_bucket_action_permission returns true if the requesting account has permission to perform
525596 * the given action on the given bucket.
@@ -538,19 +609,21 @@ function _get_auth_info(account, system, authorized_by, role, extra) {
538609 */
539610async function has_bucket_action_permission ( bucket , account , action , req_query , bucket_path = "" ) {
540611 dbg . log1 ( 'has_bucket_action_permission:' , bucket . name , account . email , bucket . owner_account . email ) ;
541- // If the system owner account wants to access the bucket, allow it
542- if ( bucket . system . owner . email . unwrap ( ) === account . email . unwrap ( ) ) return true ;
543612
544- const is_owner = ( bucket . owner_account . email . unwrap ( ) === account . email . unwrap ( ) ) ||
545- ( account . bucket_claim_owner && account . bucket_claim_owner . name . unwrap ( ) === bucket . name . unwrap ( ) ) ;
613+ // system owner can access all buckets
614+ if ( is_system_owner ( bucket , account ) ) return true ;
615+
616+ // check ownership: direct owner or OBC
617+ const has_owner_access = is_bucket_owner ( bucket , account ) || is_bucket_claim_owner ( bucket , account ) ;
618+
546619 const bucket_policy = bucket . s3_policy ;
547620
548621 if ( ! bucket_policy ) {
549622 // in case we do not have bucket policy
550- // we allow IAM account to access a bucket that that is owned by their root account
623+ // we allow IAM account to access a bucket that is owned by their root account
551624 const is_iam_and_same_root_account_owner = account . owner !== undefined &&
552625 account . owner . _id . toString ( ) === bucket . owner_account . _id . toString ( ) ;
553- return is_owner || is_iam_and_same_root_account_owner ;
626+ return has_owner_access || is_iam_and_same_root_account_owner ;
554627 }
555628 if ( ! action ) {
556629 throw new Error ( 'has_bucket_action_permission: action is required' ) ;
@@ -566,7 +639,8 @@ async function has_bucket_action_permission(bucket, account, action, req_query,
566639 ) ;
567640
568641 if ( result === 'DENY' ) return false ;
569- return is_owner || result === 'ALLOW' ;
642+
643+ return has_owner_access || result === 'ALLOW' ;
570644}
571645
572646/**
0 commit comments