github.com/cs3org/reva/v2@v2.27.7/pkg/storage/fs/cephfs/permissions.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  //go:build ceph
    20  // +build ceph
    21  
    22  package cephfs
    23  
    24  import (
    25  	"context"
    26  	"errors"
    27  	"fmt"
    28  	"strings"
    29  
    30  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    31  
    32  	cephfs2 "github.com/ceph/go-ceph/cephfs"
    33  	grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
    34  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    35  	"github.com/maxymania/go-system/posix_acl"
    36  )
    37  
    38  var perms = map[rune][]string{
    39  	'r': {
    40  		"Stat",
    41  		"GetPath",
    42  		"GetQuota",
    43  		"InitiateFileDownload",
    44  		"ListGrants",
    45  	},
    46  	'w': {
    47  		"AddGrant",
    48  		"CreateContainer",
    49  		"Delete",
    50  		"InitiateFileUpload",
    51  		"Move",
    52  		"RemoveGrant",
    53  		"PurgeRecycle",
    54  		"RestoreFileVersion",
    55  		"RestoreRecycleItem",
    56  		"UpdateGrant",
    57  	},
    58  	'x': {
    59  		"ListRecycle",
    60  		"ListContainer",
    61  		"ListFileVersions",
    62  	},
    63  }
    64  
    65  const (
    66  	aclXattr = "system.posix_acl_access"
    67  )
    68  
    69  var op2int = map[rune]uint16{'r': 4, 'w': 2, 'x': 1}
    70  
    71  func getPermissionSet(user *User, stat *cephfs2.CephStatx, mount Mount, path string) (perm *provider.ResourcePermissions) {
    72  	perm = &provider.ResourcePermissions{}
    73  
    74  	if int64(stat.Uid) == user.UidNumber || int64(stat.Gid) == user.GidNumber {
    75  		updatePerms(perm, "rwx", false)
    76  		return
    77  	}
    78  
    79  	acls := &posix_acl.Acl{}
    80  	var xattr []byte
    81  	var err error
    82  	if xattr, err = mount.GetXattr(path, aclXattr); err != nil {
    83  		return nil
    84  	}
    85  	acls.Decode(xattr)
    86  
    87  	group, err := user.fs.getGroupByID(user.ctx, fmt.Sprint(stat.Gid))
    88  
    89  	for _, acl := range acls.List {
    90  		rwx := strings.Split(acl.String(), ":")[2]
    91  		switch acl.GetType() {
    92  		case posix_acl.ACL_USER:
    93  			if int64(acl.GetID()) == user.UidNumber {
    94  				updatePerms(perm, rwx, false)
    95  			}
    96  		case posix_acl.ACL_GROUP:
    97  			if int64(acl.GetID()) == user.GidNumber || in(group.GroupName, user.Groups) {
    98  				updatePerms(perm, rwx, false)
    99  			}
   100  		case posix_acl.ACL_MASK:
   101  			updatePerms(perm, rwx, true)
   102  		case posix_acl.ACL_OTHERS:
   103  			updatePerms(perm, rwx, false)
   104  		}
   105  	}
   106  
   107  	return
   108  }
   109  
   110  func (fs *cephfs) getFullPermissionSet(ctx context.Context, mount Mount, path string) (permList []*provider.Grant) {
   111  	acls := &posix_acl.Acl{}
   112  	var xattr []byte
   113  	var err error
   114  	if xattr, err = mount.GetXattr(path, aclXattr); err != nil {
   115  		return nil
   116  	}
   117  	acls.Decode(xattr)
   118  
   119  	for _, acl := range acls.List {
   120  		rwx := strings.Split(acl.String(), ":")[2]
   121  		switch acl.GetType() {
   122  		case posix_acl.ACL_USER:
   123  			user, err := fs.getUserByID(ctx, fmt.Sprint(acl.GetID()))
   124  			if err != nil {
   125  				return nil
   126  			}
   127  			userGrant := &provider.Grant{
   128  				Grantee: &provider.Grantee{
   129  					Type: provider.GranteeType_GRANTEE_TYPE_USER,
   130  					Id:   &provider.Grantee_UserId{UserId: user.Id},
   131  				},
   132  				Permissions: &provider.ResourcePermissions{},
   133  			}
   134  			updatePerms(userGrant.Permissions, rwx, false)
   135  			permList = append(permList, userGrant)
   136  		case posix_acl.ACL_GROUP:
   137  			group, err := fs.getGroupByID(ctx, fmt.Sprint(acl.GetID()))
   138  			if err != nil {
   139  				return nil
   140  			}
   141  			groupGrant := &provider.Grant{
   142  				Grantee: &provider.Grantee{
   143  					Type: provider.GranteeType_GRANTEE_TYPE_GROUP,
   144  					Id:   &provider.Grantee_GroupId{GroupId: group.Id},
   145  				},
   146  				Permissions: &provider.ResourcePermissions{},
   147  			}
   148  			updatePerms(groupGrant.Permissions, rwx, false)
   149  			permList = append(permList, groupGrant)
   150  		}
   151  	}
   152  
   153  	return
   154  }
   155  
   156  /*
   157  func permToIntRefl(p *provider.ResourcePermissions) (result uint16) {
   158  	if p == nil { return 0b111 } //rwx
   159  
   160  	item := reflect.ValueOf(p).Elem()
   161  	for _, op := range "rwx" {
   162  		for _, perm := range perms[op] {
   163  			if item.FieldByName(perm).Bool() {
   164  				result |= op2int[op]
   165  				break //if value is 1 then bitwise OR can never change it again
   166  			}
   167  		}
   168  	}
   169  
   170  	return
   171  }
   172  */
   173  
   174  func permToInt(rp *provider.ResourcePermissions) (result uint16) {
   175  	if rp == nil {
   176  		return 0b111 // rwx
   177  	}
   178  	if rp.Stat || rp.GetPath || rp.GetQuota || rp.ListGrants || rp.InitiateFileDownload {
   179  		result |= 4
   180  	}
   181  	if rp.CreateContainer || rp.Move || rp.Delete || rp.InitiateFileUpload || rp.AddGrant || rp.UpdateGrant ||
   182  		rp.RemoveGrant || rp.DenyGrant || rp.RestoreFileVersion || rp.PurgeRecycle || rp.RestoreRecycleItem {
   183  		result |= 2
   184  	}
   185  	if rp.ListRecycle || rp.ListContainer || rp.ListFileVersions {
   186  		result |= 1
   187  	}
   188  
   189  	return
   190  }
   191  
   192  const (
   193  	updateGrant = iota
   194  	removeGrant = iota
   195  )
   196  
   197  func (fs *cephfs) changePerms(ctx context.Context, mt Mount, grant *provider.Grant, path string, method int) (err error) {
   198  	buf, err := mt.GetXattr(path, aclXattr)
   199  	if err != nil {
   200  		return
   201  	}
   202  	acls := &posix_acl.Acl{}
   203  	acls.Decode(buf)
   204  	var sid posix_acl.AclSID
   205  
   206  	switch grant.Grantee.Type {
   207  	case provider.GranteeType_GRANTEE_TYPE_USER:
   208  		var user *userpb.User
   209  		if user, err = fs.getUserByOpaqueID(ctx, grant.Grantee.GetUserId().OpaqueId); err != nil {
   210  			return
   211  		}
   212  		sid.SetUid(uint32(user.UidNumber))
   213  	case provider.GranteeType_GRANTEE_TYPE_GROUP:
   214  		var group *grouppb.Group
   215  		if group, err = fs.getGroupByOpaqueID(ctx, grant.Grantee.GetGroupId().OpaqueId); err != nil {
   216  			return
   217  		}
   218  		sid.SetGid(uint32(group.GidNumber))
   219  	default:
   220  		return errors.New("cephfs: invalid grantee type")
   221  	}
   222  
   223  	var found = false
   224  	var i int
   225  	for i = range acls.List {
   226  		if acls.List[i].AclSID == sid {
   227  			found = true
   228  		}
   229  	}
   230  
   231  	if method == updateGrant {
   232  		if found {
   233  			acls.List[i].Perm |= permToInt(grant.Permissions)
   234  			if acls.List[i].Perm == 0 { // remove empty grant
   235  				acls.List = append(acls.List[:i], acls.List[i+1:]...)
   236  			}
   237  		} else {
   238  			acls.List = append(acls.List, posix_acl.AclElement{
   239  				AclSID: sid,
   240  				Perm:   permToInt(grant.Permissions),
   241  			})
   242  		}
   243  	} else { //removeGrant
   244  		if found {
   245  			acls.List[i].Perm &^= permToInt(grant.Permissions) //bitwise and-not, to clear bits on Perm
   246  			if acls.List[i].Perm == 0 {                        // remove empty grant
   247  				acls.List = append(acls.List[:i], acls.List[i+1:]...)
   248  			}
   249  		}
   250  	}
   251  
   252  	err = mt.SetXattr(path, aclXattr, acls.Encode(), 0)
   253  
   254  	return
   255  }
   256  
   257  /*
   258  func updatePermsRefl(rp *provider.ResourcePermissions, acl string, unset bool) {
   259  	if rp == nil { return }
   260  	for _, t := range "rwx" {
   261  		if strings.ContainsRune(acl, t) {
   262  			for _, i := range perms[t] {
   263  				reflect.ValueOf(rp).Elem().FieldByName(i).SetBool(true)
   264  			}
   265  		} else if unset {
   266  			for _, i := range perms[t] {
   267  				reflect.ValueOf(rp).Elem().FieldByName(i).SetBool(false)
   268  			}
   269  		}
   270  	}
   271  }
   272  */
   273  
   274  func updatePerms(rp *provider.ResourcePermissions, acl string, unset bool) {
   275  	if rp == nil {
   276  		return
   277  	}
   278  	if strings.ContainsRune(acl, 'r') {
   279  		rp.Stat = true
   280  		rp.GetPath = true
   281  		rp.GetQuota = true
   282  		rp.InitiateFileDownload = true
   283  		rp.ListGrants = true
   284  	} else if unset {
   285  		rp.Stat = false
   286  		rp.GetPath = false
   287  		rp.GetQuota = false
   288  		rp.InitiateFileDownload = false
   289  		rp.ListGrants = false
   290  	}
   291  	if strings.ContainsRune(acl, 'w') {
   292  		rp.AddGrant = true
   293  		rp.DenyGrant = true
   294  		rp.CreateContainer = true
   295  		rp.Delete = true
   296  		rp.InitiateFileUpload = true
   297  		rp.Move = true
   298  		rp.RemoveGrant = true
   299  		rp.PurgeRecycle = true
   300  		rp.RestoreFileVersion = true
   301  		rp.RestoreRecycleItem = true
   302  		rp.UpdateGrant = true
   303  	} else if unset {
   304  		rp.AddGrant = false
   305  		rp.DenyGrant = false
   306  		rp.CreateContainer = false
   307  		rp.Delete = false
   308  		rp.InitiateFileUpload = false
   309  		rp.Move = false
   310  		rp.RemoveGrant = false
   311  		rp.PurgeRecycle = false
   312  		rp.RestoreFileVersion = false
   313  		rp.RestoreRecycleItem = false
   314  		rp.UpdateGrant = false
   315  	}
   316  	if strings.ContainsRune(acl, 'x') {
   317  		rp.ListRecycle = true
   318  		rp.ListContainer = true
   319  		rp.ListFileVersions = true
   320  	} else if unset {
   321  		rp.ListRecycle = false
   322  		rp.ListContainer = false
   323  		rp.ListFileVersions = false
   324  	}
   325  }