You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by ra...@apache.org on 2021/07/09 14:46:30 UTC

[trafficcontrol] branch master updated: Every endpoint that modifies a CDN, or components related to a CDN, needs to be "lock safe" (#5974)

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

rawlin 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 badeef8  Every endpoint that modifies a CDN, or components related to a CDN, needs to be "lock safe" (#5974)
badeef8 is described below

commit badeef8ff6a9c895b6bcfb324de5c154bc668dd6
Author: Srijeet Chatterjee <30...@users.noreply.github.com>
AuthorDate: Fri Jul 9 08:46:22 2021 -0600

    Every endpoint that modifies a CDN, or components related to a CDN, needs to be "lock safe" (#5974)
    
    * wip
    
    * adding lock checks to more endpoints
    
    * add topology checks
    
    * adding lock checks to more routes
    
    * add lock checks to remaining endpoints
    
    * fix tests
    
    * Add test
    
    * add profiles and servers tests
    
    * adding more tests
    
    * fix build
    
    * Add godocs
    
    * change ordering of imports
    
    * avoid running sql queries in a loop
    
    * Adding more debug into error msgs
    
    * code review fixes
    
    * go fmt
    
    * fix unit tests + cleanup
    
    * code review fixes
    
    * fix merge
    
    * move check inside loop
---
 traffic_ops/testing/api/v4/cachegroups_test.go     |  99 +++++++++
 traffic_ops/testing/api/v4/cdns_test.go            |  87 ++++++++
 .../testing/api/v4/deliveryservices_test.go        | 115 ++++++++++
 .../testing/api/v4/profile_parameters_test.go      |  98 +++++++++
 traffic_ops/testing/api/v4/profiles_test.go        | 125 +++++++++++
 traffic_ops/testing/api/v4/servers_test.go         | 131 +++++++++++
 .../traffic_ops_golang/cachegroup/cachegroups.go   |  10 +
 .../traffic_ops_golang/cachegroup/dspost.go        |  10 +
 .../cachegroupparameter/parameters.go              |  15 +-
 traffic_ops/traffic_ops_golang/cdn/cdns.go         |  16 +-
 traffic_ops/traffic_ops_golang/cdn/dnssec.go       |  13 +-
 traffic_ops/traffic_ops_golang/cdn/genksk.go       |  11 +-
 traffic_ops/traffic_ops_golang/cdn/namedelete.go   |   6 +
 .../traffic_ops_golang/cdn_lock/cdn_lock.go        |   6 -
 .../traffic_ops_golang/dbhelpers/db_helpers.go     | 245 ++++++++++++++++++++-
 .../traffic_ops_golang/deliveryservice/acme.go     |  17 ++
 .../deliveryservice/acme_renew.go                  |  18 +-
 .../deliveryservice/autorenewcerts.go              |  19 +-
 .../deliveryservice/deliveryservices.go            |  36 ++-
 .../deliveryservices_required_capabilities.go      |  18 +-
 .../deliveryservices_required_capabilities_test.go |   6 +
 .../traffic_ops_golang/deliveryservice/keys.go     |  12 +-
 .../traffic_ops_golang/deliveryservice/safe.go     |  12 +
 .../deliveryservice/servers/delete.go              |   7 +
 .../deliveryservice/servers/servers.go             |  31 +++
 .../traffic_ops_golang/deliveryservice/sslkeys.go  |  12 +-
 .../traffic_ops_golang/deliveryservice/urlkey.go   |  52 ++++-
 .../deliveryservicesregexes.go                     |  34 ++-
 traffic_ops/traffic_ops_golang/federations/ds.go   |  18 ++
 traffic_ops/traffic_ops_golang/origin/origins.go   |  28 +++
 traffic_ops/traffic_ops_golang/profile/copy.go     |  27 ++-
 .../traffic_ops_golang/profile/copy_test.go        |   4 +
 .../traffic_ops_golang/profile/profile_import.go   |  12 +-
 traffic_ops/traffic_ops_golang/profile/profiles.go |  63 +++++-
 .../profileparameter/postparameterprofile.go       |  13 ++
 .../profileparameter/postprofileparameter.go       |  10 +
 .../profileparameter/postprofileparametersbyid.go  |  10 +
 .../postprofileparametersbyname.go                 |  10 +
 .../profileparameter/profile_parameters.go         |  28 ++-
 traffic_ops/traffic_ops_golang/routing/routes.go   |   1 -
 .../traffic_ops_golang/server/put_status.go        |   3 +
 traffic_ops/traffic_ops_golang/server/servers.go   |  83 ++++++-
 .../server/servers_assignment.go                   |   6 +-
 .../server/servers_server_capability.go            |  19 ++
 .../staticdnsentry/staticdnsentry.go               |  44 +++-
 .../steeringtargets/steeringtargets.go             |  24 ++
 .../traffic_ops_golang/topology/topologies.go      |  34 ++-
 .../traffic_ops_golang/urisigning/urisigning.go    |  31 ++-
 48 files changed, 1682 insertions(+), 47 deletions(-)

diff --git a/traffic_ops/testing/api/v4/cachegroups_test.go b/traffic_ops/testing/api/v4/cachegroups_test.go
index 90dc150..50bd571 100644
--- a/traffic_ops/testing/api/v4/cachegroups_test.go
+++ b/traffic_ops/testing/api/v4/cachegroups_test.go
@@ -26,6 +26,7 @@ import (
 
 	"github.com/apache/trafficcontrol/lib/go-rfc"
 	"github.com/apache/trafficcontrol/lib/go-tc"
+	"github.com/apache/trafficcontrol/lib/go-util"
 	client "github.com/apache/trafficcontrol/traffic_ops/v4-client"
 )
 
@@ -57,9 +58,107 @@ func TestCacheGroups(t *testing.T) {
 		GetTestCacheGroupsByInvalidType(t)
 		GetTestCacheGroupsByType(t)
 		DeleteTestCacheGroupsByInvalidId(t)
+		UpdateCachegroupWithLocks(t)
 	})
 }
 
+func UpdateCachegroupWithLocks(t *testing.T) {
+	var cdnName string
+	servers := make([]tc.ServerV40, 0)
+	opts := client.NewRequestOptions()
+	opts.QueryParameters.Add("name", "cachegroup1")
+	cgResp, _, err := TOSession.GetCacheGroups(opts)
+	if err != nil {
+		t.Fatalf("couldn't get cachegroup: %v", err)
+	}
+	if len(cgResp.Response) != 1 {
+		t.Fatalf("expected only one cachegroup in response, but got %d, quitting", len(cgResp.Response))
+	}
+	opts.QueryParameters.Del("name")
+	opts.QueryParameters.Add("cachegroupName", "cachegroup1")
+	serversResp, _, err := TOSession.GetServers(opts)
+	if err != nil {
+		t.Fatalf("couldn't get servers for cachegroup: %v", err)
+	}
+	servers = serversResp.Response
+	if len(servers) == 0 {
+		t.Fatalf("couldn't get a cachegroup with 1 or more servers assigned, quitting")
+	}
+	server := servers[0]
+	if server.CDNName != nil {
+		cdnName = *server.CDNName
+	} else if server.CDNID != nil {
+		opts = client.RequestOptions{}
+		opts.QueryParameters.Add("id", strconv.Itoa(*server.CDNID))
+		cdnResp, _, err := TOSession.GetCDNs(opts)
+		if err != nil {
+			t.Fatalf("couldn't get CDN: %v", err)
+		}
+		if len(cdnResp.Response) != 1 {
+			t.Fatalf("expected only one CDN in response, but got %d", len(cdnResp.Response))
+		}
+		cdnName = cdnResp.Response[0].Name
+	}
+
+	// Create a new user with operations level privileges
+	user1 := tc.UserV40{
+		User: tc.User{
+			Username:             util.StrPtr("lock_user1"),
+			RegistrationSent:     tc.TimeNoModFromTime(time.Now()),
+			LocalPassword:        util.StrPtr("test_pa$$word"),
+			ConfirmLocalPassword: util.StrPtr("test_pa$$word"),
+			RoleName:             util.StrPtr("operations"),
+		},
+	}
+	user1.Email = util.StrPtr("lockuseremail@domain.com")
+	user1.TenantID = util.IntPtr(1)
+	user1.FullName = util.StrPtr("firstName LastName")
+	_, _, err = TOSession.CreateUser(user1, client.RequestOptions{})
+	if err != nil {
+		t.Fatalf("could not create test user with username: %s", *user1.Username)
+	}
+	defer ForceDeleteTestUsersByUsernames(t, []string{"lock_user1"})
+
+	// Establish a session with the newly created non admin level user
+	userSession, _, err := client.LoginWithAgent(Config.TrafficOps.URL, *user1.Username, *user1.LocalPassword, true, "to-api-v4-client-tests", false, toReqTimeout)
+	if err != nil {
+		t.Fatalf("could not login with user lock_user1: %v", err)
+	}
+
+	// Create a lock for this user
+	_, _, err = userSession.CreateCDNLock(tc.CDNLock{
+		CDN:     cdnName,
+		Message: util.StrPtr("test lock"),
+		Soft:    util.BoolPtr(false),
+	}, client.RequestOptions{})
+	if err != nil {
+		t.Fatalf("couldn't create cdn lock: %v", err)
+	}
+	cg := cgResp.Response[0]
+
+	// Try to update a cachegroup on a CDN that another user has a hard lock on -> this should fail
+	cg.ShortName = util.StrPtr("changedShortName")
+	_, reqInf, err := TOSession.UpdateCacheGroup(*cg.ID, cg, client.RequestOptions{})
+	if err == nil {
+		t.Error("expected an error while updating a cachegroup for a CDN for which a hard lock is held by another user, but got nothing")
+	}
+	if reqInf.StatusCode != http.StatusForbidden {
+		t.Errorf("expected a 403 forbidden status while updating a cachegroup for a CDN for which a hard lock is held by another user, but got %d", reqInf.StatusCode)
+	}
+
+	// Try to update a cachegroup on a CDN that the same user has a hard lock on -> this should succeed
+	_, reqInf, err = userSession.UpdateCacheGroup(*cg.ID, cg, client.RequestOptions{})
+	if err != nil {
+		t.Errorf("expected no error while updating a cachegroup for a CDN for which a hard lock is held by the same user, but got %v", err)
+	}
+
+	// Delete the lock
+	_, _, err = userSession.DeleteCDNLocks(client.RequestOptions{QueryParameters: url.Values{"cdn": []string{cdnName}}})
+	if err != nil {
+		t.Errorf("expected no error while deleting other user's lock using admin endpoint, but got %v", err)
+	}
+}
+
 func UpdateTestCacheGroupsWithHeaders(t *testing.T, h http.Header) {
 	if len(testData.CacheGroups) < 1 {
 		t.Fatal("Need at least one Cache Group to test updating Cache Groups")
diff --git a/traffic_ops/testing/api/v4/cdns_test.go b/traffic_ops/testing/api/v4/cdns_test.go
index d0e5c65..abb6286 100644
--- a/traffic_ops/testing/api/v4/cdns_test.go
+++ b/traffic_ops/testing/api/v4/cdns_test.go
@@ -57,9 +57,96 @@ func TestCDNs(t *testing.T) {
 		SortTestCdnDesc(t)
 		CreateTestCDNsAlreadyExist(t)
 		DeleteTestCDNsInvalidId(t)
+		UpdateDeleteCDNWithLocks(t)
 	})
 }
 
+func UpdateDeleteCDNWithLocks(t *testing.T) {
+	// Create a new user with operations level privileges
+	user1 := tc.UserV40{
+		User: tc.User{
+			Username:             util.StrPtr("lock_user1"),
+			RegistrationSent:     tc.TimeNoModFromTime(time.Now()),
+			LocalPassword:        util.StrPtr("test_pa$$word"),
+			ConfirmLocalPassword: util.StrPtr("test_pa$$word"),
+			RoleName:             util.StrPtr("operations"),
+		},
+	}
+	user1.Email = util.StrPtr("lockuseremail@domain.com")
+	user1.TenantID = util.IntPtr(1)
+	user1.FullName = util.StrPtr("firstName LastName")
+	_, _, err := TOSession.CreateUser(user1, client.RequestOptions{})
+	if err != nil {
+		t.Fatalf("could not create test user with username: %s", *user1.Username)
+	}
+	defer ForceDeleteTestUsersByUsernames(t, []string{"lock_user1"})
+
+	// Establish a session with the newly created non admin level user
+	userSession, _, err := client.LoginWithAgent(Config.TrafficOps.URL, *user1.Username, *user1.LocalPassword, true, "to-api-v4-client-tests", false, toReqTimeout)
+	if err != nil {
+		t.Fatalf("could not login with user lock_user1: %v", err)
+	}
+
+	cdn := createBlankCDN("locksCDN", t)
+
+	// Create a lock for this user
+	_, _, err = userSession.CreateCDNLock(tc.CDNLock{
+		CDN:     cdn.Name,
+		Message: util.StrPtr("test lock"),
+		Soft:    util.BoolPtr(false),
+	}, client.RequestOptions{})
+	if err != nil {
+		t.Fatalf("couldn't create cdn lock: %v", err)
+	}
+
+	opts := client.NewRequestOptions()
+	opts.QueryParameters.Set("name", cdn.Name)
+	cdns, _, err := userSession.GetCDNs(opts)
+	if err != nil {
+		t.Fatalf("couldn't get cdn: %v", err)
+	}
+	if len(cdns.Response) != 1 {
+		t.Fatal("couldn't get exactly one cdn in the response, quitting")
+	}
+	cdnID := cdns.Response[0].ID
+	// Try to update a CDN that another user has a hard lock on -> this should fail
+	cdns.Response[0].DomainName = "changed_domain_name"
+	_, reqInf, err := TOSession.UpdateCDN(cdnID, cdns.Response[0], client.RequestOptions{})
+	if err == nil {
+		t.Error("expected an error while updating a CDN for which a hard lock is held by another user, but got nothing")
+	}
+	if reqInf.StatusCode != http.StatusForbidden {
+		t.Errorf("expected a 403 forbidden status while updating a CDN for which a hard lock is held by another user, but got %d", reqInf.StatusCode)
+	}
+
+	// Try to update a CDN that the same user has a hard lock on -> this should succeed
+	_, reqInf, err = userSession.UpdateCDN(cdnID, cdns.Response[0], client.RequestOptions{})
+	if err != nil {
+		t.Errorf("expected no error while updating a CDN for which a hard lock is held by the same user, but got %v", err)
+	}
+
+	// Try to delete a CDN that another user has a hard lock on -> this should fail
+	_, reqInf, err = TOSession.DeleteCDN(cdnID, client.RequestOptions{})
+	if err == nil {
+		t.Error("expected an error while deleting a CDN for which a hard lock is held by another user, but got nothing")
+	}
+	if reqInf.StatusCode != http.StatusForbidden {
+		t.Errorf("expected a 403 forbidden status while deleting a CDN for which a hard lock is held by another user, but got %d", reqInf.StatusCode)
+	}
+
+	// Try to delete a CDN that the same user has a hard lock on -> this should succeed
+	// This should also delete the lock associated with this CDN
+	_, reqInf, err = userSession.DeleteCDN(cdnID, client.RequestOptions{})
+	if err != nil {
+		t.Errorf("expected no error while deleting a CDN for which a hard lock is held by the same user, but got %v", err)
+	}
+
+	locks, _, _ := userSession.GetCDNLocks(client.RequestOptions{})
+	if len(locks.Response) != 0 {
+		t.Errorf("expected deletion of CDN to delete it's associated lock, and no locks in the response, but got %d locks instead", len(locks.Response))
+	}
+}
+
 func TestCDNsDNSSEC(t *testing.T) {
 	WithObjs(t, []TCObj{CDNs, Types, Tenants, Users, Parameters, Profiles, Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers, Topologies, ServerCapabilities, DeliveryServices}, func() {
 		if includeSystemTests {
diff --git a/traffic_ops/testing/api/v4/deliveryservices_test.go b/traffic_ops/testing/api/v4/deliveryservices_test.go
index eda6ed2..86c64d9 100644
--- a/traffic_ops/testing/api/v4/deliveryservices_test.go
+++ b/traffic_ops/testing/api/v4/deliveryservices_test.go
@@ -90,10 +90,125 @@ func TestDeliveryServices(t *testing.T) {
 		t.Run("GET request using the 'type' query string parameter", GetDeliveryServiceByValidType)
 		t.Run("GET request using the 'xmlId' query string parameter", GetDeliveryServiceByValidXmlId)
 		t.Run("Descending order sorted response to GET request", SortTestDeliveryServicesDesc)
+		t.Run("Create/ Update/ Delete delivery services with locks", CUDDeliveryServiceWithLocks)
 		t.Run("TLS Versions property", addTLSVersionsToDeliveryService)
 	})
 }
 
+func CUDDeliveryServiceWithLocks(t *testing.T) {
+	// Create a new user with operations level privileges
+	user1 := tc.UserV40{
+		User: tc.User{
+			Username:             util.StrPtr("lock_user1"),
+			RegistrationSent:     tc.TimeNoModFromTime(time.Now()),
+			LocalPassword:        util.StrPtr("test_pa$$word"),
+			ConfirmLocalPassword: util.StrPtr("test_pa$$word"),
+			RoleName:             util.StrPtr("operations"),
+		},
+	}
+	user1.Email = util.StrPtr("lockuseremail@domain.com")
+	user1.TenantID = util.IntPtr(1)
+	//util.IntPtr(resp.Response[0].ID)
+	user1.FullName = util.StrPtr("firstName LastName")
+	_, _, err := TOSession.CreateUser(user1, client.RequestOptions{})
+	if err != nil {
+		t.Fatalf("could not create test user with username: %s", *user1.Username)
+	}
+	defer ForceDeleteTestUsersByUsernames(t, []string{"lock_user1"})
+
+	// Establish a session with the newly created non admin level user
+	userSession, _, err := client.LoginWithAgent(Config.TrafficOps.URL, *user1.Username, *user1.LocalPassword, true, "to-api-v4-client-tests", false, toReqTimeout)
+	if err != nil {
+		t.Fatalf("could not login with user lock_user1: %v", err)
+	}
+	if len(testData.DeliveryServices) == 0 {
+		t.Fatalf("no deliveryservices to run the test on, quitting")
+	}
+
+	cdn := createBlankCDN("sslkeytransfer", t)
+	opts := client.NewRequestOptions()
+	opts.QueryParameters.Set("name", "HTTP")
+	types, _, err := TOSession.GetTypes(opts)
+	if err != nil {
+		t.Fatalf("unable to get Types: %v - alerts: %+v", err, types.Alerts)
+	}
+	if len(types.Response) < 1 {
+		t.Fatal("expected at least one type")
+	}
+	customDS := getCustomDS(cdn.ID, types.Response[0].ID, "cdn_locks_test_ds_name", "routingName", "https://test_cdn_locks.com", "cdn_locks_test_ds_xml_id")
+
+	// Create a lock for this user
+	_, _, err = userSession.CreateCDNLock(tc.CDNLock{
+		CDN:     cdn.Name,
+		Message: util.StrPtr("test lock"),
+		Soft:    util.BoolPtr(false),
+	}, client.RequestOptions{})
+	if err != nil {
+		t.Fatalf("couldn't create cdn lock: %v", err)
+	}
+	// Try to create a new ds on a CDN that another user has a hard lock on -> this should fail
+	_, reqInf, err := TOSession.CreateDeliveryService(customDS, client.RequestOptions{})
+	if err == nil {
+		t.Error("expected an error while creating a new ds for a CDN for which a hard lock is held by another user, but got nothing")
+	}
+	if reqInf.StatusCode != http.StatusForbidden {
+		t.Errorf("expected a 403 forbidden status while creating a new ds for a CDN for which a hard lock is held by another user, but got %d", reqInf.StatusCode)
+	}
+
+	// Try to create a new ds on a CDN that the same user has a hard lock on -> this should succeed
+	dsResp, reqInf, err := userSession.CreateDeliveryService(customDS, client.RequestOptions{})
+	if err != nil {
+		t.Errorf("expected no error while creating a new ds for a CDN for which a hard lock is held by the same user, but got %v", err)
+	}
+	if len(dsResp.Response) != 1 {
+		t.Fatalf("one response expected, but got %d", len(dsResp.Response))
+	}
+	opts = client.NewRequestOptions()
+	opts.QueryParameters.Set("xmlId", *customDS.XMLID)
+	deliveryServices, _, err := userSession.GetDeliveryServices(opts)
+	if err != nil {
+		t.Fatalf("couldn't get ds: %v", err)
+	}
+	if len(deliveryServices.Response) != 1 {
+		t.Fatal("couldn't get exactly one ds in the response, quitting")
+	}
+	dsID := dsResp.Response[0].ID
+	// Try to update a ds on a CDN that another user has a hard lock on -> this should fail
+	customDS.LongDesc = util.StrPtr("changed_long_desc")
+	_, reqInf, err = TOSession.UpdateDeliveryService(*dsID, customDS, client.RequestOptions{})
+	if err == nil {
+		t.Error("expected an error while updating a ds for a CDN for which a hard lock is held by another user, but got nothing")
+	}
+	if reqInf.StatusCode != http.StatusForbidden {
+		t.Errorf("expected a 403 forbidden status while updating a ds for a CDN for which a hard lock is held by another user, but got %d", reqInf.StatusCode)
+	}
+	// Try to update a ds on a CDN that the same user has a hard lock on -> this should succeed
+	_, reqInf, err = userSession.UpdateDeliveryService(*dsID, customDS, client.RequestOptions{})
+	if err != nil {
+		t.Errorf("expected no error while updating a ds for a CDN for which a hard lock is held by the same user, but got %v", err)
+	}
+	// Try to delete a ds on a CDN that another user has a hard lock on -> this should fail
+	_, reqInf, err = TOSession.DeleteDeliveryService(*dsID, client.RequestOptions{})
+	if err == nil {
+		t.Error("expected an error while deleting a ds for a CDN for which a hard lock is held by another user, but got nothing")
+	}
+	if reqInf.StatusCode != http.StatusForbidden {
+		t.Errorf("expected a 403 forbidden status while deleting a ds for a CDN for which a hard lock is held by another user, but got %d", reqInf.StatusCode)
+	}
+
+	// Try to delete a ds on a CDN that the same user has a hard lock on -> this should succeed
+	_, reqInf, err = userSession.DeleteDeliveryService(*dsID, client.RequestOptions{})
+	if err != nil {
+		t.Errorf("expected no error while deleting a ds for a CDN for which a hard lock is held by the same user, but got %v", err)
+	}
+
+	// Delete the lock
+	_, _, err = userSession.DeleteCDNLocks(client.RequestOptions{QueryParameters: url.Values{"cdn": []string{cdn.Name}}})
+	if err != nil {
+		t.Errorf("expected no error while deleting other user's lock using admin endpoint, but got %v", err)
+	}
+}
+
 func CreateTestDeliveryServiceWithLongDescFields(t *testing.T) {
 	if len(testData.DeliveryServices) < 1 {
 		t.Fatal("Need at least one Delivery Service to test updating a Delivery Service")
diff --git a/traffic_ops/testing/api/v4/profile_parameters_test.go b/traffic_ops/testing/api/v4/profile_parameters_test.go
index f4c9542..fa11548 100644
--- a/traffic_ops/testing/api/v4/profile_parameters_test.go
+++ b/traffic_ops/testing/api/v4/profile_parameters_test.go
@@ -24,6 +24,7 @@ import (
 
 	"github.com/apache/trafficcontrol/lib/go-rfc"
 	"github.com/apache/trafficcontrol/lib/go-tc"
+	"github.com/apache/trafficcontrol/lib/go-util"
 	client "github.com/apache/trafficcontrol/traffic_ops/v4-client"
 )
 
@@ -38,9 +39,106 @@ func TestProfileParameters(t *testing.T) {
 		CreateTestProfileParametersMissingProfileId(t)
 		CreateTestProfileParametersMissingParameterId(t)
 		CreateTestProfileParametersEmptyBody(t)
+		CreateDeleteProfileParameterWithLocks(t)
 	})
 }
 
+func CreateDeleteProfileParameterWithLocks(t *testing.T) {
+	// Create a new user with operations level privileges
+	user1 := tc.UserV40{
+		User: tc.User{
+			Username:             util.StrPtr("lock_user1"),
+			RegistrationSent:     tc.TimeNoModFromTime(time.Now()),
+			LocalPassword:        util.StrPtr("test_pa$$word"),
+			ConfirmLocalPassword: util.StrPtr("test_pa$$word"),
+			RoleName:             util.StrPtr("operations"),
+		},
+	}
+	user1.Email = util.StrPtr("lockuseremail@domain.com")
+	user1.TenantID = util.IntPtr(1)
+	user1.FullName = util.StrPtr("firstName LastName")
+	_, _, err := TOSession.CreateUser(user1, client.RequestOptions{})
+	if err != nil {
+		t.Fatalf("could not create test user with username: %s", *user1.Username)
+	}
+	defer ForceDeleteTestUsersByUsernames(t, []string{"lock_user1"})
+
+	// Establish a session with the newly created non admin level user
+	userSession, _, err := client.LoginWithAgent(Config.TrafficOps.URL, *user1.Username, *user1.LocalPassword, true, "to-api-v4-client-tests", false, toReqTimeout)
+	if err != nil {
+		t.Fatalf("could not login with user lock_user1: %v", err)
+	}
+	if len(testData.Profiles) == 0 {
+		t.Fatal("no profiles to run the tests on, quitting")
+	}
+	opts := client.NewRequestOptions()
+	opts.QueryParameters.Set("name", testData.Profiles[0].Name)
+	profilesResp, _, err := TOSession.GetProfiles(opts)
+	if err != nil {
+		t.Fatalf("couldn't get profiles: %v", err)
+	}
+	if len(profilesResp.Response) != 1 {
+		t.Fatalf("expected just one profile in the response, but got %d", len(profilesResp.Response))
+	}
+	profileID := profilesResp.Response[0].ID
+
+	cdnName := testData.Profiles[0].CDNName
+	// Create a lock for this user
+	_, _, err = userSession.CreateCDNLock(tc.CDNLock{
+		CDN:     cdnName,
+		Message: util.StrPtr("test lock"),
+		Soft:    util.BoolPtr(false),
+	}, client.RequestOptions{})
+	if err != nil {
+		t.Fatalf("couldn't create cdn lock: %v", err)
+	}
+
+	_, _, err = TOSession.CreateParameter(tc.Parameter{
+		ConfigFile: "global",
+		Name:       "cdnLocksParam",
+		Value:      "https://crconfig.tm.url.test.invalid",
+	}, client.RequestOptions{})
+
+	if err != nil {
+		t.Fatalf("couldn't create a new param, quitting: %v", err)
+	}
+	opts = client.NewRequestOptions()
+	opts.QueryParameters.Set("name", "cdnLocksParam")
+	paramsResp, _, err := TOSession.GetParameters(opts)
+
+	req := tc.ProfileParameterCreationRequest{ProfileID: profileID, ParameterID: paramsResp.Response[0].ID}
+	// Try to create a new profile param on a CDN that another user has a hard lock on -> this should fail
+	_, reqInf, err := TOSession.CreateProfileParameter(req, client.RequestOptions{})
+	if err == nil {
+		t.Error("expected an error while creating a new profile param for a CDN for which a hard lock is held by another user, but got nothing")
+	}
+	if reqInf.StatusCode != http.StatusForbidden {
+		t.Errorf("expected a 403 forbidden status while creating a new profile param for a CDN for which a hard lock is held by another user, but got %d", reqInf.StatusCode)
+	}
+
+	// Try to create a new ds on a CDN that the same user has a hard lock on -> this should succeed
+	_, reqInf, err = userSession.CreateProfileParameter(req, client.RequestOptions{})
+	if err != nil {
+		t.Errorf("expected no error while creating a new profile param for a CDN for which a hard lock is held by the same user, but got %v", err)
+	}
+
+	// Try to delete a profile param on a CDN that another user has a hard lock on -> this should fail
+	_, reqInf, err = TOSession.DeleteProfileParameter(profileID, paramsResp.Response[0].ID, client.RequestOptions{})
+	if err == nil {
+		t.Error("expected an error while deleting a profile param for a CDN for which a hard lock is held by another user, but got nothing")
+	}
+	if reqInf.StatusCode != http.StatusForbidden {
+		t.Errorf("expected a 403 forbidden status while deleting a profile param for a CDN for which a hard lock is held by another user, but got %d", reqInf.StatusCode)
+	}
+
+	// Try to delete a profile param on a CDN that the same user has a hard lock on -> this should succeed
+	_, reqInf, err = userSession.DeleteProfileParameter(profileID, paramsResp.Response[0].ID, client.RequestOptions{})
+	if err != nil {
+		t.Errorf("expected no error while deleting a profile param for a CDN for which a hard lock is held by the same user, but got %v", err)
+	}
+
+}
+
 func GetTestProfileParametersIMS(t *testing.T) {
 	opts := client.NewRequestOptions()
 
diff --git a/traffic_ops/testing/api/v4/profiles_test.go b/traffic_ops/testing/api/v4/profiles_test.go
index b0dd3f7..71da5e5 100644
--- a/traffic_ops/testing/api/v4/profiles_test.go
+++ b/traffic_ops/testing/api/v4/profiles_test.go
@@ -50,9 +50,134 @@ func TestProfiles(t *testing.T) {
 		header.Set(rfc.IfMatch, etag)
 		UpdateTestProfilesWithHeaders(t, header)
 		GetTestPaginationSupportProfiles(t)
+		CUDProfileWithLocks(t)
 	})
 }
 
+func CUDProfileWithLocks(t *testing.T) {
+	resp, _, err := TOSession.GetTenants(client.RequestOptions{})
+	if err != nil {
+		t.Fatalf("could not GET tenants: %v", err)
+	}
+	if len(resp.Response) == 0 {
+		t.Fatalf("didn't get any tenant in response")
+	}
+
+	// Create a new user with operations level privileges
+	user1 := tc.UserV40{
+		User: tc.User{
+			Username:             util.StrPtr("lock_user1"),
+			RegistrationSent:     tc.TimeNoModFromTime(time.Now()),
+			LocalPassword:        util.StrPtr("test_pa$$word"),
+			ConfirmLocalPassword: util.StrPtr("test_pa$$word"),
+			RoleName:             util.StrPtr("operations"),
+		},
+	}
+	user1.Email = util.StrPtr("lockuseremail@domain.com")
+	user1.TenantID = util.IntPtr(resp.Response[0].ID)
+	user1.FullName = util.StrPtr("firstName LastName")
+	_, _, err = TOSession.CreateUser(user1, client.RequestOptions{})
+	if err != nil {
+		t.Fatalf("could not create test user with username: %s", *user1.Username)
+	}
+	defer ForceDeleteTestUsersByUsernames(t, []string{"lock_user1"})
+
+	// Establish a session with the newly created non admin level user
+	userSession, _, err := client.LoginWithAgent(Config.TrafficOps.URL, *user1.Username, *user1.LocalPassword, true, "to-api-v4-client-tests", false, toReqTimeout)
+	if err != nil {
+		t.Fatalf("could not login with user lock_user1: %v", err)
+	}
+	if len(testData.Profiles) == 0 {
+		t.Fatalf("no profiles to run the test on, quitting")
+	}
+
+	cdnsResp, _, err := TOSession.GetCDNs(client.RequestOptions{})
+	if err != nil {
+		t.Fatalf("couldn't get CDNs: %v", err)
+	}
+	if len(cdnsResp.Response) == 0 {
+		t.Fatal("got no cdns in the response, quitting")
+	}
+	// Create a lock for this user
+	_, _, err = userSession.CreateCDNLock(tc.CDNLock{
+		CDN:     cdnsResp.Response[0].Name,
+		Message: util.StrPtr("test lock"),
+		Soft:    util.BoolPtr(false),
+	}, client.RequestOptions{})
+	if err != nil {
+		t.Fatalf("couldn't create cdn lock: %v", err)
+	}
+	if len(testData.Profiles) == 0 {
+		t.Fatal("no profiles to run tests on, quitting")
+	}
+	pr := testData.Profiles[0]
+	pr.Name = "cdn_locks_test_profile"
+	pr.CDNID = cdnsResp.Response[0].ID
+	pr.CDNName = cdnsResp.Response[0].Name
+
+	// Try to create a new profile on a CDN that another user has a hard lock on -> this should fail
+	_, reqInf, err := TOSession.CreateProfile(pr, client.RequestOptions{})
+	if err == nil {
+		t.Error("expected an error while creating a new profile for a CDN for which a hard lock is held by another user, but got nothing")
+	}
+	if reqInf.StatusCode != http.StatusForbidden {
+		t.Errorf("expected a 403 forbidden status while creating a new profile for a CDN for which a hard lock is held by another user, but got %d", reqInf.StatusCode)
+	}
+
+	// Try to create a new profile on a CDN that the same user has a hard lock on -> this should succeed
+	_, reqInf, err = userSession.CreateProfile(pr, client.RequestOptions{})
+	if err != nil {
+		t.Errorf("expected no error while creating a new profile for a CDN for which a hard lock is held by the same user, but got %v", err)
+	}
+
+	opts := client.NewRequestOptions()
+	opts.QueryParameters.Set("name", pr.Name)
+	profile, _, err := userSession.GetProfiles(opts)
+	if err != nil {
+		t.Fatalf("couldn't get profile: %v", err)
+	}
+	if len(profile.Response) != 1 {
+		t.Fatal("couldn't get exactly one profile in the response, quitting")
+	}
+	profileID := profile.Response[0].ID
+	// Try to update a profile on a CDN that another user has a hard lock on -> this should fail
+	pr.Description = "changed description"
+	_, reqInf, err = TOSession.UpdateProfile(profileID, pr, client.RequestOptions{})
+	if err == nil {
+		t.Error("expected an error while updating a profile for a CDN for which a hard lock is held by another user, but got nothing")
+	}
+	if reqInf.StatusCode != http.StatusForbidden {
+		t.Errorf("expected a 403 forbidden status while updating a profile for a CDN for which a hard lock is held by another user, but got %d", reqInf.StatusCode)
+	}
+
+	// Try to update a profile on a CDN that the same user has a hard lock on -> this should succeed
+	_, reqInf, err = userSession.UpdateProfile(profileID, pr, client.RequestOptions{})
+	if err != nil {
+		t.Errorf("expected no error while updating a profile for a CDN for which a hard lock is held by the same user, but got %v", err)
+	}
+
+	// Try to delete a profile on a CDN that another user has a hard lock on -> this should fail
+	_, reqInf, err = TOSession.DeleteProfile(profileID, client.RequestOptions{})
+	if err == nil {
+		t.Error("expected an error while deleting a profile for a CDN for which a hard lock is held by another user, but got nothing")
+	}
+	if reqInf.StatusCode != http.StatusForbidden {
+		t.Errorf("expected a 403 forbidden status while deleting a profile for a CDN for which a hard lock is held by another user, but got %d", reqInf.StatusCode)
+	}
+
+	// Try to delete a profile on a CDN that the same user has a hard lock on -> this should succeed
+	_, reqInf, err = userSession.DeleteProfile(profileID, client.RequestOptions{})
+	if err != nil {
+		t.Errorf("expected no error while deleting a profile for a CDN for which a hard lock is held by the same user, but got %v", err)
+	}
+
+	// Delete the lock
+	_, _, err = userSession.DeleteCDNLocks(client.RequestOptions{QueryParameters: url.Values{"cdn": []string{cdnsResp.Response[0].Name}}})
+	if err != nil {
+		t.Errorf("expected no error while deleting other user's lock using admin endpoint, but got %v", err)
+	}
+}
+
 func UpdateTestProfilesWithHeaders(t *testing.T, header http.Header) {
 	if len(testData.Profiles) < 1 {
 		t.Fatal("Need at least one Profile to test updating a Profile with HTTP headers")
diff --git a/traffic_ops/testing/api/v4/servers_test.go b/traffic_ops/testing/api/v4/servers_test.go
index ddc1c12..5e5f33c 100644
--- a/traffic_ops/testing/api/v4/servers_test.go
+++ b/traffic_ops/testing/api/v4/servers_test.go
@@ -55,9 +55,140 @@ func TestServers(t *testing.T) {
 		UpdateTestServerStatus(t)
 		LastServerInTopologyCacheGroup(t)
 		GetServersForNonExistentDeliveryService(t)
+		CUDServerWithLocks(t)
 	})
 }
 
+func CUDServerWithLocks(t *testing.T) {
+	resp, _, err := TOSession.GetTenants(client.RequestOptions{})
+	if err != nil {
+		t.Fatalf("could not GET tenants: %v", err)
+	}
+	if len(resp.Response) == 0 {
+		t.Fatalf("didn't get any tenant in response")
+	}
+
+	// Create a new user with operations level privileges
+	user1 := tc.UserV40{
+		User: tc.User{
+			Username:             util.StrPtr("lock_user1"),
+			RegistrationSent:     tc.TimeNoModFromTime(time.Now()),
+			LocalPassword:        util.StrPtr("test_pa$$word"),
+			ConfirmLocalPassword: util.StrPtr("test_pa$$word"),
+			RoleName:             util.StrPtr("operations"),
+		},
+	}
+	user1.Email = util.StrPtr("lockuseremail@domain.com")
+	user1.TenantID = util.IntPtr(resp.Response[0].ID)
+	user1.FullName = util.StrPtr("firstName LastName")
+	_, _, err = TOSession.CreateUser(user1, client.RequestOptions{})
+	if err != nil {
+		t.Fatalf("could not create test user with username: %s", *user1.Username)
+	}
+	defer ForceDeleteTestUsersByUsernames(t, []string{"lock_user1"})
+
+	// Establish a session with the newly created non admin level user
+	userSession, _, err := client.LoginWithAgent(Config.TrafficOps.URL, *user1.Username, *user1.LocalPassword, true, "to-api-v4-client-tests", false, toReqTimeout)
+	if err != nil {
+		t.Fatalf("could not login with user lock_user1: %v", err)
+	}
+	if len(testData.Servers) == 0 {
+		t.Fatalf("no servers to run the test on, quitting")
+	}
+
+	server := testData.Servers[0]
+	server.HostName = util.StrPtr("cdn_locks_test_server")
+	server.Interfaces = []tc.ServerInterfaceInfoV40{
+		{
+			ServerInterfaceInfo: tc.ServerInterfaceInfo{
+				IPAddresses: []tc.ServerIPAddress{
+					{
+						Address:        "123.32.43.21",
+						Gateway:        util.StrPtr("100.100.100.100"),
+						ServiceAddress: true,
+					},
+				},
+				MaxBandwidth: util.Uint64Ptr(2500),
+				Monitor:      true,
+				MTU:          util.Uint64Ptr(1500),
+				Name:         "cdn_locks_interfaceName",
+			},
+			RouterHostName: "router1",
+			RouterPortName: "9090",
+		},
+	}
+	// Create a lock for this user
+	_, _, err = userSession.CreateCDNLock(tc.CDNLock{
+		CDN:     *server.CDNName,
+		Message: util.StrPtr("test lock"),
+		Soft:    util.BoolPtr(false),
+	}, client.RequestOptions{})
+	if err != nil {
+		t.Fatalf("couldn't create cdn lock: %v", err)
+	}
+	// Try to create a new server on a CDN that another user has a hard lock on -> this should fail
+	_, reqInf, err := TOSession.CreateServer(server, client.RequestOptions{})
+	if err == nil {
+		t.Error("expected an error while creating a new server for a CDN for which a hard lock is held by another user, but got nothing")
+	}
+	if reqInf.StatusCode != http.StatusForbidden {
+		t.Errorf("expected a 403 forbidden status while creating a new server for a CDN for which a hard lock is held by another user, but got %d", reqInf.StatusCode)
+	}
+
+	// Try to create a new profile on a CDN that the same user has a hard lock on -> this should succeed
+	_, reqInf, err = userSession.CreateServer(server, client.RequestOptions{})
+	if err != nil {
+		t.Errorf("expected no error while creating a new server for a CDN for which a hard lock is held by the same user, but got %v", err)
+	}
+
+	opts := client.NewRequestOptions()
+	opts.QueryParameters.Set("hostName", *server.HostName)
+	servers, _, err := userSession.GetServers(opts)
+	if err != nil {
+		t.Fatalf("couldn't get server: %v", err)
+	}
+	if len(servers.Response) != 1 {
+		t.Fatal("couldn't get exactly one server in the response, quitting")
+	}
+	serverID := servers.Response[0].ID
+	// Try to update a server on a CDN that another user has a hard lock on -> this should fail
+	servers.Response[0].DomainName = util.StrPtr("changed_domain_name")
+	_, reqInf, err = TOSession.UpdateServer(*serverID, servers.Response[0], client.RequestOptions{})
+	if err == nil {
+		t.Error("expected an error while updating a server for a CDN for which a hard lock is held by another user, but got nothing")
+	}
+	if reqInf.StatusCode != http.StatusForbidden {
+		t.Errorf("expected a 403 forbidden status while updating a server for a CDN for which a hard lock is held by another user, but got %d", reqInf.StatusCode)
+	}
+
+	// Try to update a server on a CDN that the same user has a hard lock on -> this should succeed
+	_, reqInf, err = userSession.UpdateServer(*serverID, servers.Response[0], client.RequestOptions{})
+	if err != nil {
+		t.Errorf("expected no error while updating a server for a CDN for which a hard lock is held by the same user, but got %v", err)
+	}
+
+	// Try to delete a server on a CDN that another user has a hard lock on -> this should fail
+	_, reqInf, err = TOSession.DeleteServer(*serverID, client.RequestOptions{})
+	if err == nil {
+		t.Error("expected an error while deleting a server for a CDN for which a hard lock is held by another user, but got nothing")
+	}
+	if reqInf.StatusCode != http.StatusForbidden {
+		t.Errorf("expected a 403 forbidden status while deleting a server for a CDN for which a hard lock is held by another user, but got %d", reqInf.StatusCode)
+	}
+
+	// Try to delete a server on a CDN that the same user has a hard lock on -> this should succeed
+	_, reqInf, err = userSession.DeleteServer(*serverID, client.RequestOptions{})
+	if err != nil {
+		t.Errorf("expected no error while deleting a server for a CDN for which a hard lock is held by the same user, but got %v", err)
+	}
+
+	// Delete the lock
+	_, _, err = userSession.DeleteCDNLocks(client.RequestOptions{QueryParameters: url.Values{"cdn": []string{*server.CDNName}}})
+	if err != nil {
+		t.Errorf("expected no error while deleting other user's lock using admin endpoint, but got %v", err)
+	}
+}
+
 func LastServerInTopologyCacheGroup(t *testing.T) {
 	const cacheGroupName = "topology-mid-cg-01"
 	const moveToCacheGroup = "topology-mid-cg-02"
diff --git a/traffic_ops/traffic_ops_golang/cachegroup/cachegroups.go b/traffic_ops/traffic_ops_golang/cachegroup/cachegroups.go
index 7b006ed..04272729 100644
--- a/traffic_ops/traffic_ops_golang/cachegroup/cachegroups.go
+++ b/traffic_ops/traffic_ops_golang/cachegroup/cachegroups.go
@@ -611,6 +611,11 @@ func (cg *TOCacheGroup) Update(h http.Header) (error, error, int) {
 		return userErr, sysErr, errCode
 	}
 
+	// CheckIfCurrentUserCanModifyCachegroup
+	userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCachegroup(cg.ReqInfo.Tx.Tx, *cg.ID, cg.ReqInfo.User.UserName)
+	if userErr != nil || sysErr != nil {
+		return userErr, sysErr, errCode
+	}
 	coordinateID, userErr, sysErr, errCode := cg.handleCoordinateUpdate()
 	if userErr != nil || sysErr != nil {
 		return userErr, sysErr, errCode
@@ -716,6 +721,11 @@ func (cg *TOCacheGroup) Delete() (error, error, int) {
 		return nil, errors.New("cachegroup delete: getting coord: " + err.Error()), http.StatusInternalServerError
 	}
 
+	// CheckIfCurrentUserCanModifyCachegroup
+	userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCachegroup(cg.ReqInfo.Tx.Tx, *cg.ID, cg.ReqInfo.User.UserName)
+	if userErr != nil || sysErr != nil {
+		return userErr, sysErr, errCode
+	}
 	if err = cg.deleteCoordinate(*coordinateID); err != nil {
 		return nil, errors.New("cachegroup delete: deleting coord: " + err.Error()), http.StatusInternalServerError
 	}
diff --git a/traffic_ops/traffic_ops_golang/cachegroup/dspost.go b/traffic_ops/traffic_ops_golang/cachegroup/dspost.go
index e1fff4b..8010f3c 100644
--- a/traffic_ops/traffic_ops_golang/cachegroup/dspost.go
+++ b/traffic_ops/traffic_ops_golang/cachegroup/dspost.go
@@ -84,6 +84,16 @@ func DSPostHandlerV40(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	cdnNames, err := dbhelpers.GetCDNNamesFromDSIds(inf.Tx.Tx, req.DeliveryServices)
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("getting CDN names from DS IDs "+err.Error()))
+		return
+	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDNs(inf.Tx.Tx, cdnNames, inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
 	resp, vals, userErr, sysErr, errCode := postDSes(inf.Tx.Tx, inf.User, inf.IntParams["id"], req.DeliveryServices)
 	if userErr != nil || sysErr != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
diff --git a/traffic_ops/traffic_ops_golang/cachegroupparameter/parameters.go b/traffic_ops/traffic_ops_golang/cachegroupparameter/parameters.go
index 4aab0ba..3f07f22 100644
--- a/traffic_ops/traffic_ops_golang/cachegroupparameter/parameters.go
+++ b/traffic_ops/traffic_ops_golang/cachegroupparameter/parameters.go
@@ -207,6 +207,11 @@ func (cgparam *TOCacheGroupParameter) Delete() (error, error, int) {
 		return fmt.Errorf("parameter %v does not exist", *cgparam.ID), nil, http.StatusNotFound
 	}
 
+	// CheckIfCurrentUserCanModifyCachegroup
+	userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCachegroup(cgparam.ReqInfo.Tx.Tx, cgparam.CacheGroupID, cgparam.ReqInfo.User.UserName)
+	if userErr != nil || sysErr != nil {
+		return userErr, sysErr, errCode
+	}
 	return api.GenericDelete(cgparam)
 }
 
@@ -273,7 +278,6 @@ func AddCacheGroupParameters(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	defer inf.Close()
-
 	data, err := ioutil.ReadAll(r.Body)
 	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("reading request body: "+err.Error()), nil)
@@ -309,7 +313,7 @@ func AddCacheGroupParameters(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("parsing cachegroup parameter: "+parseErr.Error()), nil)
 		return
 	}
-
+	cachegroups := []int{}
 	for _, p := range params {
 		ppExists, err := dbhelpers.CachegroupParameterAssociationExists(*p.Parameter, *p.CacheGroup, inf.Tx.Tx)
 		if err != nil {
@@ -320,8 +324,13 @@ func AddCacheGroupParameters(w http.ResponseWriter, r *http.Request) {
 			api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("parameter: "+strconv.Itoa(*p.Parameter)+" already associated with cachegroup: "+strconv.Itoa(*p.CacheGroup)+"."), nil)
 			return
 		}
+		cachegroups = append(cachegroups, *p.CacheGroup)
+	}
+	userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCachegroups(inf.Tx.Tx, cachegroups, inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
 	}
-
 	values := []string{}
 	for _, param := range params {
 		values = append(values, "("+strconv.Itoa(*param.CacheGroup)+", "+strconv.Itoa(*param.Parameter)+")")
diff --git a/traffic_ops/traffic_ops_golang/cdn/cdns.go b/traffic_ops/traffic_ops_golang/cdn/cdns.go
index f9dabb0..79ccdd0 100644
--- a/traffic_ops/traffic_ops_golang/cdn/cdns.go
+++ b/traffic_ops/traffic_ops_golang/cdn/cdns.go
@@ -142,11 +142,25 @@ func (cdn *TOCDN) Read(h http.Header, useIMS bool) ([]interface{}, error, error,
 }
 
 func (cdn *TOCDN) Update(h http.Header) (error, error, int) {
+	if cdn.ID != nil {
+		userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(cdn.APIInfo().Tx.Tx, int64(*cdn.ID), cdn.APIInfo().User.UserName)
+		if userErr != nil || sysErr != nil {
+			return userErr, sysErr, errCode
+		}
+	}
 	*cdn.DomainName = strings.ToLower(*cdn.DomainName)
 	return api.GenericUpdate(h, cdn)
 }
 
-func (cdn *TOCDN) Delete() (error, error, int) { return api.GenericDelete(cdn) }
+func (cdn *TOCDN) Delete() (error, error, int) {
+	if cdn.ID != nil {
+		userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(cdn.APIInfo().Tx.Tx, int64(*cdn.ID), cdn.APIInfo().User.UserName)
+		if userErr != nil || sysErr != nil {
+			return userErr, sysErr, errCode
+		}
+	}
+	return api.GenericDelete(cdn)
+}
 
 func selectQuery() string {
 	query := `SELECT
diff --git a/traffic_ops/traffic_ops_golang/cdn/dnssec.go b/traffic_ops/traffic_ops_golang/cdn/dnssec.go
index 82e94e9..2929817 100644
--- a/traffic_ops/traffic_ops_golang/cdn/dnssec.go
+++ b/traffic_ops/traffic_ops_golang/cdn/dnssec.go
@@ -89,7 +89,11 @@ func CreateDNSSECKeys(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, errors.New("cdn '"+cdnName+"' not found"), nil)
 		return
 	}
-
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, cdnName, inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
 	if err := generateStoreDNSSECKeys(inf.Tx.Tx, cdnName, cdnDomain, uint64(*req.TTL), uint64(*req.KSKExpirationDays), uint64(*req.ZSKExpirationDays), int64(*req.EffectiveDateUnix), inf.Vault, r.Context()); err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("generating and storing DNSSEC CDN keys: "+err.Error()))
 		return
@@ -448,6 +452,11 @@ func deleteDNSSECKeys(w http.ResponseWriter, r *http.Request, deprecated bool) {
 		writeError(w, r, inf.Tx.Tx, http.StatusNotFound, nil, nil, deprecated)
 		return
 	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, key, inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
 	if err := inf.Vault.DeleteDNSSECKeys(key, inf.Tx.Tx, r.Context()); err != nil {
 		writeError(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deleting CDN DNSSEC keys: "+err.Error()), deprecated)
 		return
@@ -470,7 +479,7 @@ func getCDNIDFromName(tx *sql.Tx, name tc.CDNName) (int, bool, error) {
 		if err == sql.ErrNoRows {
 			return id, false, nil
 		}
-		return id, false, errors.New("querying CDN ID: " + err.Error())
+		return id, false, errors.New("querying CDN ID from name: " + err.Error())
 	}
 	return id, true, nil
 }
diff --git a/traffic_ops/traffic_ops_golang/cdn/genksk.go b/traffic_ops/traffic_ops_golang/cdn/genksk.go
index 75abeae..81b21e7 100644
--- a/traffic_ops/traffic_ops_golang/cdn/genksk.go
+++ b/traffic_ops/traffic_ops_golang/cdn/genksk.go
@@ -45,7 +45,10 @@ func GenerateKSK(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	defer inf.Close()
-
+	if inf.User == nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("no user in API info"))
+		return
+	}
 	if !inf.Config.TrafficVaultEnabled {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("generating CDN KSK: Traffic Vault is not configured"))
 		return
@@ -76,7 +79,11 @@ func GenerateKSK(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, nil, nil)
 		return
 	}
-
+	userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdnName), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
 	ttl, multiplier, err := getKSKParams(inf.Tx.Tx, cdnName)
 	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("getting CDN KSK parameters: "+err.Error()))
diff --git a/traffic_ops/traffic_ops_golang/cdn/namedelete.go b/traffic_ops/traffic_ops_golang/cdn/namedelete.go
index c942335..133b8fb 100644
--- a/traffic_ops/traffic_ops_golang/cdn/namedelete.go
+++ b/traffic_ops/traffic_ops_golang/cdn/namedelete.go
@@ -27,6 +27,7 @@ import (
 
 	"github.com/apache/trafficcontrol/lib/go-tc"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
 )
 
 func DeleteName(w http.ResponseWriter, r *http.Request) {
@@ -53,6 +54,11 @@ func DeleteName(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("Failed to delete cdn name = "+string(cdnName)+" has delivery services or servers"), nil)
 		return
 	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdnName), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
 	if err := deleteCDNByName(inf.Tx.Tx, cdnName); err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deleting CDN: "+err.Error()))
 		return
diff --git a/traffic_ops/traffic_ops_golang/cdn_lock/cdn_lock.go b/traffic_ops/traffic_ops_golang/cdn_lock/cdn_lock.go
index dbb54ab..2c9e31f 100644
--- a/traffic_ops/traffic_ops_golang/cdn_lock/cdn_lock.go
+++ b/traffic_ops/traffic_ops_golang/cdn_lock/cdn_lock.go
@@ -93,9 +93,6 @@ func Create(w http.ResponseWriter, r *http.Request) {
 	}
 	defer inf.Close()
 	tx := inf.Tx.Tx
-	if inf.User == nil {
-		api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("couldn't get user for the current request"))
-	}
 	var cdnLock tc.CDNLock
 	if err := json.NewDecoder(r.Body).Decode(&cdnLock); err != nil {
 		api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
@@ -151,9 +148,6 @@ func Delete(w http.ResponseWriter, r *http.Request) {
 
 	cdn := inf.Params["cdn"]
 	tx := inf.Tx.Tx
-	if inf.User == nil {
-		api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("couldn't get user for the current request"))
-	}
 	var result tc.CDNLock
 	var err error
 	if inf.User.PrivLevel == auth.PrivLevelAdmin {
diff --git a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
index 575cd3b..34c0a9a 100644
--- a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
+++ b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
@@ -207,6 +207,145 @@ func BuildWhereAndOrderByAndPagination(parameters map[string]string, queryParams
 	return whereClause, orderBy, paginationClause, queryValues, errs
 }
 
+// CheckIfCurrentUserCanModifyCDNs checks if the current user has the lock on the list of cdns that the requested operation is to be performed on.
+// This will succeed if the either there is no lock by any user on any of the CDNs, or if the current user has the lock on any of the CDNs.
+func CheckIfCurrentUserCanModifyCDNs(tx *sql.Tx, cdns []string, user string) (error, error, int) {
+	query := `SELECT username, soft, cdn FROM cdn_lock WHERE cdn=ANY($1)`
+	var userName, cdn string
+	var soft bool
+	rows, err := tx.Query(query, pq.Array(cdns))
+	if err != nil {
+		if errors.Is(err, sql.ErrNoRows) {
+			return nil, nil, http.StatusOK
+		}
+		return nil, errors.New("querying cdn_lock for user " + user + " and cdn " + cdn + ": " + err.Error()), http.StatusInternalServerError
+	}
+	defer rows.Close()
+	for rows.Next() {
+		err = rows.Scan(&userName, &soft, &cdn)
+		if err != nil {
+			return nil, errors.New("scanning cdn_lock for user " + user + " and cdn " + cdn + ": " + err.Error()), http.StatusInternalServerError
+		}
+		if userName != "" && user != userName && !soft {
+			return errors.New("user " + userName + " currently has a hard lock on cdn " + cdn), nil, http.StatusForbidden
+		}
+	}
+	return nil, nil, http.StatusOK
+}
+
+// CheckIfCurrentUserCanModifyCDNs checks if the current user has the lock on the list of cdns(identified by ID) that the requested operation is to be performed on.
+// This will succeed if the either there is no lock by any user on any of the CDNs, or if the current user has the lock on any of the CDNs.
+func CheckIfCurrentUserCanModifyCDNsByID(tx *sql.Tx, cdns []int, user string) (error, error, int) {
+	query := `SELECT name FROM cdn WHERE id=ANY($1)`
+	var name string
+	var cdnNames []string
+	rows, err := tx.Query(query, pq.Array(cdns))
+	if err != nil {
+		return nil, errors.New("no cdn names found for the given IDs: " + err.Error()), http.StatusInternalServerError
+	}
+	defer rows.Close()
+	for rows.Next() {
+		err = rows.Scan(&name)
+		if err != nil {
+			return nil, errors.New("scanning cdn name: " + err.Error()), http.StatusInternalServerError
+		}
+		cdnNames = append(cdnNames, name)
+	}
+	return CheckIfCurrentUserCanModifyCDNs(tx, cdnNames, user)
+}
+
+// CheckIfCurrentUserCanModifyCDN checks if the current user has the lock on the cdn that the requested operation is to be performed on.
+// This will succeed if the either there is no lock by any user on the CDN, or if the current user has the lock on the CDN.
+func CheckIfCurrentUserCanModifyCDN(tx *sql.Tx, cdn, user string) (error, error, int) {
+	query := `SELECT username, soft FROM cdn_lock WHERE cdn=$1`
+	var userName string
+	var soft bool
+	rows, err := tx.Query(query, cdn)
+	if err != nil {
+		if errors.Is(err, sql.ErrNoRows) {
+			return nil, nil, http.StatusOK
+		}
+		return nil, errors.New("querying cdn_lock for user " + user + " and cdn " + cdn + ": " + err.Error()), http.StatusInternalServerError
+	}
+	defer rows.Close()
+	for rows.Next() {
+		err = rows.Scan(&userName, &soft)
+		if err != nil {
+			return nil, errors.New("scanning cdn_lock for user " + user + " and cdn " + cdn + ": " + err.Error()), http.StatusInternalServerError
+		}
+		if userName != "" && user != userName && !soft {
+			return errors.New("user " + userName + " currently has a hard lock on cdn " + cdn), nil, http.StatusForbidden
+		}
+	}
+	return nil, nil, http.StatusOK
+}
+
+// CheckIfCurrentUserCanModifyCDNWithID checks if the current user has the lock on the cdn (identified by ID) that the requested operation is to be performed on.
+// This will succeed if the either there is no lock by any user on the CDN, or if the current user has the lock on the CDN.
+func CheckIfCurrentUserCanModifyCDNWithID(tx *sql.Tx, cdnID int64, user string) (error, error, int) {
+	cdnName, ok, err := GetCDNNameFromID(tx, cdnID)
+	if err != nil {
+		return nil, err, http.StatusInternalServerError
+	} else if !ok {
+		return errors.New("CDN not found"), nil, http.StatusNotFound
+	}
+	return CheckIfCurrentUserCanModifyCDN(tx, string(cdnName), user)
+}
+
+// CheckIfCurrentUserCanModifyCachegroup checks if the current user has the lock on the cdns that are associated with the provided cachegroup ID.
+// This will succeed if no other user has a hard lock on any of the CDNs that relate to the cachegroup in question.
+func CheckIfCurrentUserCanModifyCachegroup(tx *sql.Tx, cachegroupID int, user string) (error, error, int) {
+	query := `SELECT username, cdn, soft FROM cdn_lock WHERE cdn IN (SELECT name FROM cdn WHERE id IN (SELECT cdn_id FROM server WHERE cachegroup = ($1)))`
+	var userName string
+	var cdn string
+	var soft bool
+	rows, err := tx.Query(query, cachegroupID)
+	if err != nil {
+		if errors.Is(err, sql.ErrNoRows) {
+			return nil, nil, http.StatusOK
+		}
+		return nil, errors.New("querying cdn_lock for user " + user + " and cachegroup ID " + strconv.Itoa(cachegroupID) + ": " + err.Error()), http.StatusInternalServerError
+	}
+	defer rows.Close()
+	for rows.Next() {
+		err = rows.Scan(&userName, &cdn, &soft)
+		if err != nil {
+			return nil, errors.New("scanning cdn_lock for user " + user + " and cachegroup ID " + strconv.Itoa(cachegroupID) + ": " + err.Error()), http.StatusInternalServerError
+		}
+		if userName != "" && user != userName && !soft {
+			return errors.New("user " + userName + " currently has a hard lock on cdn " + cdn), nil, http.StatusForbidden
+		}
+	}
+	return nil, nil, http.StatusOK
+}
+
+// CheckIfCurrentUserCanModifyCachegroups checks if the current user has the lock on the cdns that are associated with the provided cachegroup IDs.
+// This will succeed if no other user has a hard lock on any of the CDNs that relate to the cachegroups in question.
+func CheckIfCurrentUserCanModifyCachegroups(tx *sql.Tx, cachegroupIDs []int, user string) (error, error, int) {
+	query := `SELECT username, cdn, soft FROM cdn_lock WHERE cdn IN (SELECT name FROM cdn WHERE id IN (SELECT cdn_id FROM server WHERE cachegroup = ANY($1)))`
+	var userName string
+	var cdn string
+	var soft bool
+	rows, err := tx.Query(query, pq.Array(cachegroupIDs))
+	if err != nil {
+		if errors.Is(err, sql.ErrNoRows) {
+			return nil, nil, http.StatusOK
+		}
+		return nil, errors.New("querying cachegroups cdn_lock for user " + user + ": " + err.Error()), http.StatusInternalServerError
+	}
+	defer rows.Close()
+	for rows.Next() {
+		err = rows.Scan(&userName, &cdn, &soft)
+		if err != nil {
+			return nil, errors.New("scanning cachegroups cdn_lock for user " + user + ": " + err.Error()), http.StatusInternalServerError
+		}
+		if userName != "" && user != userName && !soft {
+			return errors.New("user " + userName + " currently has a hard lock on cdn " + cdn), nil, http.StatusForbidden
+		}
+	}
+	return nil, nil, http.StatusOK
+}
+
 func parseCriteriaAndQueryValues(queryParamsToSQLCols map[string]WhereColumnInfo, parameters map[string]string) (string, map[string]interface{}, []error) {
 	var criteria string
 
@@ -342,7 +481,7 @@ WHERE ds.id = $1
 		if err == sql.ErrNoRows {
 			return tc.DeliveryServiceName(""), tc.CDNName(""), false, nil
 		}
-		return tc.DeliveryServiceName(""), tc.CDNName(""), false, errors.New("querying delivery service name: " + err.Error())
+		return tc.DeliveryServiceName(""), tc.CDNName(""), false, errors.New("querying delivery service name and CDN name: " + err.Error())
 	}
 	return name, cdn, true, nil
 }
@@ -360,7 +499,7 @@ WHERE ds.xml_id = $1
 		if err == sql.ErrNoRows {
 			return dsId, tc.CDNName(""), false, nil
 		}
-		return dsId, tc.CDNName(""), false, errors.New("querying delivery service name: " + err.Error())
+		return dsId, tc.CDNName(""), false, errors.New("querying delivery service ID and CDN name: " + err.Error())
 	}
 	return dsId, cdn, true, nil
 }
@@ -554,7 +693,7 @@ func GetCDNNameFromID(tx *sql.Tx, id int64) (tc.CDNName, bool, error) {
 		if err == sql.ErrNoRows {
 			return "", false, nil
 		}
-		return "", false, errors.New("querying CDN ID: " + err.Error())
+		return "", false, errors.New("querying CDN name from ID: " + err.Error())
 	}
 	return tc.CDNName(name), true, nil
 }
@@ -575,7 +714,7 @@ func GetCDNIDFromName(tx *sql.Tx, name tc.CDNName) (int, bool, error) {
 		if err == sql.ErrNoRows {
 			return id, false, nil
 		}
-		return id, false, errors.New("querying CDN ID: " + err.Error())
+		return id, false, errors.New("querying CDN ID from name: " + err.Error())
 	}
 	return id, true, nil
 }
@@ -1259,3 +1398,101 @@ func CheckTopologyOrgServerCGInDSCG(tx *sql.Tx, cdnIds []int, dsTopology string,
 	}
 	return nil, nil, http.StatusOK
 }
+
+// GetCDNNameFromProfileID returns the cdn name for the provided profile ID.
+func GetCDNNameFromProfileID(tx *sql.Tx, id int) (tc.CDNName, error) {
+	name := ""
+	if err := tx.QueryRow(`SELECT name FROM cdn WHERE id = (SELECT cdn FROM profile WHERE id = $1)`, id).Scan(&name); err != nil {
+		return "", errors.New("querying CDN name from profile ID: " + err.Error())
+	}
+	return tc.CDNName(name), nil
+}
+
+// GetCDNNameFromProfileName returns the cdn name for the provided profile name.
+func GetCDNNameFromProfileName(tx *sql.Tx, profileName string) (tc.CDNName, error) {
+	name := ""
+	if err := tx.QueryRow(`SELECT name FROM cdn WHERE id = (SELECT cdn FROM profile WHERE name = $1)`, profileName).Scan(&name); err != nil {
+		return "", errors.New("querying CDN name from profile name: " + err.Error())
+	}
+	return tc.CDNName(name), nil
+}
+
+// GetServerIDsFromCachegroupNames returns a list of servers IDs for a list of cachegroup IDs.
+func GetServerIDsFromCachegroupNames(tx *sql.Tx, cgID []string) ([]int64, error) {
+	var serverIDs []int64
+	var serverID int64
+	query := `SELECT server.id FROM server JOIN cachegroup cg ON cg.id = server.cachegroup where cg.name = ANY($1)`
+	rows, err := tx.Query(query, pq.Array(cgID))
+	if err != nil {
+		return serverIDs, errors.New("getting server IDs from cachegroup names : " + err.Error())
+	}
+	defer log.Close(rows, "could not close rows in GetServerIDsFromCachegroupNames")
+	for rows.Next() {
+		err = rows.Scan(&serverID)
+		if err != nil {
+			return serverIDs, errors.New("scanning server ID : " + err.Error())
+		}
+		serverIDs = append(serverIDs, serverID)
+	}
+	return serverIDs, nil
+}
+
+// GetCDNNamesFromServerIds returns a list of cdn names for a list of server IDs.
+func GetCDNNamesFromServerIds(tx *sql.Tx, serverIds []int64) ([]string, error) {
+	var cdns []string
+	cdn := ""
+	query := `SELECT DISTINCT(name) FROM cdn JOIN server ON cdn.id = server.cdn_id WHERE server.id = ANY($1)`
+	rows, err := tx.Query(query, pq.Array(serverIds))
+	if err != nil {
+		return cdns, errors.New("getting cdn name for server : " + err.Error())
+	}
+	defer log.Close(rows, "could not close rows in GetCDNNamesFromServerIds")
+	for rows.Next() {
+		err = rows.Scan(&cdn)
+		if err != nil {
+			return cdns, errors.New("scanning cdn name " + cdn + ": " + err.Error())
+		}
+		cdns = append(cdns, cdn)
+	}
+	return cdns, nil
+}
+
+// GetCDNNamesFromDSIds returns a list of cdn names for a list of DS IDs.
+func GetCDNNamesFromDSIds(tx *sql.Tx, dsIds []int) ([]string, error) {
+	var cdns []string
+	cdn := ""
+	query := `SELECT DISTINCT(name) FROM cdn JOIN deliveryservice ON cdn.id = deliveryservice.cdn_id WHERE deliveryservice.id = ANY($1)`
+	rows, err := tx.Query(query, pq.Array(dsIds))
+	if err != nil {
+		return cdns, errors.New("getting cdn name for DS : " + err.Error())
+	}
+	defer log.Close(rows, "could not close rows in GetCDNNamesFromDSIds")
+	for rows.Next() {
+		err = rows.Scan(&cdn)
+		if err != nil {
+			return cdns, errors.New("scanning cdn name " + cdn + ": " + err.Error())
+		}
+		cdns = append(cdns, cdn)
+	}
+	return cdns, nil
+}
+
+// GetCDNNamesFromProfileIDs returns a list of cdn names for a list of profile IDs.
+func GetCDNNamesFromProfileIDs(tx *sql.Tx, profileIDs []int64) ([]string, error) {
+	var cdns []string
+	cdn := ""
+	query := `SELECT DISTINCT(cdn.name) FROM cdn JOIN profile ON cdn.id = profile.cdn WHERE profile.id = ANY($1)`
+	rows, err := tx.Query(query, pq.Array(profileIDs))
+	if err != nil {
+		return cdns, errors.New("getting cdn name for profiles : " + err.Error())
+	}
+	defer log.Close(rows, "could not close rows in GetCDNNamesFromProfileIDs")
+	for rows.Next() {
+		err = rows.Scan(&cdn)
+		if err != nil {
+			return cdns, errors.New("scanning cdn name " + cdn + ": " + err.Error())
+		}
+		cdns = append(cdns, cdn)
+	}
+	return cdns, nil
+}
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/acme.go b/traffic_ops/traffic_ops_golang/deliveryservice/acme.go
index cab649e..e1eb546 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/acme.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/acme.go
@@ -205,10 +205,18 @@ func GenerateAcmeCertificates(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("delivery service not in cdn"), nil)
 		return
 	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdnName), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		defer cancelTx()
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
 
 	asyncStatusId, errCode, userErr, sysErr := api.InsertAsyncStatus(inf.Tx.Tx, "ACME async job has started.")
 	if userErr != nil || sysErr != nil {
+		defer cancelTx()
 		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
 	}
 
 	go GetAcmeCertificates(inf.Config, req, ctx, cancelTx, true, inf.User, asyncStatusId, inf.Vault)
@@ -265,6 +273,13 @@ func GenerateLetsEncryptCertificates(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdnName), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		defer cancelTx()
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
+
 	userErr, sysErr, errCode = tenant.CheckID(inf.Tx.Tx, inf.User, dsID)
 	if userErr != nil || sysErr != nil {
 		defer cancelTx()
@@ -291,7 +306,9 @@ func GenerateLetsEncryptCertificates(w http.ResponseWriter, r *http.Request) {
 
 	asyncStatusId, errCode, userErr, sysErr := api.InsertAsyncStatus(inf.Tx.Tx, "ACME async job has started.")
 	if userErr != nil || sysErr != nil {
+		defer cancelTx()
 		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
 	}
 
 	go GetAcmeCertificates(inf.Config, req, ctx, cancelTx, true, inf.User, asyncStatusId, inf.Vault)
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/acme_renew.go b/traffic_ops/traffic_ops_golang/deliveryservice/acme_renew.go
index 20c15df..6d19f6d 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/acme_renew.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/acme_renew.go
@@ -30,6 +30,7 @@ import (
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/auth"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault"
 
@@ -56,10 +57,25 @@ func RenewAcmeCertificate(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	_, cdn, ok, err := dbhelpers.GetDSIDAndCDNFromName(inf.Tx.Tx, xmlID)
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("renew acme certificate: getting CDN from DS XML ID "+err.Error()))
+		return
+	}
+	if !ok {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, nil, nil)
+		return
+	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdn), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
+
 	ctx, cancelTx := context.WithTimeout(r.Context(), AcmeTimeout)
 	defer cancelTx()
 
-	userErr, sysErr, statusCode := renewAcmeCerts(inf.Config, xmlID, ctx, r.Context(), inf.User, inf.Vault)
+	userErr, sysErr, statusCode = renewAcmeCerts(inf.Config, xmlID, ctx, r.Context(), inf.User, inf.Vault)
 	if userErr != nil || sysErr != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
 		return
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go b/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go
index 87707a4..9ab6a57 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go
@@ -33,6 +33,7 @@ import (
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/auth"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault"
 )
 
@@ -79,13 +80,12 @@ func renewCertificates(w http.ResponseWriter, r *http.Request, deprecated bool)
 		return
 	}
 	defer inf.Close()
-
 	if !inf.Config.TrafficVaultEnabled {
 		api.HandleErrOptionalDeprecation(w, r, inf.Tx.Tx, http.StatusInternalServerError, errors.New("the Traffic Vault service is unavailable"), errors.New("getting SSL keys from Traffic Vault by xml id: Traffic Vault is not configured"), deprecated, deprecation)
 		return
 	}
 
-	rows, err := inf.Tx.Tx.Query(`SELECT xml_id, ssl_key_version FROM deliveryservice WHERE ssl_key_version != 0`)
+	rows, err := inf.Tx.Tx.Query(`SELECT xml_id, ssl_key_version, cdn_id FROM deliveryservice WHERE ssl_key_version != 0`)
 	if err != nil {
 		api.HandleErrOptionalDeprecation(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err, deprecated, deprecation)
 		return
@@ -93,16 +93,27 @@ func renewCertificates(w http.ResponseWriter, r *http.Request, deprecated bool)
 	defer rows.Close()
 
 	existingCerts := []ExistingCerts{}
+	cdnMap := make(map[int]bool)
+	cdns := []int{}
+	var cdn int
 	for rows.Next() {
 		ds := DsKey{}
-		err := rows.Scan(&ds.XmlId, &ds.Version)
+		err := rows.Scan(&ds.XmlId, &ds.Version, &cdn)
 		if err != nil {
 			api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
 			return
 		}
+		cdnMap[cdn] = true
 		existingCerts = append(existingCerts, ExistingCerts{Version: ds.Version, XmlId: ds.XmlId})
 	}
-
+	for k, _ := range cdnMap {
+		cdns = append(cdns, k)
+	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDNsByID(inf.Tx.Tx, cdns, inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
 	ctx, cancelTx := context.WithTimeout(r.Context(), AcmeTimeout*time.Duration(len(existingCerts)))
 
 	asyncStatusId, errCode, userErr, sysErr := api.InsertAsyncStatus(inf.Tx.Tx, "ACME async job has started.")
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
index 2515fdc..5fa02a3 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
@@ -403,6 +403,11 @@ func createV40(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV40 t
 	if errCode, userErr, sysErr := dbhelpers.CheckTopology(inf.Tx, ds); userErr != nil || sysErr != nil {
 		return nil, errCode, userErr, sysErr
 	}
+
+	userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(*ds.CDNID), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		return nil, errCode, userErr, sysErr
+	}
 	var resultRows *sql.Rows
 	if omitExtraLongDescFields {
 		if ds.LongDesc1 != nil || ds.LongDesc2 != nil {
@@ -848,7 +853,6 @@ func UpdateV40(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	defer inf.Close()
-
 	id := inf.IntParams["id"]
 
 	ds := tc.DeliveryServiceV40{}
@@ -857,6 +861,16 @@ func UpdateV40(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	ds.ID = &id
+	_, cdn, _, err := dbhelpers.GetDSNameAndCDNFromID(inf.Tx.Tx, id)
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deliveryservice update: getting CDN from DS ID "+err.Error()))
+		return
+	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdn), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
 	res, status, userErr, sysErr := updateV40(w, r, inf, &ds, true)
 	if userErr != nil || sysErr != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, status, userErr, sysErr)
@@ -1382,6 +1396,26 @@ func (ds *TODeliveryService) Delete() (error, error, int) {
 	}
 	ds.XMLID = &xmlID
 
+	if ds.CDNID != nil {
+		userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(ds.APIInfo().Tx.Tx, int64(*ds.CDNID), ds.APIInfo().User.UserName)
+		if userErr != nil || sysErr != nil {
+			return userErr, sysErr, errCode
+		}
+	} else if ds.CDNName != nil {
+		userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(ds.APIInfo().Tx.Tx, *ds.CDNName, ds.APIInfo().User.UserName)
+		if userErr != nil || sysErr != nil {
+			return userErr, sysErr, errCode
+		}
+	} else {
+		_, cdnName, _, err := dbhelpers.GetDSNameAndCDNFromID(ds.ReqInfo.Tx.Tx, *ds.ID)
+		if err != nil {
+			return nil, fmt.Errorf("couldn't get cdn name for DS: %v", err), http.StatusBadRequest
+		}
+		userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(ds.APIInfo().Tx.Tx, string(cdnName), ds.APIInfo().User.UserName)
+		if userErr != nil || sysErr != nil {
+			return userErr, sysErr, errCode
+		}
+	}
 	// Note ds regexes MUST be deleted before the ds, because there's a ON DELETE CASCADE on deliveryservice_regex (but not on regex).
 	// Likewise, it MUST happen in a transaction with the later DS delete, so they aren't deleted if the DS delete fails.
 	if _, err := ds.ReqInfo.Tx.Tx.Exec(`DELETE FROM regex WHERE id IN (SELECT regex FROM deliveryservice_regex WHERE deliveryservice=$1)`, *ds.ID); err != nil {
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_required_capabilities.go b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_required_capabilities.go
index e45e071..20d26a8 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_required_capabilities.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_required_capabilities.go
@@ -252,7 +252,14 @@ func (rc *RequiredCapability) Delete() (error, error, int) {
 	} else if err != nil {
 		return nil, fmt.Errorf("checking authorization for existing DS ID: %s" + err.Error()), http.StatusInternalServerError
 	}
-
+	_, cdnName, _, err := dbhelpers.GetDSNameAndCDNFromID(rc.ReqInfo.Tx.Tx, *rc.DeliveryServiceID)
+	if err != nil {
+		return nil, err, http.StatusInternalServerError
+	}
+	userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(rc.ReqInfo.Tx.Tx, string(cdnName), rc.ReqInfo.User.UserName)
+	if userErr != nil || sysErr != nil {
+		return userErr, sysErr, errCode
+	}
 	return api.GenericDelete(rc)
 }
 
@@ -265,6 +272,15 @@ func (rc *RequiredCapability) Create() (error, error, int) {
 		return nil, fmt.Errorf("checking authorization for existing DS ID: %s" + err.Error()), http.StatusInternalServerError
 	}
 
+	_, cdnName, _, err := dbhelpers.GetDSNameAndCDNFromID(rc.ReqInfo.Tx.Tx, *rc.DeliveryServiceID)
+	if err != nil {
+		return nil, err, http.StatusInternalServerError
+	}
+	userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(rc.ReqInfo.Tx.Tx, string(cdnName), rc.ReqInfo.User.UserName)
+	if userErr != nil || sysErr != nil {
+		return userErr, sysErr, errCode
+	}
+
 	// Ensure DS type is only of HTTP*, DNS* types
 	dsType, reqCaps, topology, dsExists, err := dbhelpers.GetDeliveryServiceTypeRequiredCapabilitiesAndTopology(*rc.DeliveryServiceID, rc.APIInfo().Tx.Tx)
 	if err != nil {
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_required_capabilities_test.go b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_required_capabilities_test.go
index e696d18..2445bbd 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_required_capabilities_test.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_required_capabilities_test.go
@@ -80,6 +80,8 @@ func TestCreateDeliveryServicesRequiredCapability(t *testing.T) {
 	typeRows := sqlmock.NewRows([]string{"name", "required_capabilities", "topology"}).AddRow(
 		"HTTP", "{}", nil,
 	)
+	mock.ExpectQuery("SELECT").WillReturnRows(sqlmock.NewRows([]string{"xml_id", "name"}).AddRow("name", "cdnName"))
+	mock.ExpectQuery("SELECT username").WillReturnRows(sqlmock.NewRows(nil))
 	mock.ExpectQuery("SELECT t.name.*").WillReturnRows(typeRows)
 
 	scRows := sqlmock.NewRows([]string{"name"}).AddRow(
@@ -244,6 +246,8 @@ func TestDeleteDeliveryServicesRequiredCapability(t *testing.T) {
 	mock.ExpectBegin()
 	mockTenantID(t, mock, 1)
 
+	mock.ExpectQuery("SELECT").WillReturnRows(sqlmock.NewRows([]string{"xml_id", "name"}).AddRow("name", "cdnName"))
+	mock.ExpectQuery("SELECT username").WillReturnRows(sqlmock.NewRows(nil))
 	mock.ExpectExec("DELETE").WillReturnResult(sqlmock.NewResult(1, 1))
 
 	rc := RequiredCapability{
@@ -366,6 +370,8 @@ func TestCreateDeliveryServicesRequiredCapabilityInvalidDSType(t *testing.T) {
 	typeRows := sqlmock.NewRows([]string{"name", "required_capabilities", "topology"}).AddRow(
 		"ANY_MAP", "{}", nil,
 	)
+	mock.ExpectQuery("SELECT").WillReturnRows(sqlmock.NewRows([]string{"xml_id", "name"}).AddRow("ds1", "cdnName"))
+	mock.ExpectQuery("SELECT username").WillReturnRows(sqlmock.NewRows(nil))
 	mock.ExpectQuery("SELECT t.name.*").WillReturnRows(typeRows)
 
 	userErr, sysErr, errCode := rc.Create()
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/keys.go b/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
index 40e6e10..454639f 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
@@ -37,6 +37,7 @@ import (
 	"github.com/apache/trafficcontrol/lib/go-tc"
 	"github.com/apache/trafficcontrol/lib/go-util"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault"
 )
@@ -76,7 +77,16 @@ func AddSSLKeys(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, errors.New("no DS with name "+*req.DeliveryService), nil)
 		return
 	}
-
+	_, cdn, _, err := dbhelpers.GetDSNameAndCDNFromID(inf.Tx.Tx, dsID)
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deliveryservice.AddSSLKeys: getting CDN from DS ID "+err.Error()))
+		return
+	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdn), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
 	// ECDSA keys support is only permitted for DNS delivery services
 	// Traffic Router (HTTP* delivery service types) do not support ECDSA keys
 	dsType, dsFound, err := getDSType(inf.Tx.Tx, *req.Key)
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/safe.go b/traffic_ops/traffic_ops_golang/deliveryservice/safe.go
index 616d793..0d01432 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/safe.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/safe.go
@@ -28,6 +28,7 @@ import (
 	"github.com/apache/trafficcontrol/lib/go-log"
 	"github.com/apache/trafficcontrol/lib/go-tc"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
 )
 
@@ -85,6 +86,17 @@ func UpdateSafe(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, tx, http.StatusBadRequest, fmt.Errorf("decoding: %s", err), nil)
 		return
 	}
+
+	_, cdn, _, err := dbhelpers.GetDSNameAndCDNFromID(inf.Tx.Tx, dsID)
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deliveryservice update safe: getting CDN from DS ID "+err.Error()))
+		return
+	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdn), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
 	if version.Major > 3 && version.Minor >= 0 {
 		if dsr.LongDesc1 != nil {
 			api.HandleErr(w, r, tx, http.StatusBadRequest, errors.New("the longDesc1 field is no longer supported in API 4.0 onwards"), nil)
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/servers/delete.go b/traffic_ops/traffic_ops_golang/deliveryservice/servers/delete.go
index aba4fef..7177485 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/servers/delete.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/servers/delete.go
@@ -153,6 +153,13 @@ func delete(w http.ResponseWriter, r *http.Request, deprecated bool) {
 	}
 	dsName := *ds.XMLID
 
+	if ds.CDNName != nil {
+		userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, *ds.CDNName, inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+			return
+		}
+	}
 	serverName, exists, err := dbhelpers.GetServerNameFromID(tx, serverID)
 	if err != nil {
 		api.HandleErrOptionalDeprecation(w, r, tx, http.StatusInternalServerError, nil, errors.New("getting server name from id: "+err.Error()), deprecated, &alt)
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
index 242feaa..c5f9c58 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
@@ -450,6 +450,21 @@ func GetReplaceHandler(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
 		return
 	}
+	if ds.CDNID != nil {
+		cdn, ok, err := dbhelpers.GetCDNNameFromID(inf.Tx.Tx, int64(*ds.CDNID))
+		if err != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
+			return
+		} else if !ok {
+			api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, nil, nil)
+			return
+		}
+		userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdn), inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+			return
+		}
+	}
 	serverInfos, err := dbhelpers.GetServerInfosFromIDs(inf.Tx.Tx, servers)
 	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
@@ -521,6 +536,22 @@ func GetCreateHandler(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	if ds.CDNID != nil {
+		cdn, ok, err := dbhelpers.GetCDNNameFromID(inf.Tx.Tx, int64(*ds.CDNID))
+		if err != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
+			return
+		} else if !ok {
+			api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, nil, nil)
+			return
+		}
+		userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdn), inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+			return
+		}
+	}
+
 	// get list of server Ids to insert
 	payload := tc.DeliveryServiceServers{}
 	if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go
index 66a5183..7a2d123 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go
@@ -28,6 +28,7 @@ import (
 
 	"github.com/apache/trafficcontrol/lib/go-tc"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault"
 )
@@ -42,7 +43,6 @@ func GenerateSSLKeys(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	defer inf.Close()
-
 	if !inf.Config.TrafficVaultEnabled {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deliveryservice.GenerateSSLKeys: Traffic Vault is not configured"))
 		return
@@ -65,6 +65,16 @@ func GenerateSSLKeys(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, errors.New("no DS with name "+*req.DeliveryService), nil)
 		return
 	}
+	_, cdn, _, err := dbhelpers.GetDSNameAndCDNFromID(inf.Tx.Tx, dsID)
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deliveryservice.GenerateSSLKeys: getting CDN from DS ID "+err.Error()))
+		return
+	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdn), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
 	if err := generatePutRiakKeys(req, inf.Tx.Tx, inf.Vault, r.Context()); err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("generating and putting SSL keys: "+err.Error()))
 		return
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/urlkey.go b/traffic_ops/traffic_ops_golang/deliveryservice/urlkey.go
index bea441b..6fca387 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/urlkey.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/urlkey.go
@@ -204,6 +204,25 @@ func CopyURLKeys(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	destinationDSID, ok, err := getDSIDFromName(inf.Tx.Tx, string(ds))
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deliveryservice.CopySSLKeys: getting DS ID from name "+err.Error()))
+		return
+	} else if !ok {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, errors.New("no DS with name "+string(ds)), nil)
+		return
+	}
+	_, cdn, _, err := dbhelpers.GetDSNameAndCDNFromID(inf.Tx.Tx, destinationDSID)
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deliveryservice.GenerateSSLKeys: getting CDN from DS ID "+err.Error()))
+		return
+	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdn), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
+
 	if err := inf.Vault.PutURLSigKeys(string(ds), keys, inf.Tx.Tx, r.Context()); err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("setting URL Sig keys for '"+string(ds)+" copied from "+string(copyDS)+": "+err.Error()))
 		return
@@ -220,7 +239,6 @@ func GenerateURLKeys(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	defer inf.Close()
-
 	if !inf.Config.TrafficVaultEnabled {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, userErr, errors.New("deliveryservice.GenerateURLKeys: Traffic Vault is not configured"))
 		return
@@ -254,6 +272,17 @@ func GenerateURLKeys(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	_, cdn, _, err := dbhelpers.GetDSNameAndCDNFromID(inf.Tx.Tx, dsID)
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deliveryservice.GenerateURLKeys: getting CDN from DS ID "+err.Error()))
+		return
+	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdn), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
+
 	keys, err := GenerateURLSigKeys()
 	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("generating URL sig keys: "+err.Error()))
@@ -338,6 +367,17 @@ func DeleteURLKeysByID(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	_, cdn, _, err := dbhelpers.GetDSNameAndCDNFromID(inf.Tx.Tx, dsId)
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deliveryservice.DeleteURLKeysByID: getting CDN from DS ID "+err.Error()))
+		return
+	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdn), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
+
 	err = inf.Vault.DeleteURLSigKeys(string(ds), inf.Tx.Tx, r.Context())
 	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deleting URL Sig keys from Traffic Vault: "+err.Error()))
@@ -388,6 +428,16 @@ func DeleteURLKeysByName(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, errors.New("no DS with name "+string(ds)), nil)
 		return
 	}
+	_, cdn, _, err := dbhelpers.GetDSNameAndCDNFromID(inf.Tx.Tx, dsId)
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("deliveryservice.DeleteURLKeysByName: getting CDN from DS ID "+err.Error()))
+		return
+	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdn), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
 
 	err = inf.Vault.DeleteURLSigKeys(string(ds), inf.Tx.Tx, r.Context())
 	if err != nil {
diff --git a/traffic_ops/traffic_ops_golang/deliveryservicesregexes/deliveryservicesregexes.go b/traffic_ops/traffic_ops_golang/deliveryservicesregexes/deliveryservicesregexes.go
index b9d4d2d..6c67a5b 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservicesregexes/deliveryservicesregexes.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservicesregexes/deliveryservicesregexes.go
@@ -228,6 +228,17 @@ func Post(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	_, cdnName, _, err := dbhelpers.GetDSNameAndCDNFromID(inf.Tx.Tx, inf.IntParams["dsid"])
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
+		return
+	}
+	userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdnName), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
+
 	regexID := 0
 	if err := tx.QueryRow(`INSERT INTO regex (pattern, type) VALUES ($1, $2) RETURNING id`, dsr.Pattern, dsr.Type).Scan(&regexID); err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("inserting deliveryserviceregex regex: "+err.Error()))
@@ -318,7 +329,16 @@ func Put(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("malformed JSON"), nil)
 		return
 	}
-
+	_, cdnName, _, err := dbhelpers.GetDSNameAndCDNFromID(inf.Tx.Tx, inf.IntParams["dsid"])
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
+		return
+	}
+	userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdnName), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
 	// Get current details to make sure that you're not trying to change a regex that has set number = 0 and type = HOST_REGEXP
 	if err := getCurrentDetails(tx, dsID, regexID); err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, err, nil)
@@ -416,14 +436,20 @@ func Delete(w http.ResponseWriter, r *http.Request) {
 	defer inf.Close()
 
 	dsID := inf.IntParams["dsid"]
-	dsName, ok, err := dbhelpers.GetDSNameFromID(inf.Tx.Tx, dsID)
+	dsName, cdnName, ok, err := dbhelpers.GetDSNameAndCDNFromID(inf.Tx.Tx, inf.IntParams["dsid"])
 	if err != nil {
-		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("getting delivery service name from id: "+err.Error()))
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
 		return
-	} else if !ok {
+	}
+	if !ok {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, nil, nil)
 		return
 	}
+	userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdnName), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
 	regexID := inf.IntParams["regexid"]
 
 	// Get current details to make sure that you're not trying to delete a regex that has set number = 0 and type = HOST_REGEXP
diff --git a/traffic_ops/traffic_ops_golang/federations/ds.go b/traffic_ops/traffic_ops_golang/federations/ds.go
index e5e69ee..db3227e 100644
--- a/traffic_ops/traffic_ops_golang/federations/ds.go
+++ b/traffic_ops/traffic_ops_golang/federations/ds.go
@@ -63,6 +63,16 @@ func PostDSes(w http.ResponseWriter, r *http.Request) {
 			api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("A federation must have at least one delivery service assigned"), nil)
 			return
 		}
+		cdnNames, err := dbhelpers.GetCDNNamesFromDSIds(inf.Tx.Tx, post.DSIDs)
+		if err != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
+			return
+		}
+		userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDNs(inf.Tx.Tx, cdnNames, inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+			return
+		}
 		if err := deleteDSFeds(inf.Tx.Tx, fedID); err != nil {
 			userErr, sysErr, errCode := api.ParseDBError(err)
 			api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
@@ -184,6 +194,14 @@ func (v *TOFedDSes) Delete() (error, error, int) {
 	}
 	v.ID = &dsID
 
+	_, cdnName, _, err := dbhelpers.GetDSNameAndCDNFromID(v.ReqInfo.Tx.Tx, dsID)
+	if err != nil {
+		return nil, err, http.StatusInternalServerError
+	}
+	userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(v.ReqInfo.Tx.Tx, string(cdnName), v.ReqInfo.User.UserName)
+	if userErr != nil || sysErr != nil {
+		return userErr, sysErr, errCode
+	}
 	// Check that we can delete it
 	if respCode, usrErr, sysErr := checkFedDSDeletion(v.APIInfo().Tx.Tx, *v.fedID, dsID); usrErr != nil || sysErr != nil {
 		if usrErr != nil {
diff --git a/traffic_ops/traffic_ops_golang/origin/origins.go b/traffic_ops/traffic_ops_golang/origin/origins.go
index 1241e0d..036b011 100644
--- a/traffic_ops/traffic_ops_golang/origin/origins.go
+++ b/traffic_ops/traffic_ops_golang/origin/origins.go
@@ -316,6 +316,15 @@ func (origin *TOOrigin) Update(h http.Header) (error, error, int) {
 		return errors.New("cannot update the delivery service of a primary origin"), nil, http.StatusBadRequest
 	}
 
+	_, cdnName, _, err := dbhelpers.GetDSNameAndCDNFromID(origin.ReqInfo.Tx.Tx, *origin.DeliveryServiceID)
+	if err != nil {
+		return nil, err, http.StatusInternalServerError
+	}
+	userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(origin.ReqInfo.Tx.Tx, string(cdnName), origin.ReqInfo.User.UserName)
+	if userErr != nil || sysErr != nil {
+		return userErr, sysErr, errCode
+	}
+
 	log.Debugf("about to run exec query: %s with origin: %++v", updateQuery(), origin)
 	resultRows, err := origin.ReqInfo.Tx.NamedQuery(updateQuery(), origin)
 	if err != nil {
@@ -373,6 +382,15 @@ func (origin *TOOrigin) Create() (error, error, int) {
 		return userErr, sysErr, errCode
 	}
 
+	_, cdnName, _, err := dbhelpers.GetDSNameAndCDNFromID(origin.ReqInfo.Tx.Tx, *origin.DeliveryServiceID)
+	if err != nil {
+		return nil, err, http.StatusInternalServerError
+	}
+	userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(origin.ReqInfo.Tx.Tx, string(cdnName), origin.ReqInfo.User.UserName)
+	if userErr != nil || sysErr != nil {
+		return userErr, sysErr, errCode
+	}
+
 	resultRows, err := origin.ReqInfo.Tx.NamedQuery(insertQuery(), origin)
 	if err != nil {
 		return api.ParseDBError(err)
@@ -441,6 +459,16 @@ func (origin *TOOrigin) Delete() (error, error, int) {
 		return errors.New("cannot delete a primary origin"), nil, http.StatusBadRequest
 	}
 
+	if origin.DeliveryServiceID != nil {
+		_, cdnName, _, err := dbhelpers.GetDSNameAndCDNFromID(origin.ReqInfo.Tx.Tx, *origin.DeliveryServiceID)
+		if err != nil {
+			return nil, err, http.StatusInternalServerError
+		}
+		userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(origin.ReqInfo.Tx.Tx, string(cdnName), origin.ReqInfo.User.UserName)
+		if userErr != nil || sysErr != nil {
+			return userErr, sysErr, errCode
+		}
+	}
 	result, err := origin.ReqInfo.Tx.NamedExec(deleteQuery(), origin)
 	if err != nil {
 		return nil, errors.New("origin delete: query: " + err.Error()), http.StatusInternalServerError
diff --git a/traffic_ops/traffic_ops_golang/profile/copy.go b/traffic_ops/traffic_ops_golang/profile/copy.go
index eb69781..40669cf 100644
--- a/traffic_ops/traffic_ops_golang/profile/copy.go
+++ b/traffic_ops/traffic_ops_golang/profile/copy.go
@@ -20,12 +20,15 @@ package profile
  */
 
 import (
+	"database/sql"
+	"errors"
 	"fmt"
 	"net/http"
 
 	"github.com/apache/trafficcontrol/lib/go-log"
 	"github.com/apache/trafficcontrol/lib/go-tc"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/profileparameter"
 )
 
@@ -50,7 +53,6 @@ func CopyProfileHandler(w http.ResponseWriter, r *http.Request) {
 			Name:         inf.Params["new_profile"],
 		},
 	}
-
 	errs := copyProfile(inf, &p.Response)
 	if errs.userErr != nil || errs.sysErr != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, errs.errCode, errs.userErr, errs.sysErr)
@@ -116,6 +118,29 @@ func copyProfile(inf *api.APIInfo, p *tc.ProfileCopy) errorDetails {
 		}
 	}
 
+	cdnName, err := dbhelpers.GetCDNNameFromProfileName(inf.Tx.Tx, p.ExistingName)
+	if err != nil {
+		if errors.Is(err, sql.ErrNoRows) {
+			return errorDetails{
+				userErr: errors.New("no cdn for the given profile"),
+				sysErr:  nil,
+				errCode: http.StatusBadRequest,
+			}
+		}
+		return errorDetails{
+			userErr: nil,
+			sysErr:  err,
+			errCode: http.StatusInternalServerError,
+		}
+	}
+	userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdnName), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		return errorDetails{
+			userErr: userErr,
+			sysErr:  sysErr,
+			errCode: errCode,
+		}
+	}
 	// use existing CRUD helpers to create the new profile
 	toProfile.ProfileNullable = profiles[0].(tc.ProfileNullable)
 	toProfile.ProfileNullable.Name = &p.Name
diff --git a/traffic_ops/traffic_ops_golang/profile/copy_test.go b/traffic_ops/traffic_ops_golang/profile/copy_test.go
index cdd6f92..b29b680 100644
--- a/traffic_ops/traffic_ops_golang/profile/copy_test.go
+++ b/traffic_ops/traffic_ops_golang/profile/copy_test.go
@@ -209,8 +209,12 @@ func TestCopyProfile(t *testing.T) {
 	mock.ExpectBegin()
 	mockFindProfile(t, mock, profile.Response.Name, 0)
 	mockReadProfile(t, mock, existingProfile, 1)
+	mock.ExpectQuery("SELECT").WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("cdnName"))
+	mock.ExpectQuery("SELECT username").WillReturnRows(sqlmock.NewRows(nil))
 	mockInsertProfile(t, mock, expectedID)
 	mockFindParams(t, mock, profile.Response.ExistingName)
+	mock.ExpectQuery("SELECT").WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("cdnName"))
+	mock.ExpectQuery("SELECT username").WillReturnRows(sqlmock.NewRows(nil))
 	mockInsertParams(t, mock, profile.Response.ID)
 
 	req := mockHTTPReq(t, "profiles/name/{new_profile}/copy/{existing_profile}", db)
diff --git a/traffic_ops/traffic_ops_golang/profile/profile_import.go b/traffic_ops/traffic_ops_golang/profile/profile_import.go
index c27e5ee..b375a90 100644
--- a/traffic_ops/traffic_ops_golang/profile/profile_import.go
+++ b/traffic_ops/traffic_ops_golang/profile/profile_import.go
@@ -26,9 +26,10 @@ import (
 	"net/http"
 
 	"github.com/apache/trafficcontrol/lib/go-tc"
-	"github.com/lib/pq"
-
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
+
+	"github.com/lib/pq"
 )
 
 // ImportProfileHandler handles importing profile
@@ -46,6 +47,13 @@ func ImportProfileHandler(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, err, nil)
 		return
 	}
+	if importedProfile.Profile.CDNName == nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("no CDN Name in the profile to be imported"), nil)
+	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, *importedProfile.Profile.CDNName, inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+	}
 
 	id, err := importProfile(&importedProfile.Profile, inf.Tx.Tx)
 	if err != nil {
diff --git a/traffic_ops/traffic_ops_golang/profile/profiles.go b/traffic_ops/traffic_ops_golang/profile/profiles.go
index 1c166c9..a89016a 100644
--- a/traffic_ops/traffic_ops_golang/profile/profiles.go
+++ b/traffic_ops/traffic_ops_golang/profile/profiles.go
@@ -247,9 +247,66 @@ JOIN profile_parameter pp ON pp.parameter = p.id
 WHERE pp.profile = :profile_id`
 }
 
-func (pr *TOProfile) Update(h http.Header) (error, error, int) { return api.GenericUpdate(h, pr) }
-func (pr *TOProfile) Create() (error, error, int)              { return api.GenericCreate(pr) }
-func (pr *TOProfile) Delete() (error, error, int)              { return api.GenericDelete(pr) }
+func (pr *TOProfile) checkIfProfileCanBeAlteredByCurrentUser() (error, error, int) {
+	var cdnName string
+	if pr.CDNName != nil {
+		cdnName = *pr.CDNName
+	} else {
+		if pr.CDNID != nil {
+			cdn, ok, err := dbhelpers.GetCDNNameFromID(pr.ReqInfo.Tx.Tx, int64(*pr.CDNID))
+			if err != nil {
+				return nil, err, http.StatusInternalServerError
+			} else if !ok {
+				return nil, nil, http.StatusNotFound
+			}
+			cdnName = string(cdn)
+		} else {
+			return errors.New("no cdn found for this profile"), nil, http.StatusBadRequest
+		}
+	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(pr.ReqInfo.Tx.Tx, cdnName, pr.ReqInfo.User.UserName)
+	if userErr != nil || sysErr != nil {
+		return userErr, sysErr, statusCode
+	}
+	return nil, nil, http.StatusOK
+}
+
+func (pr *TOProfile) Update(h http.Header) (error, error, int) {
+	if pr.CDNName != nil || pr.CDNID != nil {
+		userErr, sysErr, statusCode := pr.checkIfProfileCanBeAlteredByCurrentUser()
+		if userErr != nil || sysErr != nil {
+			return userErr, sysErr, statusCode
+		}
+	}
+	return api.GenericUpdate(h, pr)
+}
+
+func (pr *TOProfile) Create() (error, error, int) {
+	if pr.CDNName != nil || pr.CDNID != nil {
+		userErr, sysErr, statusCode := pr.checkIfProfileCanBeAlteredByCurrentUser()
+		if userErr != nil || sysErr != nil {
+			return userErr, sysErr, statusCode
+		}
+	}
+	return api.GenericCreate(pr)
+}
+
+func (pr *TOProfile) Delete() (error, error, int) {
+	if pr.CDNName == nil && pr.CDNID == nil {
+		cdnName, err := dbhelpers.GetCDNNameFromProfileID(pr.APIInfo().Tx.Tx, *pr.ID)
+		if err != nil {
+			return nil, err, http.StatusInternalServerError
+		}
+		pr.CDNName = util.StrPtr(string(cdnName))
+	}
+	if pr.CDNName != nil || pr.CDNID != nil {
+		userErr, sysErr, statusCode := pr.checkIfProfileCanBeAlteredByCurrentUser()
+		if userErr != nil || sysErr != nil {
+			return userErr, sysErr, statusCode
+		}
+	}
+	return api.GenericDelete(pr)
+}
 
 func updateQuery() string {
 	query := `UPDATE
diff --git a/traffic_ops/traffic_ops_golang/profileparameter/postparameterprofile.go b/traffic_ops/traffic_ops_golang/profileparameter/postparameterprofile.go
index f2accb7..387ce4e 100644
--- a/traffic_ops/traffic_ops_golang/profileparameter/postparameterprofile.go
+++ b/traffic_ops/traffic_ops_golang/profileparameter/postparameterprofile.go
@@ -28,6 +28,7 @@ import (
 
 	"github.com/apache/trafficcontrol/lib/go-tc"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
 
 	"github.com/lib/pq"
 )
@@ -45,6 +46,18 @@ func PostParamProfile(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("parse error: "+err.Error()), nil)
 		return
 	}
+	if paramProfile.ProfileIDs != nil {
+		cdnNames, err := dbhelpers.GetCDNNamesFromProfileIDs(inf.Tx.Tx, *paramProfile.ProfileIDs)
+		if err != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
+			return
+		}
+		userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDNs(inf.Tx.Tx, cdnNames, inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+			return
+		}
+	}
 	if err := insertParameterProfile(paramProfile, inf.Tx.Tx); err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("posting parameter profile: "+err.Error()))
 		return
diff --git a/traffic_ops/traffic_ops_golang/profileparameter/postprofileparameter.go b/traffic_ops/traffic_ops_golang/profileparameter/postprofileparameter.go
index 75d0120..9b25d30 100644
--- a/traffic_ops/traffic_ops_golang/profileparameter/postprofileparameter.go
+++ b/traffic_ops/traffic_ops_golang/profileparameter/postprofileparameter.go
@@ -58,6 +58,16 @@ func PostProfileParam(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, errors.New("profile not found"), nil)
 		return
 	}
+	cdnName, err := dbhelpers.GetCDNNameFromProfileID(inf.Tx.Tx, int(*profileParam.ProfileID))
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
+		return
+	}
+	userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdnName), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
 	api.CreateChangeLogRawTx(api.ApiChange, "PROFILE: "+profileName+", ID: "+strconv.FormatInt(*profileParam.ProfileID, 10)+", ACTION: Assigned "+strconv.Itoa(len(*profileParam.ParamIDs))+" parameters to profile", inf.User, inf.Tx.Tx)
 	api.WriteRespAlertObj(w, r, tc.SuccessLevel, fmt.Sprintf("%d parameters were assigned to the %s profile", len(*profileParam.ParamIDs), profileName), profileParam)
 }
diff --git a/traffic_ops/traffic_ops_golang/profileparameter/postprofileparametersbyid.go b/traffic_ops/traffic_ops_golang/profileparameter/postprofileparametersbyid.go
index a7dc661..3abce51 100644
--- a/traffic_ops/traffic_ops_golang/profileparameter/postprofileparametersbyid.go
+++ b/traffic_ops/traffic_ops_golang/profileparameter/postprofileparametersbyid.go
@@ -54,6 +54,16 @@ func PostProfileParamsByID(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	cdnName, err := dbhelpers.GetCDNNameFromProfileID(inf.Tx.Tx, profileID)
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
+		return
+	}
+	userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdnName), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
 	insertedObjs, err := insertParametersForProfile(profileName, profParams, inf.Tx.Tx)
 	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("posting profile parameters by name: "+err.Error()))
diff --git a/traffic_ops/traffic_ops_golang/profileparameter/postprofileparametersbyname.go b/traffic_ops/traffic_ops_golang/profileparameter/postprofileparametersbyname.go
index 331da56..ec0c4df 100644
--- a/traffic_ops/traffic_ops_golang/profileparameter/postprofileparametersbyname.go
+++ b/traffic_ops/traffic_ops_golang/profileparameter/postprofileparametersbyname.go
@@ -53,6 +53,16 @@ func PostProfileParamsByName(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, errors.New("no profile with that name exists"), nil)
 		return
 	}
+	cdnName, err := dbhelpers.GetCDNNameFromProfileID(inf.Tx.Tx, profileID)
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
+		return
+	}
+	userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdnName), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
 	insertedObjs, err := insertParametersForProfile(profileName, profParams, inf.Tx.Tx)
 	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("posting profile parameters by name: "+err.Error()))
diff --git a/traffic_ops/traffic_ops_golang/profileparameter/profile_parameters.go b/traffic_ops/traffic_ops_golang/profileparameter/profile_parameters.go
index c51f9d1..0065660 100644
--- a/traffic_ops/traffic_ops_golang/profileparameter/profile_parameters.go
+++ b/traffic_ops/traffic_ops_golang/profileparameter/profile_parameters.go
@@ -117,6 +117,18 @@ func (pp *TOProfileParameter) Validate() error {
 //The insert sql returns the profile and lastUpdated values of the newly inserted profileparameter and have
 //to be added to the struct
 func (pp *TOProfileParameter) Create() (error, error, int) {
+	if pp.ProfileID != nil {
+		cdnName, err := dbhelpers.GetCDNNameFromProfileID(pp.ReqInfo.Tx.Tx, *pp.ProfileID)
+		if err != nil {
+			return nil, err, http.StatusInternalServerError
+		}
+		userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(pp.ReqInfo.Tx.Tx, string(cdnName), pp.ReqInfo.User.UserName)
+		if userErr != nil || sysErr != nil {
+			return userErr, sysErr, errCode
+		}
+	} else {
+		return errors.New("no profile ID in request"), nil, http.StatusBadRequest
+	}
 	resultRows, err := pp.APIInfo().Tx.NamedQuery(insertQuery(), pp)
 	if err != nil {
 		return api.ParseDBError(err)
@@ -159,7 +171,21 @@ func (pp *TOProfileParameter) Read(h http.Header, useIMS bool) ([]interface{}, e
 	api.DefaultSort(pp.APIInfo(), "parameter")
 	return api.GenericRead(h, pp, useIMS)
 }
-func (pp *TOProfileParameter) Delete() (error, error, int) { return api.GenericDelete(pp) }
+func (pp *TOProfileParameter) Delete() (error, error, int) {
+	if pp.ProfileID != nil {
+		cdnName, err := dbhelpers.GetCDNNameFromProfileID(pp.ReqInfo.Tx.Tx, *pp.ProfileID)
+		if err != nil {
+			return nil, err, http.StatusInternalServerError
+		}
+		userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(pp.ReqInfo.Tx.Tx, string(cdnName), pp.ReqInfo.User.UserName)
+		if userErr != nil || sysErr != nil {
+			return userErr, sysErr, errCode
+		}
+	} else {
+		return errors.New("no profile ID in request"), nil, http.StatusBadRequest
+	}
+	return api.GenericDelete(pp)
+}
 func (v *TOProfileParameter) SelectMaxLastUpdatedQuery(where, orderBy, pagination, tableName string) string {
 	return `SELECT max(t) from (
 		SELECT max(pp.last_updated) as t FROM profile_parameter pp
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go b/traffic_ops/traffic_ops_golang/routing/routes.go
index 7b5326b..0f3300c 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -273,7 +273,6 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) {
 
 		//Profile: CRUD
 		{api.Version{Major: 4, Minor: 0}, http.MethodGet, `profiles/?$`, api.ReadHandler(&profile.TOProfile{}), auth.PrivLevelReadOnly, Authenticated, nil, 4687585893},
-
 		{api.Version{Major: 4, Minor: 0}, http.MethodPut, `profiles/{id}$`, api.UpdateHandler(&profile.TOProfile{}), auth.PrivLevelOperations, Authenticated, nil, 484391723},
 		{api.Version{Major: 4, Minor: 0}, http.MethodPost, `profiles/?$`, api.CreateHandler(&profile.TOProfile{}), auth.PrivLevelOperations, Authenticated, nil, 45402115563},
 		{api.Version{Major: 4, Minor: 0}, http.MethodDelete, `profiles/{id}$`, api.DeleteHandler(&profile.TOProfile{}), auth.PrivLevelOperations, Authenticated, nil, 42055944653},
diff --git a/traffic_ops/traffic_ops_golang/server/put_status.go b/traffic_ops/traffic_ops_golang/server/put_status.go
index b69fb7e..7d0176e 100644
--- a/traffic_ops/traffic_ops_golang/server/put_status.go
+++ b/traffic_ops/traffic_ops_golang/server/put_status.go
@@ -91,6 +91,9 @@ func UpdateStatusHandler(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserHasCdnLock(inf.Tx.Tx, string(cdnName), inf.User.UserName)
+	if statusCode == http.StatusForbidden {
+		userErr = fmt.Errorf("this action will result in server updates being queued and %v", userErr)
+	}
 	if userErr != nil || sysErr != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
 		return
diff --git a/traffic_ops/traffic_ops_golang/server/servers.go b/traffic_ops/traffic_ops_golang/server/servers.go
index b869e5e..110c730 100644
--- a/traffic_ops/traffic_ops_golang/server/servers.go
+++ b/traffic_ops/traffic_ops_golang/server/servers.go
@@ -1650,6 +1650,20 @@ func Update(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	if server.CDNName != nil {
+		userErr, sysErr, statusCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, *server.CDNName, inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+			return
+		}
+	} else if server.CDNID != nil {
+		userErr, sysErr, statusCode = dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(*server.CDNID), inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+			return
+		}
+	}
+
 	rows, err := inf.Tx.NamedQuery(updateQuery, server)
 	if err != nil {
 		userErr, sysErr, errCode = api.ParseDBError(err)
@@ -1738,6 +1752,20 @@ func createV1(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	if server.CDNName != nil {
+		userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, *server.CDNName, inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+			return
+		}
+	} else if server.CDNID != nil {
+		userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(*server.CDNID), inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+			return
+		}
+	}
+
 	resultRows, err := inf.Tx.NamedQuery(insertQuery, server)
 	if err != nil {
 		userErr, sysErr, errCode := api.ParseDBError(err)
@@ -1808,7 +1836,19 @@ func createV2(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
 		return
 	}
-
+	if server.CDNName != nil {
+		userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, *server.CDNName, inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+			return
+		}
+	} else if server.CDNID != nil {
+		userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(*server.CDNID), inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+			return
+		}
+	}
 	resultRows, err := inf.Tx.NamedQuery(insertQuery, server)
 	if err != nil {
 		userErr, sysErr, errCode := api.ParseDBError(err)
@@ -1882,6 +1922,20 @@ func createV3(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) {
 	currentTime := time.Now()
 	server.StatusLastUpdated = &currentTime
 
+	if server.CDNName != nil {
+		userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, *server.CDNName, inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+			return
+		}
+	} else if server.CDNID != nil {
+		userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(*server.CDNID), inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+			return
+		}
+	}
+
 	resultRows, err := inf.Tx.NamedQuery(insertQueryV3, server)
 	if err != nil {
 		userErr, sysErr, errCode := api.ParseDBError(err)
@@ -1956,6 +2010,20 @@ func createV4(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) {
 	currentTime := time.Now()
 	server.StatusLastUpdated = &currentTime
 
+	if server.CDNName != nil {
+		userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, *server.CDNName, inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+			return
+		}
+	} else if server.CDNID != nil {
+		userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(*server.CDNID), inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+			return
+		}
+	}
+
 	resultRows, err := inf.Tx.NamedQuery(insertQueryV4, server)
 	if err != nil {
 		userErr, sysErr, errCode := api.ParseDBError(err)
@@ -2111,6 +2179,19 @@ func Delete(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	server := servers[0]
+	if server.CDNName != nil {
+		userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, *server.CDNName, inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+			return
+		}
+	} else if server.CDNID != nil {
+		userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(*server.CDNID), inf.User.UserName)
+		if userErr != nil || sysErr != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+			return
+		}
+	}
 	cacheGroupIds := []int{*server.CachegroupID}
 	serverIds := []int{*server.ID}
 	hasDSOnCDN, err := dbhelpers.CachegroupHasTopologyBasedDeliveryServicesOnCDN(inf.Tx.Tx, *server.CachegroupID, *server.CDNID)
diff --git a/traffic_ops/traffic_ops_golang/server/servers_assignment.go b/traffic_ops/traffic_ops_golang/server/servers_assignment.go
index f2305ae..fcf855f 100644
--- a/traffic_ops/traffic_ops_golang/server/servers_assignment.go
+++ b/traffic_ops/traffic_ops_golang/server/servers_assignment.go
@@ -149,7 +149,11 @@ func AssignDeliveryServicesToServerHandler(w http.ResponseWriter, r *http.Reques
 		api.HandleErr(w, r, tx, errCode, nil, sysErr)
 		return
 	}
-
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(serverCDN), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
 	if len(dsList) > 0 {
 		if errCode, userErr, sysErr = checkTenancyAndCDN(tx, string(serverCDN), server, serverInfo, dsList, inf.User); userErr != nil || sysErr != nil {
 			api.HandleErr(w, r, tx, errCode, userErr, sysErr)
diff --git a/traffic_ops/traffic_ops_golang/server/servers_server_capability.go b/traffic_ops/traffic_ops_golang/server/servers_server_capability.go
index 31cffb4..e115922 100644
--- a/traffic_ops/traffic_ops_golang/server/servers_server_capability.go
+++ b/traffic_ops/traffic_ops_golang/server/servers_server_capability.go
@@ -153,6 +153,16 @@ func (ssc *TOServerServerCapability) Delete() (error, error, int) {
 		return userErr, sysErr, status
 	}
 
+	if ssc.ServerID != nil {
+		cdnName, err := dbhelpers.GetCDNNameFromServerID(ssc.APIInfo().Tx.Tx, int64(*ssc.ServerID))
+		if err != nil {
+			return nil, err, http.StatusInternalServerError
+		}
+		userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(ssc.APIInfo().Tx.Tx, string(cdnName), ssc.APIInfo().User.UserName)
+		if userErr != nil || sysErr != nil {
+			return userErr, sysErr, errCode
+		}
+	}
 	return api.GenericDelete(ssc)
 }
 
@@ -294,6 +304,15 @@ func (ssc *TOServerServerCapability) Create() (error, error, int) {
 		return fmt.Errorf("server %v has an incorrect server type. Server capabilities can only be assigned to EDGE or MID servers", *ssc.ServerID), nil, http.StatusBadRequest
 	}
 
+	cdnName, err := dbhelpers.GetCDNNameFromServerID(tx.Tx, int64(*ssc.ServerID))
+	if err != nil {
+		return nil, err, http.StatusInternalServerError
+	}
+	userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(tx.Tx, string(cdnName), ssc.APIInfo().User.UserName)
+	if userErr != nil || sysErr != nil {
+		return userErr, sysErr, errCode
+	}
+
 	resultRows, err := tx.NamedQuery(scInsertQuery(), ssc)
 	if err != nil {
 		return api.ParseDBError(err)
diff --git a/traffic_ops/traffic_ops_golang/staticdnsentry/staticdnsentry.go b/traffic_ops/traffic_ops_golang/staticdnsentry/staticdnsentry.go
index 2c2fc98..c20088d 100644
--- a/traffic_ops/traffic_ops_golang/staticdnsentry/staticdnsentry.go
+++ b/traffic_ops/traffic_ops_golang/staticdnsentry/staticdnsentry.go
@@ -141,11 +141,51 @@ func (en *TOStaticDNSEntry) Read(h http.Header, useIMS bool) ([]interface{}, err
 	api.DefaultSort(en.APIInfo(), "host")
 	return api.GenericRead(h, en, useIMS)
 }
-func (en *TOStaticDNSEntry) Create() (error, error, int) { return api.GenericCreate(en) }
+func (en *TOStaticDNSEntry) Create() (error, error, int) {
+	var cdnName tc.CDNName
+	var err error
+	if en.DeliveryServiceID != nil {
+		_, cdnName, _, err = dbhelpers.GetDSNameAndCDNFromID(en.ReqInfo.Tx.Tx, *en.DeliveryServiceID)
+		if err != nil {
+			return nil, err, http.StatusInternalServerError
+		}
+		userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(en.ReqInfo.Tx.Tx, string(cdnName), en.ReqInfo.User.UserName)
+		if userErr != nil || sysErr != nil {
+			return userErr, sysErr, errCode
+		}
+	}
+	return api.GenericCreate(en)
+}
 func (en *TOStaticDNSEntry) Update(h http.Header) (error, error, int) {
+	var cdnName tc.CDNName
+	var err error
+	if en.DeliveryServiceID != nil {
+		_, cdnName, _, err = dbhelpers.GetDSNameAndCDNFromID(en.ReqInfo.Tx.Tx, *en.DeliveryServiceID)
+		if err != nil {
+			return nil, err, http.StatusInternalServerError
+		}
+		userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(en.ReqInfo.Tx.Tx, string(cdnName), en.ReqInfo.User.UserName)
+		if userErr != nil || sysErr != nil {
+			return userErr, sysErr, errCode
+		}
+	}
 	return api.GenericUpdate(h, en)
 }
-func (en *TOStaticDNSEntry) Delete() (error, error, int) { return api.GenericDelete(en) }
+func (en *TOStaticDNSEntry) Delete() (error, error, int) {
+	var cdnName tc.CDNName
+	var err error
+	if en.DeliveryServiceID != nil {
+		_, cdnName, _, err = dbhelpers.GetDSNameAndCDNFromID(en.ReqInfo.Tx.Tx, *en.DeliveryServiceID)
+		if err != nil {
+			return nil, err, http.StatusInternalServerError
+		}
+		userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(en.ReqInfo.Tx.Tx, string(cdnName), en.ReqInfo.User.UserName)
+		if userErr != nil || sysErr != nil {
+			return userErr, sysErr, errCode
+		}
+	}
+	return api.GenericDelete(en)
+}
 func (v *TOStaticDNSEntry) SelectMaxLastUpdatedQuery(where, orderBy, pagination, tableName string) string {
 	return `SELECT max(t) from (
 		SELECT max(sde.last_updated) as t FROM staticdnsentry as sde
diff --git a/traffic_ops/traffic_ops_golang/steeringtargets/steeringtargets.go b/traffic_ops/traffic_ops_golang/steeringtargets/steeringtargets.go
index 3880290..091d6cb 100644
--- a/traffic_ops/traffic_ops_golang/steeringtargets/steeringtargets.go
+++ b/traffic_ops/traffic_ops_golang/steeringtargets/steeringtargets.go
@@ -204,6 +204,14 @@ func (st *TOSteeringTargetV11) Create() (error, error, int) {
 		return userErr, sysErr, errCode
 	}
 
+	_, cdn, _, err := dbhelpers.GetDSNameAndCDNFromID(st.ReqInfo.Tx.Tx, int(dsID))
+	if err != nil {
+		return nil, errors.New("createSteeringTarget: getting CDN from DS ID " + err.Error()), http.StatusInternalServerError
+	}
+	if userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(st.ReqInfo.Tx.Tx, string(cdn), st.ReqInfo.User.UserName); userErr != nil || sysErr != nil {
+		return userErr, sysErr, errCode
+	}
+
 	rows, err := st.ReqInfo.Tx.NamedQuery(insertQuery(), st)
 	if err != nil {
 		return api.ParseDBError(err)
@@ -230,6 +238,15 @@ func (st *TOSteeringTargetV11) Update(h http.Header) (error, error, int) {
 	if err != nil {
 		return errors.New("delivery service ID must be an integer"), nil, http.StatusBadRequest
 	}
+
+	_, cdn, _, err := dbhelpers.GetDSNameAndCDNFromID(st.ReqInfo.Tx.Tx, dsIDInt)
+	if err != nil {
+		return nil, errors.New("updateSteeringTarget: getting CDN from DS ID " + err.Error()), http.StatusInternalServerError
+	}
+	if userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(st.ReqInfo.Tx.Tx, string(cdn), st.ReqInfo.User.UserName); userErr != nil || sysErr != nil {
+		return userErr, sysErr, errCode
+	}
+
 	dsID := uint64(dsIDInt)
 	// TODO determine if the CRUDer automatically does this
 	st.DeliveryServiceID = &dsID
@@ -303,6 +320,13 @@ func (st *TOSteeringTargetV11) Delete() (error, error, int) {
 		return userErr, sysErr, errCode
 	}
 
+	_, cdn, _, err := dbhelpers.GetDSNameAndCDNFromID(st.ReqInfo.Tx.Tx, int(*st.DeliveryServiceID))
+	if err != nil {
+		return nil, errors.New("deleteSteeringTarget: getting CDN from DS ID " + err.Error()), http.StatusInternalServerError
+	}
+	if userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(st.ReqInfo.Tx.Tx, string(cdn), st.ReqInfo.User.UserName); userErr != nil || sysErr != nil {
+		return userErr, sysErr, errCode
+	}
 	result, err := st.ReqInfo.Tx.NamedExec(deleteQuery(), st)
 	if err != nil {
 		return nil, errors.New("steering target delete exec: " + err.Error()), http.StatusInternalServerError
diff --git a/traffic_ops/traffic_ops_golang/topology/topologies.go b/traffic_ops/traffic_ops_golang/topology/topologies.go
index c2fc8ac..f9fb81d 100644
--- a/traffic_ops/traffic_ops_golang/topology/topologies.go
+++ b/traffic_ops/traffic_ops_golang/topology/topologies.go
@@ -410,6 +410,10 @@ func (topology *TOTopology) GetAuditName() string {
 // Create is a requirement of the api.Creator interface.
 func (topology *TOTopology) Create() (error, error, int) {
 	tx := topology.APIInfo().Tx.Tx
+	userErr, sysErr, statusCode := topology.checkIfTopologyCanBeAlteredByCurrentUser()
+	if userErr != nil || sysErr != nil {
+		return userErr, sysErr, statusCode
+	}
 	err := tx.QueryRow(insertQuery(), topology.Name, topology.Description).Scan(&topology.Name, &topology.Description, &topology.LastUpdated)
 	if err != nil {
 		return api.ParseDBError(err)
@@ -606,7 +610,10 @@ func (topology *TOTopology) Update(h http.Header) (error, error, int) {
 	if userErr != nil || sysErr != nil {
 		return userErr, sysErr, errCode
 	}
-
+	userErr, sysErr, statusCode := topology.checkIfTopologyCanBeAlteredByCurrentUser()
+	if userErr != nil || sysErr != nil {
+		return userErr, sysErr, statusCode
+	}
 	oldTopology := TOTopology{APIInfoImpl: topology.APIInfoImpl, Topology: topologies[0].(tc.Topology)}
 
 	if err := oldTopology.removeParents(); err != nil {
@@ -648,6 +655,10 @@ func (topology *TOTopology) Update(h http.Header) (error, error, int) {
 // Delete is unused and simply satisfies the Deleter interface
 // (although TOTOpology is used as an OptionsDeleter)
 func (topology *TOTopology) Delete() (error, error, int) {
+	userErr, sysErr, statusCode := topology.checkIfTopologyCanBeAlteredByCurrentUser()
+	if userErr != nil || sysErr != nil {
+		return userErr, sysErr, statusCode
+	}
 	return nil, nil, 0
 }
 
@@ -661,6 +672,10 @@ func (topology *TOTopology) OptionsDelete() (error, error, int) {
 		return fmt.Errorf("cannot find exactly 1 topology with the query string provided"), nil, http.StatusBadRequest
 	}
 	topology.Topology = topologies[0].(tc.Topology)
+	userErr, sysErr, statusCode := topology.checkIfTopologyCanBeAlteredByCurrentUser()
+	if userErr != nil || sysErr != nil {
+		return userErr, sysErr, statusCode
+	}
 	return api.GenericOptionsDelete(topology)
 }
 
@@ -806,3 +821,20 @@ func selectMaxLastUpdatedQuery(where string) string {
 		` UNION ALL
 	select max(last_updated) as ti from last_deleted l where l.table_name='topology') as res`
 }
+
+func (topology *TOTopology) checkIfTopologyCanBeAlteredByCurrentUser() (error, error, int) {
+	cachegroups := topology.getCachegroupNames()
+	serverIDs, err := dbhelpers.GetServerIDsFromCachegroupNames(topology.ReqInfo.Tx.Tx, cachegroups)
+	if err != nil {
+		return nil, err, http.StatusInternalServerError
+	}
+	cdns, err := dbhelpers.GetCDNNamesFromServerIds(topology.ReqInfo.Tx.Tx, serverIDs)
+	if err != nil {
+		return nil, err, http.StatusInternalServerError
+	}
+	userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDNs(topology.ReqInfo.Tx.Tx, cdns, topology.ReqInfo.User.UserName)
+	if userErr != nil || sysErr != nil {
+		return userErr, sysErr, statusCode
+	}
+	return nil, nil, http.StatusOK
+}
diff --git a/traffic_ops/traffic_ops_golang/urisigning/urisigning.go b/traffic_ops/traffic_ops_golang/urisigning/urisigning.go
index 9984dba..bbcd591 100644
--- a/traffic_ops/traffic_ops_golang/urisigning/urisigning.go
+++ b/traffic_ops/traffic_ops_golang/urisigning/urisigning.go
@@ -32,6 +32,7 @@ import (
 	"github.com/apache/trafficcontrol/lib/go-rfc"
 	"github.com/apache/trafficcontrol/lib/go-tc"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
 )
 
@@ -79,7 +80,6 @@ func RemoveDeliveryServiceURIKeysHandler(w http.ResponseWriter, r *http.Request)
 		return
 	}
 	defer inf.Close()
-
 	if !inf.Config.TrafficVaultEnabled {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusServiceUnavailable, errors.New("rhe Traffic Vault service is unavailable"), errors.New("getting Traffic Vault SSL keys by host name: Traffic Vault is not configured"))
 		return
@@ -99,6 +99,20 @@ func RemoveDeliveryServiceURIKeysHandler(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
+	_, cdnName, ok, err := dbhelpers.GetDSIDAndCDNFromName(inf.Tx.Tx, xmlID)
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
+		return
+	}
+	if !ok {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, nil, nil)
+		return
+	}
+	userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdnName), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
 	_, found, err := inf.Vault.GetURISigningKeys(xmlID, inf.Tx.Tx, r.Context())
 	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("removing URI signing keys: "+err.Error()))
@@ -126,7 +140,6 @@ func SaveDeliveryServiceURIKeysHandler(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	defer inf.Close()
-
 	if !inf.Config.TrafficVaultEnabled {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusServiceUnavailable, errors.New("the Traffic Vault service is unavailable"), errors.New("getting Traffic Vault SSL keys by host name: Traffic Vault is not configured"))
 		return
@@ -146,6 +159,20 @@ func SaveDeliveryServiceURIKeysHandler(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	_, cdnName, ok, err := dbhelpers.GetDSIDAndCDNFromName(inf.Tx.Tx, xmlID)
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
+		return
+	}
+	if !ok {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, nil, nil)
+		return
+	}
+	userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdnName), inf.User.UserName)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
 	data, err := ioutil.ReadAll(r.Body)
 	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, errors.New("failed to read body"), errors.New("failed to read body: "+err.Error()))