Skip to content

Commit 45b3556

Browse files
Implement translib backend logic for the gNSI Authz (#200)
This commit adds the necessary transformer code to read authorization policy counters and related state from the Redis databases. Signed-off-by: kanchanavelusamy <velusamyk@google.com> Co-authored-by: kanchanavelusamy <velusamyk@google.com>
1 parent 1e80bfa commit 45b3556

File tree

3 files changed

+256
-14
lines changed

3 files changed

+256
-14
lines changed

config/transformer/models_list

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
#List yang models transformer need to load
2-
openconfig-acl.yang
32
openconfig-acl-annot.yang
4-
openconfig-sampling-sflow.yang
5-
openconfig-sampling-sflow-annot.yang
3+
openconfig-acl.yang
4+
openconfig-if-aggregate.yang
65
openconfig-if-ethernet.yang
7-
openconfig-interfaces.yang
8-
openconfig-interfaces-annot.yang
96
openconfig-if-ip.yang
10-
openconfig-if-aggregate.yang
11-
openconfig-mclag.yang
7+
openconfig-interfaces-annot.yang
8+
openconfig-interfaces.yang
129
openconfig-mclag-annot.yang
10+
openconfig-mclag.yang
11+
openconfig-sampling-sflow-annot.yang
12+
openconfig-sampling-sflow.yang
13+
openconfig-system-annot.yang
1314
openconfig-vlan.yang
1415
gnsi-authz.yang
1516
gnsi-pathz.yang

translib/tlerr/tlerr.go

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,11 @@ Hence, it cannot occur here.
3030
package tlerr
3131

3232
import (
33-
// "fmt"
33+
"errors"
3434
"github.com/Azure/sonic-mgmt-common/cvl"
35+
"github.com/golang/glog"
3536
"golang.org/x/text/language"
3637
"golang.org/x/text/message"
37-
// "errors"
38-
// "strings"
3938
)
4039

4140
var p *message.Printer
@@ -190,3 +189,32 @@ type TranslibBusy struct {
190189
func (e TranslibBusy) Error() string {
191190
return p.Sprintf("Translib Busy")
192191
}
192+
193+
func IsTranslibRedisClientEntryNotExist(err error) bool {
194+
switch err.(type) {
195+
case *TranslibRedisClientEntryNotExist, TranslibRedisClientEntryNotExist:
196+
return true
197+
}
198+
return false
199+
}
200+
201+
// isDBEntryNotExistError returns `true` if `err` is (or wraps around) an error
202+
// of type `TranslibRedisClientEntryNotExist`.
203+
func isDBEntryNotExistError(err error) bool {
204+
if IsTranslibRedisClientEntryNotExist(err) {
205+
return true
206+
}
207+
pdberr := &TranslibRedisClientEntryNotExist{}
208+
return errors.As(err, &TranslibRedisClientEntryNotExist{}) || errors.As(err, &pdberr)
209+
}
210+
211+
// ErrorSeverity based on `err` calculates the VLOG level.
212+
func ErrorSeverity(err error) glog.Level {
213+
if err == nil {
214+
return 3
215+
}
216+
if isDBEntryNotExistError(err) {
217+
return 3
218+
}
219+
return 0
220+
}

translib/transformer/xfmr_system.go

Lines changed: 217 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/Azure/sonic-mgmt-common/translib/db"
1010
"github.com/Azure/sonic-mgmt-common/translib/ocbinds"
11+
"github.com/Azure/sonic-mgmt-common/translib/tlerr"
1112
log "github.com/golang/glog"
1213
ygot "github.com/openconfig/ygot/ygot"
1314
)
@@ -18,7 +19,31 @@ const (
1819
/** Credential Tables **/
1920
CREDENTIALS_TBL = "CREDENTIALS"
2021
CRED_PATHZ_TBL = "CREDENTIALS|PATHZ_POLICY"
22+
CRED_AUTHZ_TBL = "CREDENTIALS|AUTHZ_POLICY"
2123
CERT_TBL = "CREDENTIALS|CERT"
24+
AUTHZ_TBL = "AUTHZ_TABLE"
25+
ACCEPTS = "permitted"
26+
REJECTS = "denied"
27+
cntResult = "cntResult"
28+
tsResult = "tsResult"
29+
30+
/** System Root paths **/
31+
SYSTEM_ROOT = "/openconfig-system:system"
32+
33+
/** Pathz paths **/
34+
GRPC_OC_SERVERS = SYSTEM_ROOT + "/openconfig-system-grpc:grpc-servers"
35+
GRPC_SERVERS = SYSTEM_ROOT + "/grpc-servers"
36+
GRPC_SERVER = GRPC_OC_SERVERS + "/grpc-server"
37+
38+
/** Authz paths **/
39+
AUTHZ_POLICY_COUNTERS = GRPC_SERVER + "/authz-policy-counters"
40+
ALL_AUTHZ = AUTHZ_POLICY_COUNTERS + "/rpcs"
41+
SINGLE_AUTHZ = ALL_AUTHZ + "/rpc"
42+
AUTHZ_STATE = SINGLE_AUTHZ + "/state"
43+
AUTHZ_SUCCESS = AUTHZ_STATE + "/access-accepts"
44+
AUTHZ_SUCCESS_TIMESTAMP = AUTHZ_STATE + "/last-access-accept"
45+
AUTHZ_FAILED = AUTHZ_STATE + "/access-rejects"
46+
AUTHZ_FAILED_TIMESTAMP = AUTHZ_STATE + "/last-access-reject"
2247
)
2348

2449
type sshState struct {
@@ -46,6 +71,8 @@ func init() {
4671
XlateFuncBind("DbToYang_grpc_server_key_xfmr", DbToYang_grpc_server_key_xfmr)
4772
XlateFuncBind("DbToYang_ssh_server_state_xfmr", DbToYang_ssh_server_state_xfmr)
4873
XlateFuncBind("Subscribe_ssh_server_state_xfmr", Subscribe_ssh_server_state_xfmr)
74+
XlateFuncBind("DbToYang_authz_policy_xfmr", DbToYang_authz_policy_xfmr)
75+
XlateFuncBind("Subscribe_authz_policy_xfmr", Subscribe_authz_policy_xfmr)
4976
}
5077

5178
type grpcState struct {
@@ -101,6 +128,15 @@ var Subscribe_ssh_server_state_xfmr SubTreeXfmrSubscribe = func(inParams XfmrSub
101128
nOpts: &notificationOpts{mInterval: 0, pType: OnChange},
102129
}, nil
103130
}
131+
var Subscribe_authz_policy_xfmr SubTreeXfmrSubscribe = func(inParams XfmrSubscInParams) (XfmrSubscOutParams, error) {
132+
log.V(3).Infof("Subscribe_authz_policy_xfmr:%s", inParams.requestURI)
133+
return XfmrSubscOutParams{
134+
dbDataMap: RedisDbSubscribeMap{
135+
db.StateDB: {"CREDENTIALS": {"AUTHZ_POLICY|gnxi": {}}}},
136+
onChange: OnchangeEnable,
137+
nOpts: &notificationOpts{mInterval: 0, pType: OnChange},
138+
}, nil
139+
}
104140

105141
var DbToYang_ssh_server_state_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) error {
106142
var state sshState
@@ -156,10 +192,38 @@ var DbToYang_ssh_server_state_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParam
156192
sysObj.SshServer.State.Counters.AccessRejects = &state.counters.accessRejects
157193
sysObj.SshServer.State.Counters.LastAccessAccept = &state.counters.lastAccessAccept
158194
sysObj.SshServer.State.Counters.LastAccessReject = &state.counters.lastAccessReject
195+
return nil
196+
}
197+
var DbToYang_authz_policy_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) error {
198+
var state certData
199+
200+
table, err := inParams.dbs[inParams.curDb].GetEntry(&db.TableSpec{Name: CRED_AUTHZ_TBL}, db.Key{Comp: []string{GNXI_ID}})
201+
if err != nil {
202+
log.V(3).Infof("Failed to read from StateDB: %v", inParams.table)
203+
return err
204+
}
205+
206+
state.version = table.Get("authz_version")
207+
time := table.Get("authz_created_on")
208+
if state.created, err = strconv.ParseUint(time, 10, 64); err != nil && time != "" {
209+
log.V(3).Infof("Couldn't find authz_created_on: %v", err)
210+
}
211+
212+
sysObj := getAppRootObject(inParams)
213+
ygot.BuildEmptyTree(sysObj.Aaa.Authorization.State)
214+
215+
sysObj.Aaa.Authorization.State.GrpcAuthzPolicyCreatedOn = &state.created
216+
sysObj.Aaa.Authorization.State.GrpcAuthzPolicyVersion = &state.version
159217

160218
return nil
161219
}
162220

