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

mattjackson 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 2f5268c  Prevent changing DS fields that would invalidate its SSL key 
(#5083)
2f5268c is described below

commit 2f5268c150c6088ffc7e8a21fa2c4e5b25b64377
Author: Steve Hamrick <[email protected]>
AuthorDate: Thu Oct 8 15:38:39 2020 -0600

    Prevent changing DS fields that would invalidate its SSL key (#5083)
    
    * Dont allow changing a DS from TO
    
    * Fix import
    
    * Build fix
    
    * Add messaging to TP
    
    * Update CHANGELOG.md
    
    * Update docs
    
    * Why am I like this, remove debug in CiaB
    
    * Make tests more robust, and fix test failure
    
    * Dont need response
    
    * Better error message
    
    * Didnt mean to commit this
    
    * Didnt mean to commit this
    
    * Hopefully fix test
---
 CHANGELOG.md                                       |   2 +-
 docs/source/api/v3/deliveryservices_id.rst         |   6 ++
 .../testing/api/v3/deliveryservices_test.go        | 103 +++++++++++++--------
 .../deliveryservice/deliveryservices.go            |  46 +++------
 .../traffic_ops_golang/user/deliveryservices.go    |   7 +-
 traffic_ops/v3-client/deliveryservice.go           |   2 +-
 .../modules/dialog/text/dialog.text.tpl.html       |   2 +-
 .../edit/FormEditDeliveryServiceController.js      |  31 ++++++-
 8 files changed, 122 insertions(+), 77 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index b76daf0..b6133df 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -68,7 +68,7 @@ The format is based on [Keep a 
Changelog](http://keepachangelog.com/en/1.0.0/).
 - Fixed #2156 - Renaming a host in TC, does not impact xmpp_id and thereby 
hashid [Related github 
issue](https://github.com/apache/trafficcontrol/issues/2156)
 - Fixed #5038 - Adds UI warning when server interface IP CIDR is too large 
[Related github issue](https://github.com/apache/trafficcontrol/issues/5038)
 - Fixed #3661 - Anonymous Proxy ipv4 whitelist does not work
-- Fixed #1897 - Delivery Service SSL keys now correctly update their CDN
+- Fixed #1847 - Delivery Service with SSL keys are no longer allowed to be 
updated when the fields changed are relevant to the SSL Keys validity.
 - Fixed the `GET /api/x/jobs` and `GET /api/x/jobs/:id` Traffic Ops API routes 
to allow falling back to Perl via the routing blacklist
 - Fixed ORT config generation not using the coalesce_number_v6 Parameter.
 - Fixed POST deliveryservices/request (designed to simple send an email) 
regression which erroneously required deep caching type and routing name. 
[Related github issue](https://github.com/apache/trafficcontrol/issues/4735)
diff --git a/docs/source/api/v3/deliveryservices_id.rst 
b/docs/source/api/v3/deliveryservices_id.rst
index dec00a2..7a6dd90 100644
--- a/docs/source/api/v3/deliveryservices_id.rst
+++ b/docs/source/api/v3/deliveryservices_id.rst
@@ -38,6 +38,9 @@ Request Structure
 
 :ccrDnsTtl:                 The :ref:`ds-dns-ttl` - named "ccrDnsTtl" for 
legacy reasons
 :cdnId:                     The integral, unique identifier of the 
:ref:`ds-cdn` to which the :term:`Delivery Service` belongs
+
+               .. note:: If the Delivery Service has SSL Keys, then cdnId is 
not allowed to change as that would invalidate the SSL Key
+
 :checkPath:                 A :ref:`ds-check-path`
 :consistentHashRegex:       A :ref:`ds-consistent-hashing-regex`
 :consistentHashQueryParams: An array of :ref:`ds-consistent-hashing-qparams`
@@ -84,6 +87,9 @@ Request Structure
 :regionalGeoBlocking:       A boolean defining the :ref:`ds-regionalgeo` 
setting on this :term:`Delivery Service`
 :remapText:                 :ref:`ds-raw-remap`
 :routingName:               The :ref:`ds-routing-name` of this :term:`Delivery 
Service`
+
+               .. note:: If the Delivery Service has SSL Keys, then 
routingName is not allowed to change as that would invalidate the SSL Key
+
 :signed:                    ``true`` if  and only if ``signingAlgorithm`` is 
not ``null``, ``false`` otherwise
 :signingAlgorithm:          Either a :ref:`ds-signing-algorithm` or ``null`` 
to indicate URL/URI signing is not implemented on this :term:`Delivery Service`
 :rangeSliceBlockSize:      An integer that defines the byte block size for the 
ATS Slice Plugin. It can only and must be set if ``rangeRequestHandling`` is 
set to 3. It can only be between (inclusive) 262144 (256KB) - 33554432 (32MB).
diff --git a/traffic_ops/testing/api/v3/deliveryservices_test.go 
b/traffic_ops/testing/api/v3/deliveryservices_test.go
index 99e953e..29c72e8 100644
--- a/traffic_ops/testing/api/v3/deliveryservices_test.go
+++ b/traffic_ops/testing/api/v3/deliveryservices_test.go
@@ -40,7 +40,7 @@ func TestDeliveryServices(t *testing.T) {
                header.Set(rfc.IfModifiedSince, 
currentTime.Format(time.RFC1123))
 
                if includeSystemTests {
-                       DeliveryServiceCDNKeyTransferTest(t)
+                       SSLDeliveryServiceCDNUpdateTest(t)
                }
                GetTestDeliveryServicesIMS(t)
                GetAccessibleToTest(t)
@@ -68,6 +68,12 @@ func createBlankCDN(cdnName string, t *testing.T) tc.CDN {
        if err != nil {
                t.Fatal("unable to create cdn: " + err.Error())
        }
+
+       originalKeys, _, err := TOSession.GetCDNSSLKeysWithHdr(cdnName, nil)
+       if err != nil {
+               t.Fatalf("unable to get keys on cdn %v: %v", cdnName, err)
+       }
+
        cdns, _, err := TOSession.GetCDNByNameWithHdr(cdnName, nil)
        if err != nil {
                t.Fatalf("unable to get cdn %v: %v", cdnName, err)
@@ -79,13 +85,32 @@ func createBlankCDN(cdnName string, t *testing.T) tc.CDN {
        if err != nil {
                t.Fatalf("unable to get keys on cdn %v: %v", cdnName, err)
        }
-       if len(keys) != 0 {
-               t.Fatal("expected no ssl keys on cdn " + cdnName)
+       if len(keys) != len(originalKeys) {
+               t.Fatalf("expected %v ssl keys on cdn %v, got %v", 
len(originalKeys), cdnName, len(keys))
        }
        return cdns[0]
 }
 
-func DeliveryServiceCDNKeyTransferTest(t *testing.T) {
+func cleanUp(t *testing.T, ds tc.DeliveryServiceNullableV30, oldCDNID int, 
newCDNID int) {
+       _, _, err := TOSession.DeleteDeliveryServiceSSLKeysByID(*ds.XMLID)
+       if err != nil {
+               t.Error(err)
+       }
+       _, err = TOSession.DeleteDeliveryService(strconv.Itoa(*ds.ID))
+       if err != nil {
+               t.Error(err)
+       }
+       _, _, err = TOSession.DeleteCDNByID(oldCDNID)
+       if err != nil {
+               t.Error(err)
+       }
+       _, _, err = TOSession.DeleteCDNByID(newCDNID)
+       if err != nil {
+               t.Error(err)
+       }
+}
+
+func SSLDeliveryServiceCDNUpdateTest(t *testing.T) {
        cdnNameOld := "sslkeytransfer"
        oldCdn := createBlankCDN(cdnNameOld, t)
        cdnNameNew := "sslkeytransfer1"
@@ -108,7 +133,8 @@ func DeliveryServiceCDNKeyTransferTest(t *testing.T) {
                                                        Active:               
util.BoolPtr(true),
                                                        CDNID:                
util.IntPtr(oldCdn.ID),
                                                        DSCP:                 
util.IntPtr(0),
-                                                       DisplayName:          
util.StrPtr("asdf"),
+                                                       DisplayName:          
util.StrPtr("displayName"),
+                                                       RoutingName:          
util.StrPtr("routingName"),
                                                        GeoLimit:             
util.IntPtr(0),
                                                        GeoProvider:          
util.IntPtr(0),
                                                        IPV6RoutingEnabled:   
util.BoolPtr(false),
@@ -124,7 +150,7 @@ func DeliveryServiceCDNKeyTransferTest(t *testing.T) {
                                                        RegionalGeoBlocking:  
util.BoolPtr(false),
                                                        TenantID:             
util.IntPtr(1),
                                                        TypeID:               
util.IntPtr(types[0].ID),
-                                                       XMLID:                
util.StrPtr("asdf"),
+                                                       XMLID:                
util.StrPtr("dsID"),
                                                },
                                        },
                                },
@@ -137,6 +163,8 @@ func DeliveryServiceCDNKeyTransferTest(t *testing.T) {
        }
        ds.CDNName = &oldCdn.Name
 
+       defer cleanUp(t, ds, oldCdn.ID, newCdn.ID)
+
        _, _, err = TOSession.GenerateSSLKeysForDS(*ds.XMLID, *ds.CDNName, 
tc.SSLKeyRequestFields{
                BusinessUnit: util.StrPtr("BU"),
                City:         util.StrPtr("CI"),
@@ -149,30 +177,49 @@ func DeliveryServiceCDNKeyTransferTest(t *testing.T) {
                t.Fatalf("unable to generate sslkeys for cdn %v: %v", 
oldCdn.Name, err)
        }
 
-       // Check old CDN has an ssl key
-       keys, _, err := TOSession.GetCDNSSLKeysWithHdr(oldCdn.Name, nil)
+       tries := 0
+       var oldCDNKeys []tc.CDNSSLKeys
+       for tries < 5 {
+               time.Sleep(time.Second)
+               oldCDNKeys, _, err = 
TOSession.GetCDNSSLKeysWithHdr(oldCdn.Name, nil)
+               if err == nil && len(oldCDNKeys) > 0 {
+                       break
+               }
+               tries++
+       }
        if err != nil {
                t.Fatalf("unable to get cdn %v keys: %v", oldCdn.Name, err)
        }
-       if len(keys) != 1 {
-               t.Fatalf("expected %v key, got %v", 1, len(keys))
+       if len(oldCDNKeys) < 1 {
+               t.Fatal("expected at least 1 key")
        }
 
-       // Change CDN
+       newCDNKeys, _, err := TOSession.GetCDNSSLKeysWithHdr(newCdn.Name, nil)
+       if err != nil {
+               t.Fatalf("unable to get cdn %v keys: %v", newCdn.Name, err)
+       }
+
+       ds.RoutingName = util.StrPtr("anothername")
+       _, _, err = TOSession.UpdateDeliveryServiceV30(*ds.ID, ds)
+       if err == nil {
+               t.Fatal("should not be able to update delivery service (routing 
name) as it has ssl keys")
+       }
+       ds.RoutingName = util.StrPtr("routingName")
+
        ds.CDNID = &newCdn.ID
        ds.CDNName = &newCdn.Name
        _, _, err = TOSession.UpdateDeliveryServiceV30(*ds.ID, ds)
-       if err != nil {
-               t.Fatal(err)
+       if err == nil {
+               t.Fatal("should not be able to update delivery service (cdn) as 
it has ssl keys")
        }
 
-       // Check new CDN has ssl key
-       keys, _, err = TOSession.GetCDNSSLKeysWithHdr(newCdn.Name, nil)
+       // Check new CDN still has an ssl key
+       keys, _, err := TOSession.GetCDNSSLKeysWithHdr(newCdn.Name, nil)
        if err != nil {
                t.Fatalf("unable to get cdn %v keys: %v", newCdn.Name, err)
        }
-       if len(keys) != 1 {
-               t.Fatalf("expected %v key, got %v", 1, len(keys))
+       if len(keys) != len(newCDNKeys) {
+               t.Fatalf("expected %v keys, got %v", len(newCDNKeys), len(keys))
        }
 
        // Check old CDN does not have ssl key
@@ -180,26 +227,8 @@ func DeliveryServiceCDNKeyTransferTest(t *testing.T) {
        if err != nil {
                t.Fatalf("unable to get cdn %v keys: %v", oldCdn.Name, err)
        }
-       if len(keys) != 0 {
-               t.Fatalf("expected %v key, got %v", 1, len(keys))
-       }
-
-       // Clean up
-       _, _, err = TOSession.DeleteDeliveryServiceSSLKeysByID(*ds.XMLID)
-       if err != nil {
-               t.Fatal(err)
-       }
-       _, err = TOSession.DeleteDeliveryService(strconv.Itoa(*ds.ID))
-       if err != nil {
-               t.Fatal(err)
-       }
-       _, _, err = TOSession.DeleteCDNByID(oldCdn.ID)
-       if err != nil {
-               t.Fatal(err)
-       }
-       _, _, err = TOSession.DeleteCDNByID(newCdn.ID)
-       if err != nil {
-               t.Fatal(err)
+       if len(keys) != len(oldCDNKeys) {
+               t.Fatalf("expected %v key, got %v", len(oldCDNKeys), len(keys))
        }
 
 }
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go 
b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
index 46cc2a9..03dce9f 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
@@ -34,9 +34,7 @@ import (
        "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/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/riaksvc"
        "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
        
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/util/ims"
 
@@ -752,7 +750,6 @@ WHERE
 
 func updateV30(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, ds 
*tc.DeliveryServiceNullableV30) (*tc.DeliveryServiceNullableV30, int, error, 
error) {
        tx := inf.Tx.Tx
-       cfg := inf.Config
        user := inf.User
 
        if err := ds.Validate(tx); err != nil {
@@ -783,8 +780,9 @@ func updateV30(w http.ResponseWriter, r *http.Request, inf 
*api.APIInfo, ds *tc.
        // oldHostName will be used to determine if SSL Keys need updating - 
this will be empty if the DS doesn't have SSL keys, because DS types without 
SSL keys may not have regexes, and thus will fail to get a host name.
        oldHostName := ""
        oldCDNName := ""
+       oldRoutingName := ""
        if dsType.HasSSLKeys() {
-               oldHostName, oldCDNName, err = getOldDetails(*ds.ID, tx)
+               oldHostName, oldCDNName, oldRoutingName, err = 
getOldDetails(*ds.ID, tx)
                if err != nil {
                        return nil, http.StatusInternalServerError, nil, 
errors.New("getting existing delivery service hostname: " + err.Error())
                }
@@ -926,10 +924,8 @@ func updateV30(w http.ResponseWriter, r *http.Request, inf 
*api.APIInfo, ds *tc.
                }
        }
 
-       if newDSType.HasSSLKeys() && (oldHostName != newHostName || oldCDNName 
!= newCDNName) {
-               if err := updateSSLKeys(ds, newHostName, newCDNName, tx, cfg); 
err != nil {
-                       return nil, http.StatusInternalServerError, nil, 
errors.New("updating delivery service " + *ds.XMLID + ": updating SSL keys: " + 
err.Error())
-               }
+       if newDSType.HasSSLKeys() && (oldHostName != newHostName || oldCDNName 
!= newCDNName || oldRoutingName != *ds.RoutingName) {
+               return nil, http.StatusForbidden, nil, errors.New("delivery 
service has ssl keys that cannot be automatically changed")
        }
 
        if err := EnsureParams(tx, *ds.ID, *ds.XMLID, ds.EdgeHeaderRewrite, 
ds.MidHeaderRewrite, ds.RegexRemap, ds.CacheURL, ds.SigningAlgorithm, 
newDSType, ds.MaxOriginConnections); err != nil {
@@ -1093,7 +1089,7 @@ func selectMaxLastUpdatedQuery(where string) string {
        select max(last_updated) as t from last_deleted l where 
l.table_name='deliveryservice') as res`
 }
 
-func getOldDetails(id int, tx *sql.Tx) (string, string, error) {
+func getOldDetails(id int, tx *sql.Tx) (string, string, string, error) {
        q := `
 SELECT ds.xml_id, ds.protocol, type.name, ds.routing_name, cdn.domain_name, 
cdn.name
 FROM  deliveryservice as ds
@@ -1108,25 +1104,25 @@ WHERE ds.id=$1
        cdnDomain := ""
        cdnName := ""
        if err := tx.QueryRow(q, id).Scan(&xmlID, &protocol, &dsTypeStr, 
&routingName, &cdnDomain, &cdnName); err != nil {
-               return "", "", fmt.Errorf("querying delivery service %v host 
name: "+err.Error()+"\n", id)
+               return "", "", "", fmt.Errorf("querying delivery service %v 
host name: "+err.Error()+"\n", id)
        }
        dsType := tc.DSTypeFromString(dsTypeStr)
        if dsType == tc.DSTypeInvalid {
-               return "", "", errors.New("getting delivery services matchlist: 
got invalid delivery service type '" + dsTypeStr + "'")
+               return "", "", "", errors.New("getting delivery services 
matchlist: got invalid delivery service type '" + dsTypeStr + "'")
        }
        matchLists, err := GetDeliveryServicesMatchLists([]string{xmlID}, tx)
        if err != nil {
-               return "", "", errors.New("getting delivery services matchlist: 
" + err.Error())
+               return "", "", "", errors.New("getting delivery services 
matchlist: " + err.Error())
        }
        matchList, ok := matchLists[xmlID]
        if !ok {
-               return "", "", errors.New("delivery service has no match lists 
(is your delivery service missing regexes?)")
+               return "", "", "", errors.New("delivery service has no match 
lists (is your delivery service missing regexes?)")
        }
        host, err := getHostName(protocol, dsType, routingName, matchList, 
cdnDomain) // protocol defaults to 0: doesn't need to check Valid()
        if err != nil {
-               return "", "", errors.New("getting hostname: " + err.Error())
+               return "", "", "", errors.New("getting hostname: " + 
err.Error())
        }
-       return host, cdnName, nil
+       return host, cdnName, routingName, nil
 }
 
 func getTypeFromID(id int, tx *sql.Tx) (tc.DSType, error) {
@@ -1344,26 +1340,6 @@ func GetDeliveryServices(query string, queryValues 
map[string]interface{}, tx *s
        return dses, nil, nil, http.StatusOK
 }
 
-func updateSSLKeys(ds *tc.DeliveryServiceNullableV30, hostName string, cdnName 
string, tx *sql.Tx, cfg *config.Config) error {
-       if ds.XMLID == nil {
-               return errors.New("delivery services has no XMLID!")
-       }
-       key, ok, err := riaksvc.GetDeliveryServiceSSLKeysObj(*ds.XMLID, 
riaksvc.DSSSLKeyVersionLatest, tx, cfg.RiakAuthOptions, cfg.RiakPort)
-       if err != nil {
-               return errors.New("getting SSL key: " + err.Error())
-       }
-       if !ok {
-               return nil // no keys to update
-       }
-       key.DeliveryService = *ds.XMLID
-       key.Hostname = hostName
-       key.CDN = cdnName
-       if err := riaksvc.PutDeliveryServiceSSLKeysObj(key, tx, 
cfg.RiakAuthOptions, cfg.RiakPort); err != nil {
-               return errors.New("putting updated SSL key: " + err.Error())
-       }
-       return nil
-}
-
 // getHostName gets the host name used for delivery service requests. The 
dsProtocol may be nil, if the delivery service type doesn't have a protocol 
(e.g. ANY_MAP).
 func getHostName(dsProtocol *int, dsType tc.DSType, dsRoutingName string, 
dsMatchList []tc.DeliveryServiceMatch, cdnDomain string) (string, error) {
        exampleURLs := MakeExampleURLs(dsProtocol, dsType, dsRoutingName, 
dsMatchList, cdnDomain)
diff --git a/traffic_ops/traffic_ops_golang/user/deliveryservices.go 
b/traffic_ops/traffic_ops_golang/user/deliveryservices.go
index 8a6e483..530bf3e 100644
--- a/traffic_ops/traffic_ops_golang/user/deliveryservices.go
+++ b/traffic_ops/traffic_ops_golang/user/deliveryservices.go
@@ -197,7 +197,12 @@ WHERE dsu.tm_user_id = $1
        dses := []tc.DeliveryServiceNullable{}
        for rows.Next() {
                ds := tc.DeliveryServiceNullable{}
-               err := rows.Scan(&ds.Active, &ds.AnonymousBlockingEnabled, 
&ds.CacheURL, &ds.CCRDNSTTL, &ds.CDNID, &ds.CDNName, &ds.CheckPath, 
&ds.DeepCachingType, &ds.DisplayName, &ds.DNSBypassCNAME, &ds.DNSBypassIP, 
&ds.DNSBypassIP6, &ds.DNSBypassTTL, &ds.DSCP, &ds.EdgeHeaderRewrite, 
&ds.GeoLimitRedirectURL, &ds.GeoLimit, &ds.GeoLimitCountries, &ds.GeoProvider, 
&ds.GlobalMaxMBPS, &ds.GlobalMaxTPS, &ds.FQPacingRate, &ds.HTTPBypassFQDN, 
&ds.ID, &ds.InfoURL, &ds.InitialDispersion, &ds.IPV6RoutingEnable [...]
+               err := rows.Scan(&ds.Active, &ds.AnonymousBlockingEnabled, 
&ds.CacheURL, &ds.CCRDNSTTL, &ds.CDNID, &ds.CDNName,
+                       &ds.CheckPath, &ds.DeepCachingType, &ds.DisplayName, 
&ds.DNSBypassCNAME, &ds.DNSBypassIP, &ds.DNSBypassIP6,
+                       &ds.DNSBypassTTL, &ds.DSCP, &ds.EdgeHeaderRewrite, 
&ds.GeoLimitRedirectURL, &ds.GeoLimit, &ds.GeoLimitCountries,
+                       &ds.GeoProvider, &ds.GlobalMaxMBPS, &ds.GlobalMaxTPS, 
&ds.FQPacingRate, &ds.HTTPBypassFQDN, &ds.ID, &ds.InfoURL,
+                       &ds.InitialDispersion, &ds.IPV6RoutingEnabled, 
&ds.LastUpdated, &ds.LogsEnabled, &ds.LongDesc, &ds.LongDesc1,
+                       &ds.LongDesc2, &ds.MaxDNSAnswers, &ds.MidHeaderRewrite, 
&ds.MissLat, &ds.MissLong, &ds.MultiSiteOrigin, &ds.OrgServerFQDN, 
&ds.OriginShield, &ds.ProfileID, &ds.ProfileName, &ds.ProfileDesc, 
&ds.Protocol, &ds.QStringIgnore, &ds.RangeRequestHandling, &ds.RegexRemap, 
&ds.RegionalGeoBlocking, &ds.RemapText, &ds.RoutingName, &ds.SigningAlgorithm, 
&ds.SSLKeyVersion, &ds.TenantID, &ds.Tenant, &ds.TRRequestHeaders, 
&ds.TRResponseHeaders, &ds.Type, &ds.TypeID, &ds.XMLID)
                if err != nil {
                        return nil, errors.New("scanning user delivery services 
: " + err.Error())
                }
diff --git a/traffic_ops/v3-client/deliveryservice.go 
b/traffic_ops/v3-client/deliveryservice.go
index 9b5f19f..2e10ffc 100644
--- a/traffic_ops/v3-client/deliveryservice.go
+++ b/traffic_ops/v3-client/deliveryservice.go
@@ -74,7 +74,7 @@ const (
        // intended to be used with fmt.Sprintf to insert its required path 
parameter (namely the XMLID
        // of the Delivery Service of interest).
        // See Also: 
https://traffic-control-cdn.readthedocs.io/en/latest/api/v3/deliveryservices_xmlid_xmlid_sslkeys.html
-       API_DELIVERY_SERVICE_XMLID_SSL_KEYS = API_DELIVERY_SERVICES + 
"/xmlid/%s/sslkeys"
+       API_DELIVERY_SERVICE_XMLID_SSL_KEYS = API_DELIVERY_SERVICES + 
"/xmlId/%s/sslkeys"
 
        // API_DELIVERY_SERVICE_GENERATE_SSL_KEYS is the API path on which 
Traffic Ops will generate new SSL keys
        // See Also: 
https://traffic-control-cdn.readthedocs.io/en/latest/api/v3/deliveryservices_sslkeys_generate.html
diff --git 
a/traffic_portal/app/src/common/modules/dialog/text/dialog.text.tpl.html 
b/traffic_portal/app/src/common/modules/dialog/text/dialog.text.tpl.html
index 625236c..2c1481e 100644
--- a/traffic_portal/app/src/common/modules/dialog/text/dialog.text.tpl.html
+++ b/traffic_portal/app/src/common/modules/dialog/text/dialog.text.tpl.html
@@ -23,7 +23,7 @@ under the License.
 </div>
 <div class="modal-body">
     <p ng-bind-html="params.message"></p>
-    <pre>{{::text}}</pre>
+    <pre ng-if="text != null">{{::text}}</pre>
 </div>
 <div class="modal-footer">
     <button class="btn btn-primary" ng-click="cancel()">Close</button>
diff --git 
a/traffic_portal/app/src/common/modules/form/deliveryService/edit/FormEditDeliveryServiceController.js
 
b/traffic_portal/app/src/common/modules/form/deliveryService/edit/FormEditDeliveryServiceController.js
index 2c03d25..6257110 100644
--- 
a/traffic_portal/app/src/common/modules/form/deliveryService/edit/FormEditDeliveryServiceController.js
+++ 
b/traffic_portal/app/src/common/modules/form/deliveryService/edit/FormEditDeliveryServiceController.js
@@ -22,6 +22,11 @@ var FormEditDeliveryServiceController = 
function(deliveryService, origin, topolo
        // extends the FormDeliveryServiceController to inherit common methods
        angular.extend(this, $controller('FormDeliveryServiceController', { 
deliveryService: deliveryService, dsCurrent: deliveryService, origin: origin, 
topologies: topologies, type: type, types: types, $scope: $scope }));
 
+       this.$onInit = function() {
+               $scope.originalRoutingName = deliveryService.routingName;
+               $scope.originalCDN = deliveryService.cdnId;
+       };
+
        var createDeliveryServiceDeleteRequest = function(deliveryService) {
                var params = {
                        title: "Delivery Service Delete Request",
@@ -161,6 +166,30 @@ var FormEditDeliveryServiceController = 
function(deliveryService, origin, topolo
        };
 
        $scope.save = function(deliveryService) {
+               if (deliveryService.sslKeyVersion !== null && 
deliveryService.sslKeyVersion !== 0 &&
+                       ($scope.originalRoutingName !== 
deliveryService.routingName || $scope.originalCDN !== deliveryService.cdnId) &&
+                       type.indexOf("HTTP") !== -1) {
+
+                       let params = {
+                               title: "Cannot update Delivery Service",
+                               message: "Delivery Service has SSL Keys that 
cannot be updated"
+                       };
+
+                       $uibModal.open({
+                               templateUrl: 
"common/modules/dialog/text/dialog.text.tpl.html",
+                               controller: "DialogTextController",
+                               size: "md",
+                               resolve: {
+                                       params: function() {
+                                               return params;
+                                       },
+                                       text: function() {
+                                               return null;
+                                       }
+                               }
+                       });
+                       return;
+               }
                // if ds requests are enabled in 
traffic_portal_properties.json, we'll create a ds request, else just update the 
ds
                if ($scope.dsRequestsEnabled) {
                        var params = {
@@ -197,7 +226,7 @@ var FormEditDeliveryServiceController = 
function(deliveryService, origin, topolo
                                        status: status,
                                        deliveryService: deliveryService
                                };
-                               // if the user chooses to complete/fulfill the 
update request immediately, a delivery service request will be made and marked 
as complete, 
+                               // if the user chooses to complete/fulfill the 
update request immediately, a delivery service request will be made and marked 
as complete,
                                // then if that is successful, the DS will be 
updated
                                if (options.status.id == $scope.COMPLETE) {
                                        
createDeliveryServiceUpdateRequest(dsRequest, options.comment, true).then(

Reply via email to