forked from Azure/azure-storage-azcopy
/
securityInfoPersistenceManager.go
108 lines (85 loc) · 3.38 KB
/
securityInfoPersistenceManager.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package ste
import (
"context"
"net/url"
"sync"
"github.com/Azure/azure-pipeline-go/pipeline"
"github.com/aymanjarrousms/azure-storage-file-go/azfile"
"github.com/golang/groupcache/lru"
)
// securityInfoPersistenceManager implements a system to interface with Azure Files
// (since this is the only remote at the moment that is SDDL aware)
// in which SDDL strings can be uploaded and mapped to their remote IDs, then obtained from their remote IDs.
type securityInfoPersistenceManager struct {
sipmMu *sync.RWMutex
cache *lru.Cache
ctx context.Context
}
// Files supports SDDLs up to and equal to 8kb. Because this isn't KiB, We're going to infer that it's 8x1000, not 8x1024.
var filesServiceMaxSDDLSize = 8000
func newSecurityInfoPersistenceManager(ctx context.Context) *securityInfoPersistenceManager {
return &securityInfoPersistenceManager{
sipmMu: &sync.RWMutex{},
cache: lru.New(3000), // Assuming all entries are around 9kb, this would use around 30MB.
ctx: ctx,
}
}
// Technically, yes, GetSDDLFromID can be used in conjunction with PutSDDL.
// Being realistic though, GetSDDLFromID will only be called when downloading,
// and PutSDDL will only be called when uploading/doing S2S.
func (sipm *securityInfoPersistenceManager) PutSDDL(sddlString string, shareURL azfile.ShareURL) (string, error) {
fileURLParts := azfile.NewFileURLParts(shareURL.URL())
fileURLParts.SAS = azfile.SASQueryParameters{} // Clear the SAS query params since it's extra unnecessary length.
rawfURL := fileURLParts.URL()
sddlKey := rawfURL.String() + "|SDDL|" + sddlString
// Acquire a read lock.
sipm.sipmMu.RLock()
// First, let's check the cache for a hit or miss.
// These IDs are per share, so we use a share-unique key.
// The SDDL string will be consistent from a local source.
id, ok := sipm.cache.Get(sddlKey)
sipm.sipmMu.RUnlock()
if ok {
return id.(string), nil
}
cResp, err := shareURL.CreatePermission(sipm.ctx, sddlString)
if err != nil {
return "", err
}
permKey := cResp.FilePermissionKey()
sipm.sipmMu.Lock()
sipm.cache.Add(sddlKey, permKey)
sipm.sipmMu.Unlock()
return permKey, nil
}
func (sipm *securityInfoPersistenceManager) GetSDDLFromID(id string, shareURL url.URL, p pipeline.Pipeline) (string, error) {
fileURLParts := azfile.NewFileURLParts(shareURL)
fileURLParts.SAS = azfile.SASQueryParameters{} // Clear the SAS query params since it's extra unnecessary length.
rawfURL := fileURLParts.URL()
sddlKey := rawfURL.String() + "|ID|" + id
sipm.sipmMu.Lock()
// fetch from the cache
// The SDDL string will be consistent from a local source.
perm, ok := sipm.cache.Get(sddlKey)
sipm.sipmMu.Unlock()
if ok {
return perm.(string), nil
}
actionableShareURL := azfile.NewShareURL(shareURL, p)
// to clarify, the GetPermission call only works against the share root, and not against a share snapshot
// if we detect that the source is a snapshot, we simply get rid of the snapshot value
if len(fileURLParts.ShareSnapshot) != 0 {
fileURLParts := azfile.NewFileURLParts(shareURL)
fileURLParts.ShareSnapshot = "" // clear the snapshot value
actionableShareURL = azfile.NewShareURL(fileURLParts.URL(), p)
}
si, err := actionableShareURL.GetPermission(sipm.ctx, id)
if err != nil {
return "", err
}
sipm.sipmMu.Lock()
// If we got the permission fine, commit to the cache.
sipm.cache.Add(sddlKey, si.Permission)
sipm.sipmMu.Unlock()
return si.Permission, nil
}