221+
var DbToYang_grpc_server_key_xfmr KeyXfmrDbToYang = func(inParams XfmrParams) (map[string]interface{}, error) {
222+
log.V(3).Info("DbToYang_grpc_server_key_xfmr root, uri: ", inParams.ygRoot, inParams.uri)
223+
224+
return map[string]interface{}{"name": NewPathInfo(inParams.uri).Var("name")}, nil
225+
}
226+
163227
var Subscribe_grpc_server_xfmr SubTreeXfmrSubscribe = func(inParams XfmrSubscInParams) (XfmrSubscOutParams, error) {
164228
pathInfo := NewPathInfo(inParams.uri)
165229
serverName := pathInfo.Var("name")
@@ -198,14 +262,20 @@ var Subscribe_grpc_server_xfmr SubTreeXfmrSubscribe = func(inParams XfmrSubscInP
198262
result.onChange = OnchangeEnable
199263
result.nOpts = &notificationOpts{mInterval: 60, pType: Sample}
200264
}
265+
201266
return result, nil
202267
}
203268

204269
var DbToYang_grpc_server_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) error {
205270
pathInfo := NewPathInfo(inParams.uri)
206271
serverNames := []string{pathInfo.Var("name")}
272+
targetUriPath, err := getYangPathFromUri(pathInfo.Path)
273+
if err != nil {
274+
log.V(0).Infof("Error Parsing Uri Path, err: %v", err)
275+
}
207276
if log.V(3) {
208277
log.Info("SubtreeXfmrFunc - Uri SYS AUTH: ", inParams.uri)
278+
log.Info("TARGET URI PATH SYS AUTH:", targetUriPath)
209279
log.Info("names:", serverNames)
210280
}
211281
stateDb := inParams.dbs[db.StateDB]
@@ -289,13 +359,156 @@ var DbToYang_grpc_server_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) er
289359
serverObj.State.GnmiPathzPolicyCreatedOn = &state.pathzCreated
290360
serverObj.State.GnmiPathzPolicyVersion = &state.pathzVersion
291361

