This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new e6eb7521c9 Make role name mutable (#6959)
e6eb7521c9 is described below

commit e6eb7521c949d128fc2d522e93b02ab7b1297ffa
Author: Eric Holguin <[email protected]>
AuthorDate: Fri Jul 15 12:06:22 2022 -0600

    Make role name mutable (#6959)
    
    * Make role name mutable, add parseDBerror functionality to create and 
update
    
    * Remove unused role function helper
    
    * Add space validation for role name to match TP
    
    * Increase role test coverage
    
    * Add the space validation to v3
    
    * Fix v4 tests
    
    * Fix v3 tests
    
    * Update tests
    
    * Fix internal server error when v3 update and delete id doesnt exist
    
    * Fix unit test
    
    * remove space validation and tests
    
    * Remove TP space validation for role names
    
    * Address comments
---
 traffic_ops/testing/api/v3/roles_test.go           | 142 +++++++++++++++++++--
 traffic_ops/testing/api/v3/tc-fixtures.json        |   9 ++
 traffic_ops/testing/api/v4/roles_test.go           | 123 ++++++++++++++++--
 traffic_ops/testing/api/v4/tc-fixtures.json        |   8 ++
 .../traffic_ops_golang/dbhelpers/db_helpers.go     |  19 +--
 traffic_ops/traffic_ops_golang/role/roles.go       |  69 +++++-----
 .../common/modules/form/role/FormRoleController.js |   2 +-
 .../common/modules/form/role/form.role.tpl.html    |   2 +-
 8 files changed, 308 insertions(+), 66 deletions(-)

diff --git a/traffic_ops/testing/api/v3/roles_test.go 
b/traffic_ops/testing/api/v3/roles_test.go
index 5a93ecd50f..f240367687 100644
--- a/traffic_ops/testing/api/v3/roles_test.go
+++ b/traffic_ops/testing/api/v3/roles_test.go
@@ -75,13 +75,50 @@ func TestRoles(t *testing.T) {
                                        },
                                        Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
                                },
+                               "BAD REQUEST when MISSING NAME": {
+                                       ClientSession: TOSession,
+                                       RequestBody: map[string]interface{}{
+                                               "description": "missing name",
+                                               "privLevel":   30,
+                                               "capabilities": []string{
+                                                       "all-read",
+                                                       "all-write",
+                                               },
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+                               },
+                               "BAD REQUEST when MISSING DESCRIPTION": {
+                                       ClientSession: TOSession,
+                                       RequestBody: map[string]interface{}{
+                                               "name":      "nodescription",
+                                               "privLevel": 30,
+                                               "capabilities": []string{
+                                                       "all-read",
+                                                       "all-write",
+                                               },
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+                               },
+                               "BAD REQUEST when ROLE NAME ALREADY EXISTS": {
+                                       ClientSession: TOSession,
+                                       RequestBody: map[string]interface{}{
+                                               "name":        "new_admin",
+                                               "description": "description",
+                                               "privLevel":   30,
+                                               "capabilities": []string{
+                                                       "all-read",
+                                                       "all-write",
+                                               },
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+                               },
                        },
                        "PUT": {
                                "OK when VALID request": {
-                                       EndpointId:    GetRoleID(t, 
"another_role"),
+                                       EndpointId:    GetRoleID(t, 
"update_role"),
                                        ClientSession: TOSession,
                                        RequestBody: map[string]interface{}{
-                                               "name":        "another_role",
+                                               "name":        "new_name",
                                                "description": "new updated 
description",
                                                "privLevel":   30,
                                                "capabilities": []string{
@@ -90,7 +127,75 @@ func TestRoles(t *testing.T) {
                                                },
                                        },
                                        Expectations: 
utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
-                                               
validateRoleUpdateCreateFields("another_role", 
map[string]interface{}{"Description": "new updated description"})),
+                                               
validateRoleUpdateCreateFields("new_name", map[string]interface{}{"Name": 
"new_name", "Description": "new updated description"})),
+                               },
+                               "BAD REQUEST when MISSING NAME": {
+                                       EndpointId:    GetRoleID(t, 
"another_role"),
+                                       ClientSession: TOSession,
+                                       RequestBody: map[string]interface{}{
+                                               "description": "missing name",
+                                               "privLevel":   30,
+                                               "capabilities": []string{
+                                                       "all-read",
+                                                       "all-write",
+                                               },
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+                               },
+                               "BAD REQUEST when MISSING DESCRIPTION": {
+                                       EndpointId:    GetRoleID(t, 
"another_role"),
+                                       ClientSession: TOSession,
+                                       RequestBody: map[string]interface{}{
+                                               "name":      "noDescription",
+                                               "privLevel": 30,
+                                               "capabilities": []string{
+                                                       "all-read",
+                                                       "all-write",
+                                               },
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+                               },
+                               "BAD REQUEST when ADMIN ROLE": {
+                                       EndpointId:    GetRoleID(t, "admin"),
+                                       ClientSession: TOSession,
+                                       RequestBody: map[string]interface{}{
+                                               "name":        "adminUpdated",
+                                               "privLevel":   30,
+                                               "description": "description",
+                                               "capabilities": []string{
+                                                       "all-read",
+                                                       "all-write",
+                                               },
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+                               },
+                               "NOT FOUND when ROLE DOESNT EXIST": {
+                                       EndpointId:    func() int { return 
9999999 },
+                                       ClientSession: TOSession,
+                                       RequestBody: map[string]interface{}{
+                                               "name":        "doesntexist",
+                                               "privLevel":   30,
+                                               "description": "description",
+                                               "capabilities": []string{
+                                                       "all-read",
+                                                       "all-write",
+                                               },
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusNotFound)),
+                               },
+                               "BAD REQUEST when ROLE NAME ALREADY EXISTS": {
+                                       EndpointId:    GetRoleID(t, 
"another_role"),
+                                       ClientSession: TOSession,
+                                       RequestBody: map[string]interface{}{
+                                               "name":        "new_admin",
+                                               "privLevel":   30,
+                                               "description": "description",
+                                               "capabilities": []string{
+                                                       "all-read",
+                                                       "all-write",
+                                               },
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
                                },
                                "PRECONDITION FAILED when updating with IMS & 
IUS Headers": {
                                        EndpointId:     GetRoleID(t, 
"another_role"),
@@ -193,9 +298,14 @@ func validateRoleFields(expectedResp 
map[string]interface{}) utils.CkReqFunc {
                        for _, role := range roleResp {
                                switch field {
                                case "Name":
+                                       assert.RequireNotNil(t, role.Name, 
"Expected Name to not be nil.")
                                        assert.Equal(t, expected, *role.Name, 
"Expected Name to be %v, but got %s", expected, *role.Name)
                                case "Description":
+                                       assert.RequireNotNil(t, 
role.Description, "Expected Description to not be nil.")
                                        assert.Equal(t, expected, 
*role.Description, "Expected Description to be %v, but got %s", expected, 
*role.Description)
+                               case "PrivLevel":
+                                       assert.RequireNotNil(t, role.PrivLevel, 
"Expected PrivLevel to not be nil.")
+                                       assert.Equal(t, expected, 
*role.PrivLevel, "Expected PrivLevel to be %v, but got %d", expected, 
*role.PrivLevel)
                                default:
                                        t.Errorf("Expected field: %v, does not 
exist in response", field)
                                }
@@ -219,6 +329,7 @@ func validateRoleSort() utils.CkReqFunc {
                var roleNames []string
                roleResp := resp.([]tc.Role)
                for _, role := range roleResp {
+                       assert.RequireNotNil(t, role.Name, "Expected Name to 
not be nil.")
                        roleNames = append(roleNames, *role.Name)
                }
                assert.Equal(t, true, sort.StringsAreSorted(roleNames), "List 
is not sorted by their names: %v", roleNames)
@@ -239,10 +350,12 @@ func validateRoleDescSort() utils.CkReqFunc {
                assert.RequireEqual(t, len(roleAscResp), len(roleDescResp), 
"Expected descending order response length: %d, to match ascending order 
response length %d", len(roleAscResp), len(roleDescResp))
                // Insert Role names to the front of a new list, so they are 
now reversed to be in ascending order.
                for _, role := range roleDescResp {
+                       assert.RequireNotNil(t, role.Name, "Expected Name to 
not be nil.")
                        descSortedList = append([]string{*role.Name}, 
descSortedList...)
                }
                // Insert Role names by appending to a new list, so they stay 
in ascending order.
                for _, role := range roleAscResp {
+                       assert.RequireNotNil(t, role.Name, "Expected Name to 
not be nil.")
                        ascSortedList = append(ascSortedList, *role.Name)
                }
                assert.Exactly(t, ascSortedList, descSortedList, "Role 
responses are not equal after reversal: %v - %v", ascSortedList, descSortedList)
@@ -254,6 +367,8 @@ func GetRoleID(t *testing.T, name string) func() int {
                role, _, _, err := TOSession.GetRoleByNameWithHdr(name, nil)
                assert.RequireNoError(t, err, "Get Roles Request failed with 
error:", err)
                assert.RequireEqual(t, 1, len(role), "Expected response object 
length 1, but got %d", len(role))
+               assert.RequireNotNil(t, role, "Expected role to not be nil.")
+               assert.RequireNotNil(t, role[0].ID, "Expected ID to not be 
nil.")
                return *role[0].ID
        }
 }
@@ -266,13 +381,20 @@ func CreateTestRoles(t *testing.T) {
 }
 
 func DeleteTestRoles(t *testing.T) {
-       for _, r := range testData.Roles {
-               roleID := GetRoleID(t, *r.Name)()
-               _, _, _, err := TOSession.DeleteRoleByID(roleID)
-               assert.NoError(t, err, "Expected no error while deleting role 
%s, but got %v", *r.Name, err)
+       roles, _, _, err := TOSession.GetRolesWithHdr(nil)
+       assert.NoError(t, err, "Cannot get Roles: %v", err)
+       for _, role := range roles {
+               // Don't delete active roles created by test setup
+               assert.RequireNotNil(t, role.Name, "Expected Name to not be 
nil.")
+               assert.RequireNotNil(t, role.ID, "Expected ID to not be nil.")
+               if *role.Name == "admin" || *role.Name == "disallowed" || 
*role.Name == "operations" || *role.Name == "portal" || *role.Name == 
"read-only" || *role.Name == "steering" || *role.Name == "federation" {
+                       continue
+               }
+               _, _, _, err := TOSession.DeleteRoleByID(*role.ID)
+               assert.NoError(t, err, "Expected no error while deleting role 
%s, but got %v", *role.Name, err)
                // Retrieve the Role to see if it got deleted
-               getRole, _, _, err := TOSession.GetRoleByIDWithHdr(roleID, nil)
-               assert.NoError(t, err, "Error getting Role '%s' after deletion: 
%v", *r.Name, err)
-               assert.Equal(t, 0, len(getRole), "Expected Role '%s' to be 
deleted, but it was found in Traffic Ops", *r.Name)
+               getRole, _, _, err := TOSession.GetRoleByIDWithHdr(*role.ID, 
nil)
+               assert.NoError(t, err, "Error getting Role '%s' after deletion: 
%v", *role.Name, err)
+               assert.Equal(t, 0, len(getRole), "Expected Role '%s' to be 
deleted, but it was found in Traffic Ops", *role.Name)
        }
 }
diff --git a/traffic_ops/testing/api/v3/tc-fixtures.json 
b/traffic_ops/testing/api/v3/tc-fixtures.json
index c10c8018bc..8ec7462391 100644
--- a/traffic_ops/testing/api/v3/tc-fixtures.json
+++ b/traffic_ops/testing/api/v3/tc-fixtures.json
@@ -2422,6 +2422,15 @@
                 "all-read",
                 "all-write"
             ]
+        },
+        {
+            "name": "update_role",
+            "description": "role for testing update",
+            "privLevel": 30,
+            "capabilities": [
+                "all-read",
+                "all-write"
+            ]
         }
     ],
     "servers": [
diff --git a/traffic_ops/testing/api/v4/roles_test.go 
b/traffic_ops/testing/api/v4/roles_test.go
index ba87e20226..c5124b4705 100644
--- a/traffic_ops/testing/api/v4/roles_test.go
+++ b/traffic_ops/testing/api/v4/roles_test.go
@@ -61,12 +61,48 @@ func TestRoles(t *testing.T) {
                                        Expectations:  
utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), 
validateRoleDescSort()),
                                },
                        },
+                       "POST": {
+                               "BAD REQUEST when MISSING NAME": {
+                                       ClientSession: TOSession,
+                                       RequestBody: map[string]interface{}{
+                                               "description": "missing name",
+                                               "permissions": []string{
+                                                       "all-read",
+                                                       "all-write",
+                                               },
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+                               },
+                               "BAD REQUEST when MISSING DESCRIPTION": {
+                                       ClientSession: TOSession,
+                                       RequestBody: map[string]interface{}{
+                                               "name": "noDescription",
+                                               "permissions": []string{
+                                                       "all-read",
+                                                       "all-write",
+                                               },
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+                               },
+                               "BAD REQUEST when ROLE NAME ALREADY EXISTS": {
+                                       ClientSession: TOSession,
+                                       RequestBody: map[string]interface{}{
+                                               "name":        "new_admin",
+                                               "description": "description",
+                                               "permissions": []string{
+                                                       "all-read",
+                                                       "all-write",
+                                               },
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+                               },
+                       },
                        "PUT": {
                                "OK when VALID request": {
                                        ClientSession: TOSession,
-                                       RequestOpts:   
client.RequestOptions{QueryParameters: url.Values{"name": {"another_role"}}},
+                                       RequestOpts:   
client.RequestOptions{QueryParameters: url.Values{"name": {"update_role"}}},
                                        RequestBody: map[string]interface{}{
-                                               "name":        "another_role",
+                                               "name":        "new_name",
                                                "description": "new updated 
description",
                                                "permissions": []string{
                                                        "all-read",
@@ -74,7 +110,70 @@ func TestRoles(t *testing.T) {
                                                },
                                        },
                                        Expectations: 
utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
-                                               
validateRoleUpdateCreateFields("another_role", 
map[string]interface{}{"Description": "new updated description"})),
+                                               
validateRoleUpdateCreateFields("new_name", map[string]interface{}{"Name": 
"new_name", "Description": "new updated description"})),
+                               },
+                               "BAD REQUEST when MISSING NAME": {
+                                       ClientSession: TOSession,
+                                       RequestOpts:   
client.RequestOptions{QueryParameters: url.Values{"name": {"another_role"}}},
+                                       RequestBody: map[string]interface{}{
+                                               "description": "missing name",
+                                               "permissions": []string{
+                                                       "all-read",
+                                                       "all-write",
+                                               },
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+                               },
+                               "BAD REQUEST when MISSING DESCRIPTION": {
+                                       ClientSession: TOSession,
+                                       RequestOpts:   
client.RequestOptions{QueryParameters: url.Values{"name": {"another_role"}}},
+                                       RequestBody: map[string]interface{}{
+                                               "name": "noDescription",
+                                               "permissions": []string{
+                                                       "all-read",
+                                                       "all-write",
+                                               },
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+                               },
+                               "BAD REQUEST when ADMIN ROLE": {
+                                       ClientSession: TOSession,
+                                       RequestOpts:   
client.RequestOptions{QueryParameters: url.Values{"name": {"admin"}}},
+                                       RequestBody: map[string]interface{}{
+                                               "name":        "adminUpdated",
+                                               "description": "description",
+                                               "permissions": []string{
+                                                       "all-read",
+                                                       "all-write",
+                                               },
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
+                               },
+                               "NOT FOUND when ROLE DOESNT EXIST": {
+                                       ClientSession: TOSession,
+                                       RequestOpts:   
client.RequestOptions{QueryParameters: url.Values{"name": {"doesntexist"}}},
+                                       RequestBody: map[string]interface{}{
+                                               "name":        "doesntexist",
+                                               "description": "description",
+                                               "permissions": []string{
+                                                       "all-read",
+                                                       "all-write",
+                                               },
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusNotFound)),
+                               },
+                               "BAD REQUEST when ROLE NAME ALREADY EXISTS": {
+                                       ClientSession: TOSession,
+                                       RequestOpts:   
client.RequestOptions{QueryParameters: url.Values{"name": {"another_role"}}},
+                                       RequestBody: map[string]interface{}{
+                                               "name":        "new_admin",
+                                               "description": "description",
+                                               "permissions": []string{
+                                                       "all-read",
+                                                       "all-write",
+                                               },
+                                       },
+                                       Expectations: 
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
                                },
                                "PRECONDITION FAILED when updating with IMS & 
IUS Headers": {
                                        ClientSession: TOSession,
@@ -239,14 +338,20 @@ func CreateTestRoles(t *testing.T) {
 }
 
 func DeleteTestRoles(t *testing.T) {
-       for _, r := range testData.Roles {
-               _, _, err := TOSession.DeleteRole(r.Name, 
client.NewRequestOptions())
-               assert.NoError(t, err, "Expected no error while deleting role 
%s, but got %v", r.Name, err)
+       roles, _, err := TOSession.GetRoles(client.RequestOptions{})
+       assert.NoError(t, err, "Cannot get Roles: %v - alerts: %+v", err, 
roles.Alerts)
+       for _, role := range roles.Response {
+               // Don't delete active roles created by test setup
+               if role.Name == "admin" || role.Name == "disallowed" || 
role.Name == "operations" || role.Name == "portal" || role.Name == "read-only" 
|| role.Name == "steering" || role.Name == "federation" {
+                       continue
+               }
+               _, _, err := TOSession.DeleteRole(role.Name, 
client.NewRequestOptions())
+               assert.NoError(t, err, "Expected no error while deleting role 
%s, but got %v", role.Name, err)
                // Retrieve the Role to see if it got deleted
                opts := client.NewRequestOptions()
-               opts.QueryParameters.Set("name", r.Name)
+               opts.QueryParameters.Set("name", role.Name)
                getRole, _, err := TOSession.GetRoles(opts)
-               assert.NoError(t, err, "Error getting Role '%s' after deletion: 
%v - alerts: %+v", r.Name, err, getRole.Alerts)
-               assert.Equal(t, 0, len(getRole.Response), "Expected Role '%s' 
to be deleted, but it was found in Traffic Ops", r.Name)
+               assert.NoError(t, err, "Error getting Role '%s' after deletion: 
%v - alerts: %+v", role.Name, err, getRole.Alerts)
+               assert.Equal(t, 0, len(getRole.Response), "Expected Role '%s' 
to be deleted, but it was found in Traffic Ops", role.Name)
        }
 }
diff --git a/traffic_ops/testing/api/v4/tc-fixtures.json 
b/traffic_ops/testing/api/v4/tc-fixtures.json
index 52bbf385a1..524c11f14d 100644
--- a/traffic_ops/testing/api/v4/tc-fixtures.json
+++ b/traffic_ops/testing/api/v4/tc-fixtures.json
@@ -2572,6 +2572,14 @@
                 "all-read",
                 "all-write"
             ]
+        },
+        {
+            "name": "update_role",
+            "description": "role for testing update",
+            "permissions": [
+                "all-read",
+                "all-write"
+            ]
         }
     ],
     "servers": [
diff --git a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go 
b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
index 7f6407748f..2be3f6cf88 100644
--- a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
+++ b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
@@ -542,6 +542,13 @@ func GetCapabilitiesFromRoleName(tx *sql.Tx, role string) 
([]string, error) {
        return caps, nil
 }
 
+// RoleExists returns whether or not the role with the given roleName exists, 
and any error that occurred.
+func RoleExists(tx *sql.Tx, roleID int) (bool, error) {
+       exists := false
+       err := tx.QueryRow(`SELECT EXISTS(SELECT * FROM role WHERE role.id = 
$1)`, roleID).Scan(&exists)
+       return exists, err
+}
+
 // GetDSNameFromID loads the DeliveryService's xml_id from the database, from 
the ID. Returns whether the delivery service was found, and any error.
 func GetDSNameFromID(tx *sql.Tx, id int) (tc.DeliveryServiceName, bool, error) 
{
        name := tc.DeliveryServiceName("")
@@ -1805,18 +1812,6 @@ func GetRoleIDFromName(tx *sql.Tx, roleName string) 
(int, bool, error) {
        return id, true, nil
 }
 
-// GetRoleNameFromID returns the name of the role associated with the supplied 
ID.
-func GetRoleNameFromID(tx *sql.Tx, roleID int) (string, bool, error) {
-       var name string
-       if err := tx.QueryRow(`SELECT name FROM role WHERE id = $1`, 
roleID).Scan(&name); err != nil {
-               if errors.Is(err, sql.ErrNoRows) {
-                       return name, false, nil
-               }
-               return name, false, fmt.Errorf("querying role name from ID: 
%w", err)
-       }
-       return name, true, nil
-}
-
 // GetCDNNameDomain returns the name and domain for a given CDN ID.
 func GetCDNNameDomain(cdnID int, tx *sql.Tx) (string, string, error) {
        q := `SELECT cdn.name, cdn.domain_name from cdn where cdn.id = $1`
diff --git a/traffic_ops/traffic_ops_golang/role/roles.go 
b/traffic_ops/traffic_ops_golang/role/roles.go
index 3a0e3186f9..b5a4afd75d 100644
--- a/traffic_ops/traffic_ops_golang/role/roles.go
+++ b/traffic_ops/traffic_ops_golang/role/roles.go
@@ -58,19 +58,12 @@ type TORole struct {
        PQCapabilities *pq.StringArray `json:"-" db:"capabilities"`
 }
 
-func updateLegacyRoleQuery() string {
+func updateRoleQuery() string {
        return `UPDATE
 role SET
 name=$1,
 description=$2
-WHERE id=$3 RETURNING last_updated`
-}
-
-func updateRoleQuery() string {
-       return `UPDATE
-role SET
-description=$1
-WHERE name=$2 RETURNING last_updated`
+WHERE name=$3 RETURNING last_updated`
 }
 
 func (v *TORole) GetLastUpdated() (*time.Time, bool, error) {
@@ -224,6 +217,13 @@ func (role *TORole) Read(h http.Header, useIMS bool) 
([]interface{}, error, erro
 }
 
 func (role *TORole) Update(h http.Header) (error, error, int) {
+
+       if ok, err := dbhelpers.RoleExists(role.ReqInfo.Tx.Tx, *role.ID); err 
!= nil {
+               return nil, fmt.Errorf("verifying Role exists: %w", err), 
http.StatusInternalServerError
+       } else if !ok {
+               return errors.New("role not found"), nil, http.StatusNotFound
+       }
+
        var isAdmin bool
        if err := role.ReqInfo.Tx.Get(&isAdmin, isAdminQuery, role.ID); err != 
nil {
                return nil, fmt.Errorf("checking if Role to be modified is 
'%s': %w", tc.AdminRoleName, err), http.StatusInternalServerError
@@ -259,6 +259,13 @@ func (role *TORole) Update(h http.Header) (error, error, 
int) {
 }
 
 func (role *TORole) Delete() (error, error, int) {
+
+       if ok, err := dbhelpers.RoleExists(role.ReqInfo.Tx.Tx, *role.ID); err 
!= nil {
+               return nil, fmt.Errorf("verifying Role exists: %w", err), 
http.StatusInternalServerError
+       } else if !ok {
+               return errors.New("role not found"), nil, http.StatusNotFound
+       }
+
        assignedUsers := 0
        if err := role.ReqInfo.Tx.Get(&assignedUsers, "SELECT COUNT(id) FROM 
public.tm_user WHERE role=$1", role.ID); err != nil {
                return nil, errors.New("role delete counting assigned users: " 
+ err.Error()), http.StatusInternalServerError
@@ -332,12 +339,7 @@ func deleteQuery() string {
 
 // Update will modify the role identified by the role name.
 func Update(w http.ResponseWriter, r *http.Request) {
-       var roleID int
-       var roleDesc string
-       var roleCapabilities []string
        var roleV4 tc.RoleV4
-       var ok bool
-       var err error
 
        inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"name"}, nil)
        if userErr != nil || sysErr != nil {
@@ -352,24 +354,24 @@ func Update(w http.ResponseWriter, r *http.Request) {
                return
        }
 
-       roleName := inf.Params["name"]
-       if roleName == tc.AdminRoleName {
-               api.HandleErr(w, r, tx, http.StatusBadRequest, 
cannotModifyAdminError, nil)
+       if err := roleV4.Validate(); err != nil {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
                return
        }
 
-       if err := roleV4.Validate(); err != nil {
-               api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+       currentRoleName := inf.Params["name"]
+       if currentRoleName == tc.AdminRoleName {
+               api.HandleErr(w, r, tx, http.StatusBadRequest, 
cannotModifyAdminError, nil)
                return
        }
+
        missing := inf.User.MissingPermissions(roleV4.Permissions...)
        if len(missing) != 0 {
                api.HandleErr(w, r, tx, http.StatusForbidden, 
fmt.Errorf("cannot request more than assigned permissions, current user needs 
%s permissions", strings.Join(missing, ",")), nil)
                return
        }
-       roleDesc = roleV4.Description
-       roleCapabilities = roleV4.Permissions
-       roleID, ok, err = dbhelpers.GetRoleIDFromName(tx, roleName)
+
+       roleID, ok, err := dbhelpers.GetRoleIDFromName(tx, currentRoleName)
        if err != nil {
                api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, 
err)
                return
@@ -378,7 +380,7 @@ func Update(w http.ResponseWriter, r *http.Request) {
                return
        }
 
-       existingLastUpdated, found, err := api.GetLastUpdatedByName(inf.Tx, 
roleName, "role")
+       existingLastUpdated, found, err := api.GetLastUpdatedByName(inf.Tx, 
currentRoleName, "role")
        if err == nil && found == false {
                api.HandleErr(w, r, tx, http.StatusNotFound, errors.New("no 
such role"), nil)
                return
@@ -392,9 +394,10 @@ func Update(w http.ResponseWriter, r *http.Request) {
                api.HandleErr(w, r, tx, http.StatusPreconditionFailed, 
api.ResourceModifiedError, nil)
                return
        }
-       rows, err := tx.Query(updateRoleQuery(), roleDesc, roleName)
+       rows, err := tx.Query(updateRoleQuery(), roleV4.Name, 
roleV4.Description, currentRoleName)
        if err != nil {
-               api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, 
fmt.Errorf("updating role: %w", err))
+               usrErr, sysErr, code := api.ParseDBError(err)
+               api.HandleErr(w, r, tx, code, usrErr, fmt.Errorf("updating 
role: %w", sysErr))
                return
        }
        defer rows.Close()
@@ -411,26 +414,25 @@ func Update(w http.ResponseWriter, r *http.Request) {
                roleV4.LastUpdated = &lastUpdated
        }
 
-       userErr, sysErr, errCode = deleteRoleCapabilityAssociations(inf.Tx, 
roleName)
+       userErr, sysErr, errCode = deleteRoleCapabilityAssociations(inf.Tx, 
roleV4.Name)
        if userErr != nil || sysErr != nil {
                api.HandleErr(w, r, tx, errCode, userErr, sysErr)
                return
        }
-       userErr, sysErr, errCode = createRoleCapabilityAssociations(inf.Tx, 
roleID, &roleCapabilities)
+       userErr, sysErr, errCode = createRoleCapabilityAssociations(inf.Tx, 
roleID, &roleV4.Permissions)
        if userErr != nil || sysErr != nil {
                api.HandleErr(w, r, tx, errCode, userErr, sysErr)
                return
        }
        alerts := tc.CreateAlerts(tc.SuccessLevel, "role was updated.")
        var roleResponse interface{}
-       capabilities := roleCapabilities
        roleResponse = tc.RoleV4{
-               Name:        roleName,
-               Permissions: capabilities,
-               Description: roleDesc,
+               Name:        roleV4.Name,
+               Permissions: roleV4.Permissions,
+               Description: roleV4.Description,
        }
        api.WriteAlertsObj(w, r, http.StatusOK, alerts, roleResponse)
-       changeLogMsg := fmt.Sprintf("ROLE: %s, ID: %d, ACTION: Updated Role", 
roleName, roleID)
+       changeLogMsg := fmt.Sprintf("ROLE: %s, ID: %d, ACTION: Updated Role", 
roleV4.Name, roleID)
        api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx)
 }
 
@@ -559,7 +561,8 @@ func Create(w http.ResponseWriter, r *http.Request) {
 
        rows, err := tx.Query(createQuery(), roleName, roleDesc, privLevel)
        if err != nil {
-               api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, 
fmt.Errorf("creating role: %w", err))
+               usrErr, sysErr, code := api.ParseDBError(err)
+               api.HandleErr(w, r, tx, code, usrErr, fmt.Errorf("creating 
role: %w", sysErr))
                return
        }
        defer rows.Close()
diff --git 
a/traffic_portal/app/src/common/modules/form/role/FormRoleController.js 
b/traffic_portal/app/src/common/modules/form/role/FormRoleController.js
index dee50fadd4..c6010d09ba 100644
--- a/traffic_portal/app/src/common/modules/form/role/FormRoleController.js
+++ b/traffic_portal/app/src/common/modules/form/role/FormRoleController.js
@@ -46,7 +46,7 @@ var FormRoleController = function(roles, $scope, $location, 
formUtils, locationU
                        if ($scope.role.name === "admin") {
                                _pattern = /^admin$/;
                        } else {
-                               _pattern = /^(?!admin$)\S+$/;
+                               _pattern = /^(?!admin$).+$/;
                        }
                }
                return _pattern;
diff --git a/traffic_portal/app/src/common/modules/form/role/form.role.tpl.html 
b/traffic_portal/app/src/common/modules/form/role/form.role.tpl.html
index 84fc0c48c8..1f0a5aeb33 100644
--- a/traffic_portal/app/src/common/modules/form/role/form.role.tpl.html
+++ b/traffic_portal/app/src/common/modules/form/role/form.role.tpl.html
@@ -37,7 +37,7 @@ under the License.
                 <div class="col-md-10 col-sm-10 col-xs-12">
                     <input name="name" id="name" type="text" 
class="form-control" ng-model="role.name" 
ng-readonly="!hasPropertyError(roleForm.name, 'pattern') && role.name === 
'admin'" ng-pattern="namePattern()" required autofocus/>
                     <small class="input-error" 
ng-show="hasPropertyError(roleForm.name, 'required')">Required</small>
-                    <small class="input-error" 
ng-show="hasPropertyError(roleForm.name, 'pattern')">No spaces, and cannot be 
named "admin"</small>
+                    <small class="input-error" 
ng-show="hasPropertyError(roleForm.name, 'pattern')">Cannot be named 
"admin"</small>
                     <span ng-show="hasError(roleForm.name)" 
class="form-control-feedback"><i class="fa fa-times"></i></span>
                 </div>
             </div>

Reply via email to