github.com/minio/console@v1.4.1/web-app/src/screens/Console/Buckets/ListBuckets/UploadPermissionUtils.ts (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2023 MinIO, Inc.
     3  //
     4  // This program is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Affero General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // This program is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  // GNU Affero General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Affero General Public License
    15  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16  
    17  export const extractFileExtn = (resourceStr: string) => {
    18    //file extensions may contain query string. so exclude query strings !
    19    return (resourceStr.match(/\.([^.]*?)(?=\?|#|$)/) || [])[1];
    20  };
    21  export const getPolicyAllowedFileExtensions = (
    22    sessionGrants: Record<string, string[]>,
    23    uploadPath: string,
    24    scopes: string[] = [],
    25  ) => {
    26    const sessionGrantWildCards = getSessionGrantsWildCard(
    27      sessionGrants,
    28      uploadPath,
    29      scopes,
    30    );
    31  
    32    //get acceptable files if any in the policy.
    33    const allowedFileExtensions = sessionGrantWildCards.reduce(
    34      (acc: string[], cv: string) => {
    35        const extension: string = extractFileExtn(cv);
    36        if (extension) {
    37          acc.push(`.${extension}`); //strict extension matching.
    38        }
    39        return acc;
    40      },
    41      [],
    42    );
    43  
    44    const uniqueExtensions = [...new Set(allowedFileExtensions)];
    45    return uniqueExtensions.join(",");
    46  };
    47  
    48  // The resource should not have the extensions (*.ext) for the hasPermission to work.
    49  // so sanitize this and also use to extract the allowed extensions outside of permission check.
    50  export const getSessionGrantsWildCard = (
    51    sessionGrants: Record<string, string[]>,
    52    uploadPath: string,
    53    scopes: string[] = [],
    54  ) => {
    55    //get only the path matching grants to reduce processing.
    56    const grantsWithExtension = Object.keys(sessionGrants).reduce(
    57      (acc: Record<string, string[]>, grantKey: string) => {
    58        if (extractFileExtn(grantKey) && grantKey.includes(uploadPath)) {
    59          acc[grantKey] = sessionGrants[grantKey];
    60        }
    61        return acc;
    62      },
    63      {},
    64    );
    65  
    66    const checkPathsForPermission = (sessionGrantKey: string) => {
    67      const grantActions = grantsWithExtension[sessionGrantKey];
    68      const hasScope = grantActions.some((actionKey) =>
    69        scopes.find((scopeKey) => {
    70          let wildCardMatch = false;
    71          const hasWildCard = scopeKey.indexOf("*") !== -1;
    72          if (hasWildCard) {
    73            const scopeActionKey = scopeKey.substring(0, scopeKey.length - 1);
    74  
    75            wildCardMatch = actionKey.includes(scopeActionKey);
    76          }
    77  
    78          return wildCardMatch || actionKey === scopeKey;
    79        }),
    80      );
    81  
    82      const sessionGrantKeyPath = sessionGrantKey.substring(
    83        0,
    84        sessionGrantKey.indexOf("/*."), //start of extension part.
    85      );
    86      const isUploadPathMatching =
    87        sessionGrantKeyPath === `arn:aws:s3:::${uploadPath}`;
    88  
    89      const hasGrant =
    90        isUploadPathMatching && sessionGrantKey !== "arn:aws:s3:::*";
    91  
    92      return hasScope && hasGrant;
    93    };
    94  
    95    return Object.keys(grantsWithExtension).filter(checkPathsForPermission);
    96  };