github.com/cyverse/go-irodsclient@v0.13.2/fs/fs_acl.go (about)

     1  package fs
     2  
     3  import (
     4  	"fmt"
     5  
     6  	irods_fs "github.com/cyverse/go-irodsclient/irods/fs"
     7  	"github.com/cyverse/go-irodsclient/irods/types"
     8  	"github.com/cyverse/go-irodsclient/irods/util"
     9  	"golang.org/x/xerrors"
    10  )
    11  
    12  // ListACLs returns ACLs
    13  func (fs *FileSystem) ListACLs(path string) ([]*types.IRODSAccess, error) {
    14  	stat, err := fs.Stat(path)
    15  	if err != nil {
    16  		return nil, err
    17  	}
    18  
    19  	if stat.Type == DirectoryEntry {
    20  		return fs.ListDirACLs(path)
    21  	} else if stat.Type == FileEntry {
    22  		return fs.ListFileACLs(path)
    23  	}
    24  
    25  	return nil, xerrors.Errorf("unknown type - %s", stat.Type)
    26  }
    27  
    28  // ListACLsForEntries returns ACLs for entries in a collection
    29  func (fs *FileSystem) ListACLsForEntries(path string) ([]*types.IRODSAccess, error) {
    30  	irodsPath := util.GetCorrectIRODSPath(path)
    31  
    32  	collectionEntry, err := fs.getCollection(irodsPath)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  
    37  	collection := fs.getCollectionFromEntry(collectionEntry)
    38  
    39  	return fs.listACLsForEntries(collection)
    40  }
    41  
    42  // ListACLsWithGroupUsers returns ACLs
    43  func (fs *FileSystem) ListACLsWithGroupUsers(path string) ([]*types.IRODSAccess, error) {
    44  	stat, err := fs.Stat(path)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	accesses := []*types.IRODSAccess{}
    50  	if stat.Type == DirectoryEntry {
    51  		accessList, err := fs.ListDirACLsWithGroupUsers(path)
    52  		if err != nil {
    53  			return nil, err
    54  		}
    55  
    56  		accesses = append(accesses, accessList...)
    57  	} else if stat.Type == FileEntry {
    58  		accessList, err := fs.ListFileACLsWithGroupUsers(path)
    59  		if err != nil {
    60  			return nil, err
    61  		}
    62  
    63  		accesses = append(accesses, accessList...)
    64  	} else {
    65  		return nil, xerrors.Errorf("unknown type '%s'", stat.Type)
    66  	}
    67  
    68  	return accesses, nil
    69  }
    70  
    71  // ListDirACLs returns ACLs of a directory
    72  func (fs *FileSystem) ListDirACLs(path string) ([]*types.IRODSAccess, error) {
    73  	irodsPath := util.GetCorrectIRODSPath(path)
    74  
    75  	// check cache first
    76  	cachedAccesses := fs.cache.GetACLsCache(irodsPath)
    77  	if cachedAccesses != nil {
    78  		return cachedAccesses, nil
    79  	}
    80  
    81  	// otherwise, retrieve it and add it to cache
    82  	conn, err := fs.metaSession.AcquireConnection()
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	defer fs.metaSession.ReturnConnection(conn)
    87  
    88  	accesses, err := irods_fs.ListCollectionAccesses(conn, irodsPath)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	// cache it
    94  	fs.cache.AddACLsCache(irodsPath, accesses)
    95  
    96  	return accesses, nil
    97  }
    98  
    99  // ListDirACLsWithGroupUsers returns ACLs of a directory
   100  // CAUTION: this can fail if a group contains a lot of users
   101  func (fs *FileSystem) ListDirACLsWithGroupUsers(path string) ([]*types.IRODSAccess, error) {
   102  	accesses, err := fs.ListDirACLs(path)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  
   107  	newAccesses := []*types.IRODSAccess{}
   108  	newAccessesMap := map[string]*types.IRODSAccess{}
   109  
   110  	for _, access := range accesses {
   111  		if access.UserType == types.IRODSUserRodsGroup {
   112  			// retrieve all users in the group
   113  			users, err := fs.ListGroupUsers(access.UserName)
   114  			if err != nil {
   115  				return nil, err
   116  			}
   117  
   118  			for _, user := range users {
   119  				userAccess := &types.IRODSAccess{
   120  					Path:        access.Path,
   121  					UserName:    user.Name,
   122  					UserZone:    user.Zone,
   123  					UserType:    user.Type,
   124  					AccessLevel: access.AccessLevel,
   125  				}
   126  
   127  				// remove duplicates
   128  				newAccessesMap[fmt.Sprintf("%s||%s", user.Name, access.AccessLevel)] = userAccess
   129  			}
   130  		} else {
   131  			newAccessesMap[fmt.Sprintf("%s||%s", access.UserName, access.AccessLevel)] = access
   132  		}
   133  	}
   134  
   135  	// convert map to array
   136  	for _, access := range newAccessesMap {
   137  		newAccesses = append(newAccesses, access)
   138  	}
   139  
   140  	return newAccesses, nil
   141  }
   142  
   143  // ListFileACLs returns ACLs of a file
   144  func (fs *FileSystem) ListFileACLs(path string) ([]*types.IRODSAccess, error) {
   145  	irodsPath := util.GetCorrectIRODSPath(path)
   146  
   147  	// check cache first
   148  	cachedAccesses := fs.cache.GetACLsCache(irodsPath)
   149  	if cachedAccesses != nil {
   150  		return cachedAccesses, nil
   151  	}
   152  
   153  	// otherwise, retrieve it and add it to cache
   154  	conn, err := fs.metaSession.AcquireConnection()
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	defer fs.metaSession.ReturnConnection(conn)
   159  
   160  	collectionEntry, err := fs.getCollection(util.GetIRODSPathDirname(irodsPath))
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	collection := fs.getCollectionFromEntry(collectionEntry)
   166  
   167  	accesses, err := irods_fs.ListDataObjectAccesses(conn, collection, util.GetIRODSPathFileName(irodsPath))
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  
   172  	// cache it
   173  	fs.cache.AddACLsCache(irodsPath, accesses)
   174  
   175  	return accesses, nil
   176  }
   177  
   178  // ListFileACLsWithGroupUsers returns ACLs of a file
   179  func (fs *FileSystem) ListFileACLsWithGroupUsers(path string) ([]*types.IRODSAccess, error) {
   180  	accesses, err := fs.ListFileACLs(path)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  
   185  	newAccesses := []*types.IRODSAccess{}
   186  	newAccessesMap := map[string]*types.IRODSAccess{}
   187  
   188  	for _, access := range accesses {
   189  		if access.UserType == types.IRODSUserRodsGroup {
   190  			// retrieve all users in the group
   191  			users, err := fs.ListGroupUsers(access.UserName)
   192  			if err != nil {
   193  				return nil, err
   194  			}
   195  
   196  			for _, user := range users {
   197  				userAccess := &types.IRODSAccess{
   198  					Path:        access.Path,
   199  					UserName:    user.Name,
   200  					UserZone:    user.Zone,
   201  					UserType:    user.Type,
   202  					AccessLevel: access.AccessLevel,
   203  				}
   204  
   205  				// remove duplicates
   206  				newAccessesMap[fmt.Sprintf("%s||%s", user.Name, access.AccessLevel)] = userAccess
   207  			}
   208  		} else {
   209  			newAccessesMap[fmt.Sprintf("%s||%s", access.UserName, access.AccessLevel)] = access
   210  		}
   211  	}
   212  
   213  	// convert map to array
   214  	for _, access := range newAccessesMap {
   215  		newAccesses = append(newAccesses, access)
   216  	}
   217  
   218  	return newAccesses, nil
   219  }
   220  
   221  // listACLsForEntries lists ACLs for entries in a collection
   222  func (fs *FileSystem) listACLsForEntries(collection *types.IRODSCollection) ([]*types.IRODSAccess, error) {
   223  	// check cache first
   224  	cachedAccesses := []*types.IRODSAccess{}
   225  	useCached := false
   226  
   227  	cachedDirEntryPaths := fs.cache.GetDirCache(collection.Path)
   228  	if cachedDirEntryPaths != nil {
   229  		useCached = true
   230  		for _, cachedDirEntryPath := range cachedDirEntryPaths {
   231  			cachedAccess := fs.cache.GetACLsCache(cachedDirEntryPath)
   232  			if cachedAccess != nil {
   233  				cachedAccesses = append(cachedAccesses, cachedAccess...)
   234  			} else {
   235  				useCached = false
   236  				break
   237  			}
   238  		}
   239  	}
   240  
   241  	if useCached {
   242  		return cachedAccesses, nil
   243  	}
   244  
   245  	// otherwise, retrieve it and add it to cache
   246  	conn, err := fs.metaSession.AcquireConnection()
   247  	if err != nil {
   248  		return nil, err
   249  	}
   250  	defer fs.metaSession.ReturnConnection(conn)
   251  
   252  	// ListAccessesForSubCollections does not return Accesses for some files/dirs
   253  	// For these files/dirs, we compare accesses we obtained to the list of files/dirs in a dir
   254  	// and register an empty Access array to cache
   255  	dirEntryPathsToBeAdded := []string{}
   256  	if cachedDirEntryPaths != nil {
   257  		dirEntryPathsToBeAdded = append(dirEntryPathsToBeAdded, cachedDirEntryPaths...)
   258  	} else {
   259  		// otherwise, retrieve it and add it to cache
   260  		collections, err := irods_fs.ListSubCollections(conn, collection.Path)
   261  		if err != nil {
   262  			return nil, err
   263  		}
   264  
   265  		entries := []*Entry{}
   266  
   267  		for _, coll := range collections {
   268  			entry := fs.getEntryFromCollection(coll)
   269  			entries = append(entries, entry)
   270  
   271  			// cache it
   272  			fs.cache.RemoveNegativeEntryCache(entry.Path)
   273  			fs.cache.AddEntryCache(entry)
   274  		}
   275  
   276  		dataobjects, err := irods_fs.ListDataObjectsMasterReplica(conn, collection)
   277  		if err != nil {
   278  			return nil, err
   279  		}
   280  
   281  		for _, dataobject := range dataobjects {
   282  			if len(dataobject.Replicas) == 0 {
   283  				continue
   284  			}
   285  
   286  			entry := fs.getEntryFromDataObject(dataobject)
   287  			entries = append(entries, entry)
   288  
   289  			// cache it
   290  			fs.cache.RemoveNegativeEntryCache(entry.Path)
   291  			fs.cache.AddEntryCache(entry)
   292  		}
   293  
   294  		// cache dir entries
   295  		dirEntryPaths := []string{}
   296  		for _, entry := range entries {
   297  			dirEntryPaths = append(dirEntryPaths, entry.Path)
   298  			dirEntryPathsToBeAdded = append(dirEntryPathsToBeAdded, entry.Path)
   299  		}
   300  		fs.cache.AddDirCache(collection.Path, dirEntryPaths)
   301  	}
   302  
   303  	// list access
   304  	dirEntryPathsAdded := map[string]bool{}
   305  
   306  	collectionAccesses, err := irods_fs.ListAccessesForSubCollections(conn, collection.Path)
   307  	if err != nil {
   308  		return nil, err
   309  	}
   310  
   311  	accesses := []*types.IRODSAccess{}
   312  
   313  	accesses = append(accesses, collectionAccesses...)
   314  
   315  	// cache it
   316  	fs.cache.AddACLsCacheMulti(collectionAccesses)
   317  
   318  	dataobjectAccesses, err := irods_fs.ListAccessesForDataObjects(conn, collection)
   319  	if err != nil {
   320  		return nil, err
   321  	}
   322  
   323  	accesses = append(accesses, dataobjectAccesses...)
   324  
   325  	// cache it
   326  	fs.cache.AddACLsCacheMulti(dataobjectAccesses)
   327  
   328  	for _, acc := range accesses {
   329  		dirEntryPathsAdded[acc.Path] = true
   330  	}
   331  
   332  	// cache missing dir entries
   333  	for _, pathToBeAdded := range dirEntryPathsToBeAdded {
   334  		if _, ok := dirEntryPathsAdded[pathToBeAdded]; !ok {
   335  			// add empty one
   336  			fs.cache.AddACLsCache(pathToBeAdded, []*types.IRODSAccess{})
   337  			dirEntryPathsAdded[pathToBeAdded] = true
   338  		}
   339  	}
   340  
   341  	return accesses, nil
   342  }