292-
}
362+
// Authz counter
363+
authzTables, err := stateDb.GetTable(&db.TableSpec{Name: AUTHZ_TBL})
364+
if err != nil {
365+
log.V(tlerr.ErrorSeverity(err)).Infof("getAuthzPolicyCounter failed to get AUTHZ_TBL, err: %v", err)
366+
return err
367+
}
368+
369+
rpcString := pathInfo.Var("name#2")
370+
rpcStrings := []string{rpcString}
371+
372+
if rpcString == "" || rpcString == "*" {
373+
rpcStrings = []string{}
374+
rpcStrings, err = getAllRpcs(authzTables, serverName)
375+
if err != nil {
376+
log.V(tlerr.ErrorSeverity(err)).Infof("Failed get all authz rpcs, err: %v", err)
377+
return err
378+
}
379+
}
380+
381+
ygot.BuildEmptyTree(serverObj.AuthzPolicyCounters)
382+
for _, rpcString := range rpcStrings {
383+
service, rpc, err := getServiceRpc(rpcString)
384+
if err != nil {
385+
log.V(0).Infof("invalid RPC method %s", rpcString)
386+
continue
387+
}
388+
389+
authzPolicyData := getAuthzPolicyCounter(authzTables, serverName, rpcString)
390+
rpcObj, ok := serverObj.AuthzPolicyCounters.Rpcs.Rpc[rpcString]
391+
if !ok {
392+
rpcObj, err = serverObj.AuthzPolicyCounters.Rpcs.NewRpc(rpcString)
393+
if err != nil {
394+
log.V(0).Infof("serverObj.AuthzPolicyCounters.Rpcs.NewRpc(%v) failed: %v", rpcString, err)
395+
continue
396+
}
397+
}
398+
ygot.BuildEmptyTree(rpcObj)
399+
400+
// If targetUriPath is a parent AUTHZ_STATE, i.e.root path, all counters and timestamps should be returned
401+
allAuthzCounter := strings.HasPrefix(AUTHZ_STATE, targetUriPath) || targetUriPath == GRPC_OC_SERVERS
293402

403+
tmpCnt := make(map[string]*uint64)
404+
tmpTs := make(map[string]*uint64)
405+
if cnt, ok := authzPolicyData[cntResult]; ok {
406+
tmpCnt = cnt
407+
}
408+
if ts, ok := authzPolicyData[tsResult]; ok {
409+
tmpTs = ts
410+
}
411+
// Handle root paths here.
412+
if allAuthzCounter {
413+
ygot.BuildEmptyTree(rpcObj.State)
414+
rpcObj.State.AccessAccepts = tmpCnt["*|"+serverName+"|"+service+"|"+rpc+"|"+ACCEPTS]
415+
rpcObj.State.LastAccessAccept = tmpTs["*|"+serverName+"|"+service+"|"+rpc+"|"+ACCEPTS]
416+
rpcObj.State.AccessRejects = tmpCnt["*|"+serverName+"|"+service+"|"+rpc+"|"+REJECTS]
417+
rpcObj.State.LastAccessReject = tmpTs["*|"+serverName+"|"+service+"|"+rpc+"|"+REJECTS]
418+
419+
} else {
420+
// Handle leaf paths here.
421+
switch targetUriPath {
422+
case AUTHZ_SUCCESS:
423+
rpcObj.State.AccessAccepts = tmpCnt["*|"+serverName+"|"+service+"|"+rpc+"|"+ACCEPTS]
424+
case AUTHZ_SUCCESS_TIMESTAMP:
425+
rpcObj.State.LastAccessAccept = tmpTs["*|"+serverName+"|"+service+"|"+rpc+"|"+ACCEPTS]
426+
case AUTHZ_FAILED:
427+
rpcObj.State.AccessRejects = tmpCnt["*|"+serverName+"|"+service+"|"+rpc+"|"+REJECTS]
428+
case AUTHZ_FAILED_TIMESTAMP:
429+
rpcObj.State.LastAccessReject = tmpTs["*|"+serverName+"|"+service+"|"+rpc+"|"+REJECTS]
430+
}
431+
}
432+
}
433+
}
294434
return nil
295435
}
296436

