diff --git a/gnmi_server/clientCertAuth.go b/gnmi_server/clientCertAuth.go index 48fecf3ca..4e770f226 100644 --- a/gnmi_server/clientCertAuth.go +++ b/gnmi_server/clientCertAuth.go @@ -5,6 +5,7 @@ import ( "crypto/x509" "io" "net/http" + "strings" "time" "github.com/sonic-net/sonic-gnmi/common_utils" "github.com/sonic-net/sonic-gnmi/swsscommon" @@ -260,7 +261,11 @@ func PopulateAuthStructByCommonName(certCommonName string, auth *common_utils.Au var fieldValuePairs = configDbConnector.Get_entry(serviceConfigTableName, certCommonName) if fieldValuePairs.Size() > 0 { - if fieldValuePairs.Has_key("role") { + if fieldValuePairs.Has_key("role@") { + var role = fieldValuePairs.Get("role@") + auth.Roles = strings.Split(role, ",") + } else if fieldValuePairs.Has_key("role") { + // Backward compatibility for single role DB schema var role = fieldValuePairs.Get("role") auth.Roles = []string{role} } diff --git a/gnmi_server/server_test.go b/gnmi_server/server_test.go index 1cfbb31df..7a16d30ca 100644 --- a/gnmi_server/server_test.go +++ b/gnmi_server/server_test.go @@ -5036,6 +5036,71 @@ func TestClientCertAuthenAndAuthor(t *testing.T) { swsscommon.DeleteDBConnector(configDb) } +func TestClientCertAuthenAndAuthorMultiRole(t *testing.T) { + if !swsscommon.SonicDBConfigIsInit() { + swsscommon.SonicDBConfigInitialize() + } + + var configDb = swsscommon.NewDBConnector("CONFIG_DB", uint(0), true) + var gnmiTable = swsscommon.NewTable(configDb, "GNMI_CLIENT_CERT") + configDb.Flushdb() + + // initialize err variable + err := status.Error(codes.Unauthenticated, "") + + // when config table is empty, will authorize with PopulateAuthStruct + mockpopulate := gomonkey.ApplyFunc(PopulateAuthStruct, func(username string, auth *common_utils.AuthInfo, r []string) error { + return nil + }) + defer mockpopulate.Reset() + + // check auth with nil cert name + ctx, cancel := CreateAuthorizationCtx() + ctx, err = ClientCertAuthenAndAuthor(ctx, "", false) + if err != nil { + t.Errorf("CommonNameMatch with empty config table should success: %v", err) + } + + cancel() + + // check get 1 cert name + ctx, cancel = CreateAuthorizationCtx() + configDb.Flushdb() + gnmiTable.Hset("certname1", "role@", "readwrite") + ctx, err = ClientCertAuthenAndAuthor(ctx, "GNMI_CLIENT_CERT", false) + if err != nil { + t.Errorf("CommonNameMatch with correct cert name should success: %v", err) + } + + cancel() + + // check get multiple cert names + ctx, cancel = CreateAuthorizationCtx() + configDb.Flushdb() + gnmiTable.Hset("certname1", "role@", "readwrite") + gnmiTable.Hset("certname2", "role@", "readonly") + ctx, err = ClientCertAuthenAndAuthor(ctx, "GNMI_CLIENT_CERT", false) + if err != nil { + t.Errorf("CommonNameMatch with correct cert name should success: %v", err) + } + + cancel() + + // check a invalid cert cname + ctx, cancel = CreateAuthorizationCtx() + configDb.Flushdb() + gnmiTable.Hset("certname2", "role@", "readonly") + ctx, err = ClientCertAuthenAndAuthor(ctx, "GNMI_CLIENT_CERT", false) + if err == nil { + t.Errorf("CommonNameMatch with invalid cert name should fail: %v", err) + } + + cancel() + + swsscommon.DeleteTable(gnmiTable) + swsscommon.DeleteDBConnector(configDb) +} + type MockServerStream struct { grpc.ServerStream }