blob: 66d3222e1e568eb6ee0e843524d5d6a19891c125 [file] [log] [blame]
Stephane Barbariea188d942018-10-16 16:43:04 -04001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Stephane Barbariedc5022d2018-11-19 15:21:44 -050016
Stephane Barbariea188d942018-10-16 16:43:04 -040017package model
18
19import (
20 "github.com/opencord/voltha-go/common/log"
Stephane Barbariea188d942018-10-16 16:43:04 -040021 "sync"
22 "time"
23)
24
Stephane Barbariedc5022d2018-11-19 15:21:44 -050025type singletonProxyAccessControl struct {
26 sync.RWMutex
Stephane Barbariec53a2752019-03-08 17:50:10 -050027 cache sync.Map
Stephane Barbarie933b09b2019-01-09 11:12:09 -050028 reservedCount int
Stephane Barbariea188d942018-10-16 16:43:04 -040029}
30
Stephane Barbariedc5022d2018-11-19 15:21:44 -050031var instanceProxyAccessControl *singletonProxyAccessControl
32var onceProxyAccessControl sync.Once
Stephane Barbariea188d942018-10-16 16:43:04 -040033
Stephane Barbariedc5022d2018-11-19 15:21:44 -050034// PAC provides access to the proxy access control singleton instance
35func PAC() *singletonProxyAccessControl {
36 onceProxyAccessControl.Do(func() {
Stephane Barbarie933b09b2019-01-09 11:12:09 -050037 instanceProxyAccessControl = &singletonProxyAccessControl{}
Stephane Barbariea188d942018-10-16 16:43:04 -040038 })
Stephane Barbariedc5022d2018-11-19 15:21:44 -050039 return instanceProxyAccessControl
Stephane Barbariea188d942018-10-16 16:43:04 -040040}
41
Stephane Barbariec53a2752019-03-08 17:50:10 -050042// IsReserved will verify if access control is active for a specific path within the model
43func (singleton *singletonProxyAccessControl) IsReserved(pathLock string) bool {
44 singleton.Lock()
45 defer singleton.Unlock()
46
47 _, exists := singleton.cache.Load(pathLock)
48 log.Debugw("is-reserved", log.Fields{"pathLock": pathLock, "exists": exists})
49
50 return exists
51}
52
Stephane Barbariedc5022d2018-11-19 15:21:44 -050053// ReservePath will apply access control for a specific path within the model
Stephane Barbarie933b09b2019-01-09 11:12:09 -050054func (singleton *singletonProxyAccessControl) ReservePath(path string, proxy *Proxy, pathLock string) *proxyAccessControl {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050055 singleton.Lock()
56 defer singleton.Unlock()
Stephane Barbarie933b09b2019-01-09 11:12:09 -050057 singleton.reservedCount++
58 if pac, exists := singleton.cache.Load(pathLock); !exists {
59 log.Debugf("Creating new PAC entry for path:%s pathLock:%s", path, pathLock)
60 newPac := NewProxyAccessControl(proxy, pathLock)
Stephane Barbariec53a2752019-03-08 17:50:10 -050061 singleton.cache.Store(pathLock, newPac)
Stephane Barbarie933b09b2019-01-09 11:12:09 -050062 return newPac
Stephane Barbariedc5022d2018-11-19 15:21:44 -050063 } else {
Stephane Barbarie933b09b2019-01-09 11:12:09 -050064 log.Debugf("Re-using existing PAC entry for path:%s pathLock:%s", path, pathLock)
65 return pac.(*proxyAccessControl)
Stephane Barbariedc5022d2018-11-19 15:21:44 -050066 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -050067}
68
69// ReleasePath will remove access control for a specific path within the model
70func (singleton *singletonProxyAccessControl) ReleasePath(pathLock string) {
71 singleton.Lock()
72 defer singleton.Unlock()
Stephane Barbarie933b09b2019-01-09 11:12:09 -050073
74 singleton.reservedCount--
75
76 if singleton.reservedCount == 0 {
77 singleton.cache.Delete(pathLock)
78 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -050079}
80
81// ProxyAccessControl is the abstraction interface to the base proxyAccessControl structure
Stephane Barbariea188d942018-10-16 16:43:04 -040082type ProxyAccessControl interface {
83 Get(path string, depth int, deep bool, txid string, control bool) interface{}
84 Update(path string, data interface{}, strict bool, txid string, control bool) interface{}
85 Add(path string, data interface{}, txid string, control bool) interface{}
86 Remove(path string, txid string, control bool) interface{}
Stephane Barbariedc5022d2018-11-19 15:21:44 -050087 SetProxy(proxy *Proxy)
Stephane Barbariea188d942018-10-16 16:43:04 -040088}
89
Stephane Barbariedc5022d2018-11-19 15:21:44 -050090// proxyAccessControl holds details of the path and proxy that requires access control
Stephane Barbariea188d942018-10-16 16:43:04 -040091type proxyAccessControl struct {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050092 sync.RWMutex
Stephane Barbariea188d942018-10-16 16:43:04 -040093 Proxy *Proxy
94 PathLock chan struct{}
95 Path string
96
97 start time.Time
98 stop time.Time
99}
100
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500101// NewProxyAccessControl creates a new instance of an access control structure
Stephane Barbarie933b09b2019-01-09 11:12:09 -0500102func NewProxyAccessControl(proxy *Proxy, path string) *proxyAccessControl {
Stephane Barbariea188d942018-10-16 16:43:04 -0400103 return &proxyAccessControl{
104 Proxy: proxy,
105 Path: path,
106 PathLock: make(chan struct{}, 1),
107 }
108}
109
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500110// lock will prevent access to a model path
Stephane Barbariea188d942018-10-16 16:43:04 -0400111func (pac *proxyAccessControl) lock() {
Stephane Barbariea188d942018-10-16 16:43:04 -0400112 pac.PathLock <- struct{}{}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500113 pac.setStart(time.Now())
Stephane Barbariea188d942018-10-16 16:43:04 -0400114}
115
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500116// unlock will release control of a model path
117func (pac *proxyAccessControl) unlock() {
118 <-pac.PathLock
119 pac.setStop(time.Now())
120 GetProfiling().AddToInMemoryLockTime(pac.getStop().Sub(pac.getStart()).Seconds())
121}
122
123// getStart is used for profiling purposes and returns the time at which access control was applied
124func (pac *proxyAccessControl) getStart() time.Time {
125 pac.Lock()
126 defer pac.Unlock()
127 return pac.start
128}
129
130// getStart is used for profiling purposes and returns the time at which access control was removed
131func (pac *proxyAccessControl) getStop() time.Time {
132 pac.Lock()
133 defer pac.Unlock()
134 return pac.stop
135}
136
137// getPath returns the access controlled path
138func (pac *proxyAccessControl) getPath() string {
139 pac.Lock()
140 defer pac.Unlock()
141 return pac.Path
142}
143
144// getProxy returns the proxy used to reach a specific location in the data model
145func (pac *proxyAccessControl) getProxy() *Proxy {
146 pac.Lock()
147 defer pac.Unlock()
148 return pac.Proxy
149}
150
151// setStart is for profiling purposes and applies a start time value at which access control was started
152func (pac *proxyAccessControl) setStart(time time.Time) {
153 pac.Lock()
154 defer pac.Unlock()
155 pac.start = time
156}
157
158// setStop is for profiling purposes and applies a stop time value at which access control was stopped
159func (pac *proxyAccessControl) setStop(time time.Time) {
160 pac.Lock()
161 defer pac.Unlock()
162 pac.stop = time
163}
164
165// SetProxy is used to changed the proxy object of an access controlled path
166func (pac *proxyAccessControl) SetProxy(proxy *Proxy) {
167 pac.Lock()
168 defer pac.Unlock()
169 pac.Proxy = proxy
170}
171
Stephane Barbarieaa467942019-02-06 14:09:44 -0500172// List retrieves data linked to a data model path
173func (pac *proxyAccessControl) List(path string, depth int, deep bool, txid string, control bool) interface{} {
174 if control {
175 pac.lock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500176 log.Debugw("locked-access--list", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500177 defer pac.unlock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500178 defer log.Debugw("unlocked-access--list", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500179 }
180
181 // FIXME: Forcing depth to 0 for now due to problems deep copying the data structure
182 // The data traversal through reflection currently corrupts the content
183
184 return pac.getProxy().GetRoot().List(path, "", depth, deep, txid)
185}
186
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500187// Get retrieves data linked to a data model path
Stephane Barbariea188d942018-10-16 16:43:04 -0400188func (pac *proxyAccessControl) Get(path string, depth int, deep bool, txid string, control bool) interface{} {
189 if control {
190 pac.lock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500191 log.Debugw("locked-access--get", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbariea188d942018-10-16 16:43:04 -0400192 defer pac.unlock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500193 defer log.Debugw("unlocked-access--get", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbariea188d942018-10-16 16:43:04 -0400194 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500195
196 // FIXME: Forcing depth to 0 for now due to problems deep copying the data structure
197 // The data traversal through reflection currently corrupts the content
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500198 return pac.getProxy().GetRoot().Get(path, "", 0, deep, txid)
Stephane Barbariea188d942018-10-16 16:43:04 -0400199}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500200
201// Update changes the content of the data model at the specified location with the provided data
Stephane Barbariea188d942018-10-16 16:43:04 -0400202func (pac *proxyAccessControl) Update(path string, data interface{}, strict bool, txid string, control bool) interface{} {
203 if control {
204 pac.lock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500205 log.Debugw("locked-access--update", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbariea188d942018-10-16 16:43:04 -0400206 defer pac.unlock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500207 defer log.Debugw("unlocked-access--update", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbariea188d942018-10-16 16:43:04 -0400208 }
Stephane Barbariec53a2752019-03-08 17:50:10 -0500209
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500210 result := pac.getProxy().GetRoot().Update(path, data, strict, txid, nil)
211
212 if result != nil {
213 return result.GetData()
214 }
215 return nil
Stephane Barbariea188d942018-10-16 16:43:04 -0400216}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500217
218// Add creates a new data model entry at the specified location with the provided data
Stephane Barbariea188d942018-10-16 16:43:04 -0400219func (pac *proxyAccessControl) Add(path string, data interface{}, txid string, control bool) interface{} {
220 if control {
221 pac.lock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500222 log.Debugw("locked-access--add", log.Fields{"path": path, "fullPath": pac.Path})
Stephane Barbariea188d942018-10-16 16:43:04 -0400223 defer pac.unlock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500224 defer log.Debugw("unlocked-access--add", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbariea188d942018-10-16 16:43:04 -0400225 }
Stephane Barbariec53a2752019-03-08 17:50:10 -0500226
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500227 result := pac.getProxy().GetRoot().Add(path, data, txid, nil)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500228
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500229 if result != nil {
230 return result.GetData()
231 }
232 return nil
Stephane Barbariea188d942018-10-16 16:43:04 -0400233}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500234
235// Remove discards information linked to the data model path
Stephane Barbariea188d942018-10-16 16:43:04 -0400236func (pac *proxyAccessControl) Remove(path string, txid string, control bool) interface{} {
237 if control {
238 pac.lock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500239 log.Debugw("locked-access--remove", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbariea188d942018-10-16 16:43:04 -0400240 defer pac.unlock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500241 defer log.Debugw("unlocked-access--remove", log.Fields{"path": path, "fullPath": pac.Proxy.getFullPath()})
Stephane Barbariea188d942018-10-16 16:43:04 -0400242 }
Stephane Barbariec53a2752019-03-08 17:50:10 -0500243
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500244 return pac.getProxy().GetRoot().Remove(path, txid, nil)
Stephane Barbariea188d942018-10-16 16:43:04 -0400245}