297-
var DbToYang_grpc_server_key_xfmr KeyXfmrDbToYang = func(inParams XfmrParams) (map[string]interface{}, error) {
298-
log.V(3).Info("DbToYang_grpc_server_key_xfmr root, uri: ", inParams.ygRoot, inParams.uri)
437+
func getAuthzPolicyCounter(authzTables db.Table, server string, rpcString string) map[string]map[string]*uint64 {
438+
cntMap := make(map[string]*uint64)
439+
tsMap := make(map[string]*uint64)
299440

300-
return map[string]interface{}{"name": NewPathInfo(inParams.uri).Var("name")}, nil
441+
for _, oper := range []string{ACCEPTS, REJECTS} {
442+
var service string
443+
var rpc string
444+
service, rpc, err := getServiceRpc(rpcString)
445+
if err != nil {
446+
log.V(0).Infof("invalid RPC method %s", rpcString)
447+
continue
448+
}
449+
450+
pattern := "*|" + server + "|" + service + "|" + rpc + "|" + oper
451+
key := db.NewKey(server, service, rpc, oper)
452+
453+
// Sum the data collected
454+
value, err := authzTables.GetEntry(*key)
455+
if err != nil {
456+
log.V(tlerr.ErrorSeverity(err)).Infof("Cannot get value from %v table for %v, err: %v", AUTHZ_TBL, key, err)
457+
continue
458+
}
459+
460+
c := value.Get("count")
461+
if c != "" {
462+
if dbCnt, err := strconv.ParseUint(c, 10, 64); err == nil {
463+
cntMap[pattern] = &dbCnt
464+
} else {
465+
log.V(tlerr.ErrorSeverity(err)).Infof("Failed to convert counters from DB for authz, err: %v", err)
466+
}
467+
}
468+
469+
ts := value.Get("timestamp")
470+
if ts != "" {
471+
if dbTs, err := strconv.ParseUint(ts, 10, 64); err == nil {
472+
tsMap[pattern] = &dbTs
473+
} else {
474+
log.V(tlerr.ErrorSeverity(err)).Infof("Failed to convert timestamp for counters from DB for authz, err: %v", err)
475+
}
476+
}
477+
}
478+
return map[string]map[string]*uint64{cntResult: cntMap, tsResult: tsMap}
479+
}
480+
481+
func getServiceRpc(rpcString string) (string, string, error) {
482+
strs := strings.Split(rpcString, "/")
483+
if len(strs) == 3 {
484+
return strs[1], strs[2], nil
485+
}
486+
487+
return "", "", errors.New("invalid RPC method " + rpcString)
488+
}
489+
490+
func getAllRpcs(authzTables db.Table, server string) ([]string, error) {
491+
var res []string
492+
check := make(map[string]bool)
493+
authzTableKeys, err := authzTables.GetKeys()
494+
if err != nil {
495+
log.V(tlerr.ErrorSeverity(err)).Infof("Cannot get all keys from %v table, err: %v", AUTHZ_TBL, err)
496+
return []string{}, err
497+
}
498+
for _, authzTableKey := range authzTableKeys {
499+
if len(authzTableKey.Comp) != 4 {
500+
log.V(3).Infof("invalid number of Comps for authzTableKey %v.", authzTableKey)
501+
continue
502+
}
503+
if authzTableKey.Comp[0] != server {
504+
continue
505+
}
506+
key := "/" + authzTableKey.Comp[1] + "/" + authzTableKey.Comp[2]
507+
if val, ok := check[key]; !ok || !val {
508+
res = append(res, key)
509+
check[key] = true
510+
}
511+
}
512+
513+
return res, nil
301514
}

0 commit comments

Comments
 (0)