You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2020/01/15 16:46:29 UTC

[trafficserver] branch 9.0.x updated (6ef37a3 -> 35f789a)

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

zwoop pushed a change to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git.


    from 6ef37a3  Promote 'Enable_Config_Var' from HttpConnectionCount to HttpConfig. This is so other configuration can use it.
     new f85548b  Add Example URI Signer Python Script
     new 35f789a  Add simple autest and subsequent fixes

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 plugins/experimental/uri_signing/common.h          |   7 +-
 plugins/experimental/uri_signing/config.c          |   1 -
 plugins/experimental/uri_signing/parse.c           |   4 +-
 plugins/experimental/uri_signing/parse.h           |   2 +
 .../uri_signing/python_signer/README.md            |  36 ++++
 .../uri_signing/python_signer/example_config.json  |  33 ++++
 .../uri_signing/python_signer/uri_signer.py        | 132 +++++++++++++
 plugins/experimental/uri_signing/uri_signing.c     |  52 ++++--
 tests/Pipfile                                      |   1 +
 .../gold_tests/pluginTest/uri_signing/config.json  |  27 +++
 .../pluginTest/uri_signing/gold/200.gold           |  13 ++
 .../pluginTest/uri_signing/gold/403.gold           |  16 ++
 .../gold_tests/pluginTest/uri_signing/run_sign.sh  | 104 +++++++++++
 .../gold_tests/pluginTest/uri_signing/signer.json  |  18 ++
 .../pluginTest/uri_signing/uri_signing.test.py     | 208 +++++++++++++++++++++
 15 files changed, 638 insertions(+), 16 deletions(-)
 create mode 100644 plugins/experimental/uri_signing/python_signer/README.md
 create mode 100644 plugins/experimental/uri_signing/python_signer/example_config.json
 create mode 100755 plugins/experimental/uri_signing/python_signer/uri_signer.py
 create mode 100644 tests/gold_tests/pluginTest/uri_signing/config.json
 create mode 100644 tests/gold_tests/pluginTest/uri_signing/gold/200.gold
 create mode 100644 tests/gold_tests/pluginTest/uri_signing/gold/403.gold
 create mode 100755 tests/gold_tests/pluginTest/uri_signing/run_sign.sh
 create mode 100644 tests/gold_tests/pluginTest/uri_signing/signer.json
 create mode 100644 tests/gold_tests/pluginTest/uri_signing/uri_signing.test.py


[trafficserver] 02/02: Add simple autest and subsequent fixes

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

zwoop pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit 35f789a9682a88f0846cfd78f828518b314a79bb
Author: Brian Olsen <br...@comcast.com>
AuthorDate: Thu Dec 5 20:59:52 2019 +0000

    Add simple autest and subsequent fixes
    
    (cherry picked from commit ea3aa04b88bbfea1412d8f2105c73c4eace763ee)
---
 plugins/experimental/uri_signing/common.h          |   7 +-
 plugins/experimental/uri_signing/config.c          |   1 -
 plugins/experimental/uri_signing/parse.c           |   4 +-
 plugins/experimental/uri_signing/parse.h           |   2 +
 .../uri_signing/python_signer/uri_signer.py        | 128 +++++++++----
 plugins/experimental/uri_signing/uri_signing.c     |  52 ++++--
 tests/Pipfile                                      |   1 +
 .../gold_tests/pluginTest/uri_signing/config.json  |  27 +++
 .../pluginTest/uri_signing/gold/200.gold           |  13 ++
 .../pluginTest/uri_signing/gold/403.gold           |  16 ++
 .../gold_tests/pluginTest/uri_signing/run_sign.sh  | 104 +++++++++++
 .../gold_tests/pluginTest/uri_signing/signer.json  |  18 ++
 .../pluginTest/uri_signing/uri_signing.test.py     | 208 +++++++++++++++++++++
 13 files changed, 530 insertions(+), 51 deletions(-)

diff --git a/plugins/experimental/uri_signing/common.h b/plugins/experimental/uri_signing/common.h
index 467d0ce..9a51bb6 100644
--- a/plugins/experimental/uri_signing/common.h
+++ b/plugins/experimental/uri_signing/common.h
@@ -33,7 +33,10 @@ void PrintToStdErr(const char *fmt, ...);
 #else
 
 #include "ts/ts.h"
-#define PluginDebug(...) TSDebug("uri_signing", PLUGIN_NAME " " __VA_ARGS__)
-#define PluginError(...) PluginDebug(__VA_ARGS__), TSError(PLUGIN_NAME " " __VA_ARGS__)
+#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
+#define PluginDebug(fmt, ...) TSDebug(PLUGIN_NAME, "[%s:% 4d] %s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__);
+#define PluginError(fmt, ...)      \
+  PluginDebug(fmt, ##__VA_ARGS__); \
+  TSError("[%s:% 4d] %s(): " fmt, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__);
 
 #endif
diff --git a/plugins/experimental/uri_signing/config.c b/plugins/experimental/uri_signing/config.c
index 815f12c..8727e9f 100644
--- a/plugins/experimental/uri_signing/config.c
+++ b/plugins/experimental/uri_signing/config.c
@@ -289,7 +289,6 @@ read_config(const char *path)
         PluginDebug("Found Id in the config: %s", cfg->id);
       }
     }
-    json_decref(id_json);
 
     json_t *strip_json = json_object_get(jwks, "strip_token");
     if (strip_json) {
diff --git a/plugins/experimental/uri_signing/parse.c b/plugins/experimental/uri_signing/parse.c
index 3ca10b2..f577e74 100644
--- a/plugins/experimental/uri_signing/parse.c
+++ b/plugins/experimental/uri_signing/parse.c
@@ -31,8 +31,8 @@ cjose_jws_t *
 get_jws_from_uri(const char *uri, size_t uri_ct, const char *paramName, char *strip_uri, size_t buff_ct, size_t *strip_ct)
 {
   /* Reserved characters as defined by the URI Generic Syntax RFC: https://tools.ietf.org/html/rfc3986#section-2.2 */
-  static const char *reserved_string  = ":/?#[]@!$&\'()*+,;=";
-  static const char *sub_delim_string = "!$&\'()*+,;=";
+  static char const *const reserved_string  = ":/?#[]@!$&\'()*+,;=";
+  static char const *const sub_delim_string = "!$&\'()*+,;=";
 
   /* If param name ends in reserved character this will be treated as the termination symbol when parsing for package. Default is
    * '='. */
diff --git a/plugins/experimental/uri_signing/parse.h b/plugins/experimental/uri_signing/parse.h
index a16b3e9..98a35ab 100644
--- a/plugins/experimental/uri_signing/parse.h
+++ b/plugins/experimental/uri_signing/parse.h
@@ -21,6 +21,8 @@
 #include <stdlib.h>
 
 struct _cjose_jws_int;
+
+/* For now strip_ct returns size of string *including* the null terminator */
 struct _cjose_jws_int *get_jws_from_uri(const char *uri, size_t uri_ct, const char *paramName, char *strip_uri, size_t buff_ct,
                                         size_t *strip_ct);
 struct _cjose_jws_int *get_jws_from_cookie(const char **cookie, size_t *cookie_ct, const char *paramName);
diff --git a/plugins/experimental/uri_signing/python_signer/uri_signer.py b/plugins/experimental/uri_signing/python_signer/uri_signer.py
index 274361e..353241b 100755
--- a/plugins/experimental/uri_signing/python_signer/uri_signer.py
+++ b/plugins/experimental/uri_signing/python_signer/uri_signer.py
@@ -8,7 +8,7 @@
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
 #
-#      http://www.apache.org/licenses/LICENSE-2.0
+#    http://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
@@ -26,49 +26,107 @@ import os
 from jose import jwk, jwt
 
 def main():
-    parser = argparse.ArgumentParser()
-    parser.add_argument('-c', '--config',
-                        help="Configuration File",
-                        required=True)
-    parser.add_argument('-u', '--uri',
-                        help="URI to sign",
-                        required=True)
-    args = parser.parse_args()
+  parser = argparse.ArgumentParser()
+  parser.add_argument('-c', '--config',
+            help="Configuration File",
+            required=True)
+  parser.add_argument('-u', '--uri',
+            help="URI to sign",
+            required=True)
 
-    with open(args.config, 'r') as f:
-        config = json.load(f)
+  # helpers
+  parser.add_argument('--key_index', type=int, nargs=1)
+  parser.add_argument('--token_lifetime', type=int, nargs=1)
 
-    keys = config["keys"]
+  # override arguments -- claims
+  parser.add_argument('--aud', nargs=1)
+  parser.add_argument('--cdniets', type=int, nargs=1)
+  parser.add_argument('--cdnistd', type=int, nargs=1)
+  parser.add_argument('--cdnistt', type=int, nargs=1)
+  parser.add_argument('--exp', type=int, nargs=1)
+  parser.add_argument('--iss', nargs=1)
 
-    # Randomly select a key
+  # override arguments -- key
+  parser.add_argument('--alg', nargs=1)
+  parser.add_argument('--k', nargs=1)
+  parser.add_argument('--kid', nargs=1)
+  parser.add_argument('--kty', nargs=1)
+
+  args = parser.parse_args()
+
+  with open(args.config, 'r') as f:
+    config = json.load(f)
+
+  keys = config["keys"]
+
+  # Select a key, either explicitly or randomly
+  key_index = 0
+  if args.key_index:
+    key_index = args.key_index[0]
+    print("args key_index " + str(key_index))
+  else:
     key_index = random.randint(0,len(keys)-1)
-    print("Using Key: " + str(keys[key_index]["kid"]) + " to sign URI.")
-    key = keys[key_index]
+    print("randomizing key index")
+
+  print("Using key_index " + str(key_index))
+
+  print("Using Key: " + str(keys[key_index]["kid"]) + " to sign URI.")
+  key = keys[key_index]
+
+  # Build Out claimset
+  claimset = {}
+  if "iss" in config.keys():
+    claimset["iss"] = config["iss"]
+
+  if "token_lifetime" in config.keys():
+    claimset["exp"] = int(time.time()) + config["token_lifetime"]
+  else:
+    claimset["exp"] = int(time.time()) + 30
+
+  if "aud" in config.keys():
+    claimset["aud"] = config["aud"]
+
+  if "cdnistt" in config.keys():
+    if config["cdnistt"]:
+      claimset["cdnistt"] = 1
+      if "cdniets" in config.keys():
+        claimset["cdniets"] = config["cdniets"]
+      else:
+        claimset["cdniets"] = 30
+
+
+  # process override args - simple
+  if args.iss:
+    claimset["iss"] = args.iss[0]
+  if args.exp:
+    claimset["exp"] = args.exp[0]
+  if args.aud:
+    claimset["aud"] = args.aud[0]
 
-    # Build Out claimset
-    claimset = {}
-    if ("iss" in config.keys()):
-        claimset["iss"] = config["iss"]
+  # process override args - complex
+  if args.cdnistt:
+    claimset["cdnistt"] = args.cdnistt[0]
 
-    if ("token_lifetime" in config.keys()):
-        claimset["exp"] = int(time.time()) + config["token_lifetime"]
-    else:
-        claimset["exp"] = int(time.time()) + 30
+  if "cdnistt" in config.keys():
+    if args.cdniets:
+      claimset["cdniets"] = arg.cdniets[0]
 
-    if("aud" in config.keys()):
-        claimset["aud"] = config["aud"]
+  # specific key overrides
+  if args.alg:
+    key["alg"] = args.alg[0]
+  if args.kid:
+    key["kid"] = args.kid[0]
+  if args.kty:
+    key["kty"] = args.kty[0]
+  if args.k:
+    key["k"] = args.k[0]
 
-    if("cdnistt"  in config.keys()):
-        if config["cdnistt"]:
-            claimset["cdnistt"] = 1
-            if("cdniets" in config.keys()):
-                claimset["cdniets"] = config["cdniets"]
-            else:
-                claimset["cdniets"] = 30
+  print(claimset)
+  print(key)
 
-    Token = jwt.encode(claimset,key,algorithm=key["alg"])
+  Token = jwt.encode(claimset,key,algorithm=key["alg"])
 
-    print("Signed URL: " + args.uri + "?urisigning=" + Token)
+  print("Signed URL: " + args.uri + "?URISigningPackage=" + Token)
 
 if __name__ == "__main__":
-     main()
+   main()
diff --git a/plugins/experimental/uri_signing/uri_signing.c b/plugins/experimental/uri_signing/uri_signing.c
index f85a87b..30c8e45 100644
--- a/plugins/experimental/uri_signing/uri_signing.c
+++ b/plugins/experimental/uri_signing/uri_signing.c
@@ -62,10 +62,23 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char *errbuf, int errbuf_s
 
   TSDebug(PLUGIN_NAME, "Initializing remap function of %s -> %s with config from %s", argv[0], argv[1], argv[2]);
 
-  const char *install_dir = TSInstallDirGet();
-  size_t config_file_ct   = snprintf(NULL, 0, "%s/%s/%s", install_dir, "etc/trafficserver", argv[2]);
-  char *config_file       = malloc(config_file_ct + 1);
-  (void)snprintf(config_file, config_file_ct + 1, "%s/%s/%s", install_dir, "etc/trafficserver", argv[2]);
+  char const *const fname = argv[2];
+
+  if (0 == strlen(fname)) {
+    snprintf(errbuf, errbuf_size, "[TSRemapNewKeyInstance] - Invalid config file name for %s -> %s", argv[0], argv[1]);
+    return TS_ERROR;
+  }
+
+  char *config_file = NULL;
+  if ('/' == fname[0]) {
+    config_file = strdup(fname);
+  } else {
+    char const *const config_dir = TSConfigDirGet();
+    size_t const config_file_ct  = snprintf(NULL, 0, "%s/%s", config_dir, fname);
+    config_file                  = malloc(config_file_ct + 1);
+    (void)snprintf(config_file, config_file_ct + 1, "%s/%s", config_dir, fname);
+  }
+
   TSDebug(PLUGIN_NAME, "config file name: %s", config_file);
   struct config *cfg = read_config(config_file);
   if (!cfg) {
@@ -159,8 +172,9 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
   const char *url         = NULL;
   char *strip_uri         = NULL;
   TSRemapStatus status    = TSREMAP_NO_REMAP;
+  bool checked_auth       = false;
 
-  const char *package = "URISigningPackage";
+  static char const *const package = "URISigningPackage";
 
   TSMBuffer mbuf;
   TSMLoc ul;
@@ -207,7 +221,11 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
     field = TSMimeHdrFieldFind(buffer, hdr, "Cookie", 6);
     if (field == TS_NULL_MLOC) {
       TSHandleMLocRelease(buffer, TS_NULL_MLOC, hdr);
-      goto fail;
+      if (!checked_auth) {
+        goto check_auth;
+      } else {
+        goto fail;
+      }
     }
 
     const char *client_cookie;
@@ -218,7 +236,11 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
     TSHandleMLocRelease(buffer, TS_NULL_MLOC, hdr);
 
     if (!client_cookie || !client_cookie_ct) {
-      goto fail;
+      if (!checked_auth) {
+        goto check_auth;
+      } else {
+        goto fail;
+      }
     }
     size_t client_cookie_sz_ct = client_cookie_ct;
   check_more_cookies:
@@ -246,11 +268,15 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
         cjose_jws_t *map_jws = get_jws_from_uri(map_url, map_url_ct, package, map_strip_uri, map_strip_size, &map_strip_ct);
         cjose_jws_release(map_jws);
 
-        char *strip_uri_start = &map_strip_uri[0];
-        char *strip_uri_end   = &map_strip_uri[map_strip_ct];
-        PluginDebug("Stripping token from upstream url to: %s", strip_uri_start);
+        char const *strip_uri_start = map_strip_uri;
 
-        TSParseResult parse_rc = TSUrlParse(rri->requestBufp, rri->requestUrl, (const char **)&strip_uri_start, strip_uri_end);
+        /* map_strip_uri is null terminated */
+        size_t const mlen         = strlen(strip_uri_start);
+        char const *strip_uri_end = strip_uri_start + mlen;
+
+        PluginDebug("Stripping token from upstream url to: %.*s", (int)mlen, strip_uri_start);
+
+        TSParseResult parse_rc = TSUrlParse(rri->requestBufp, rri->requestUrl, &strip_uri_start, strip_uri_end);
         if (map_url != NULL) {
           TSfree(map_url);
         }
@@ -266,8 +292,10 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
       }
     }
   }
+check_auth:
   /* Check auth_dir and pass through if configured */
   if (uri_matches_auth_directive((struct config *)ih, url, url_ct)) {
+    PluginDebug("Auth directive matched for %.*s", url_ct, url);
     if (url != NULL) {
       TSfree((void *)url);
     }
@@ -276,6 +304,8 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
     }
     return TSREMAP_NO_REMAP;
   }
+  checked_auth = true;
+
   if (!jws) {
     goto fail;
   }
diff --git a/tests/Pipfile b/tests/Pipfile
index f98af7f..8176a39 100644
--- a/tests/Pipfile
+++ b/tests/Pipfile
@@ -34,6 +34,7 @@ gunicorn = "*"
 httpbin = "*"
 microserver = ">=1.0.4"
 jsonschema = "*"
+python-jose = "*"
 
 [requires]
 python_version = "3"
diff --git a/tests/gold_tests/pluginTest/uri_signing/config.json b/tests/gold_tests/pluginTest/uri_signing/config.json
new file mode 100644
index 0000000..e466cf7
--- /dev/null
+++ b/tests/gold_tests/pluginTest/uri_signing/config.json
@@ -0,0 +1,27 @@
+{
+ "issuer": {
+  "id": "issuer",
+  "renewal_kid": "1",
+  "strip_token": true,
+  "auth_directives": [
+   {
+    "auth": "allow",
+    "uri": "regex:.*crossdomain.xml"
+   }
+  ],
+  "keys": [
+   {
+    "alg": "HS256",
+    "k": "SECRET00",
+    "kid": "0",
+    "kty": "oct"
+   },
+   {
+    "alg": "HS256",
+    "k": "SECRET01",
+    "kid": "1",
+    "kty": "oct"
+   }
+  ]
+ }
+}
diff --git a/tests/gold_tests/pluginTest/uri_signing/gold/200.gold b/tests/gold_tests/pluginTest/uri_signing/gold/200.gold
new file mode 100644
index 0000000..d3ab3e0
--- /dev/null
+++ b/tests/gold_tests/pluginTest/uri_signing/gold/200.gold
@@ -0,0 +1,13 @@
+``
+> GET `` HTTP/1.1
+> User-Agent: curl/``
+> Host: somehost``
+> Accept: */*
+> Proxy-Connection: ``
+``
+< HTTP/1.1 200 OK
+< Content-Length: ``
+< Date: ``
+< Proxy-Connection: ``
+< Server: ATS/``
+``
diff --git a/tests/gold_tests/pluginTest/uri_signing/gold/403.gold b/tests/gold_tests/pluginTest/uri_signing/gold/403.gold
new file mode 100644
index 0000000..8c6fe13
--- /dev/null
+++ b/tests/gold_tests/pluginTest/uri_signing/gold/403.gold
@@ -0,0 +1,16 @@
+``
+> GET `` HTTP/1.1
+> User-Agent: curl/``
+> Host: somehost``
+> Accept: */*
+> Proxy-Connection: ``
+``
+< HTTP/1.1 403 Forbidden
+< Date: ``
+< Proxy-Connection: ``
+< Server: ATS/``
+< Cache-Control: no-store
+< Content-Type: text/html
+< Content-Language: en
+< Content-Length: ``
+``
diff --git a/tests/gold_tests/pluginTest/uri_signing/run_sign.sh b/tests/gold_tests/pluginTest/uri_signing/run_sign.sh
new file mode 100755
index 0000000..375a83b
--- /dev/null
+++ b/tests/gold_tests/pluginTest/uri_signing/run_sign.sh
@@ -0,0 +1,104 @@
+# Script to run sign.pl script.  Single parameter is number 1 or greater selecting a set of script parameters.
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+# Preset arguments for generating tests
+#
+cmd_args ()
+{
+SELECT="$1"
+
+FUTURE="1923056084" #  Monday, December 9, 2030 14:14:44
+
+case "$SELECT" in
+0) # future signing
+  echo "-c signer.json"
+  echo "-u http://somehost/someasset.ts"
+	echo "--exp=${FUTURE}"
+	echo "--key_index=0"
+  ;;
+1) # expired signing (~1970)
+  echo "-c signer.json"
+  echo "-u http://somehost/someasset.ts"
+	echo "--key_index=0"
+	echo "--exp=1"
+  ;;
+2) # future, second key
+  echo "-c signer.json"
+  echo "-u http://somehost/someasset.ts"
+	echo "--exp=${FUTURE}"
+	echo "--key_index=1"
+  ;;
+3)
+  ;;
+*)
+  echo "run_sign.sh: bad parameter" 1>&2
+  exit 1
+  ;;
+esac
+}
+
+# Find the path to the sign.pl script in the url_sig (source) directory.
+#
+find_cmd ()
+{
+local D T='..'
+while [[ ! -d $T/.git ]]
+do
+  if [[ ! -d $T/.. ]] ; then
+    echo "Working directory not in a git repo" 1>&2
+    exit 1
+  fi
+  T="$T/.."
+done
+
+for D in $( find $T -name uri_signing -type d )
+do
+    if [[ -x $D/python_signer/uri_signer.py ]] ; then
+        echo "$D/python_signer/uri_signer.py"
+        return 0
+    fi
+done
+
+echo "cannot find uri_signer.py" 1>&2
+exit 1
+}
+
+# check for python-jose module
+pip list | grep python-jose > /dev/null
+status=$?
+if [[ "$status" != 0 ]] ; then
+    echo "cannot find python-jose" 1>&2
+    exit 1
+fi
+
+CMD=$( find_cmd )
+status=$?
+if [[ "$status" != 0 ]] ; then
+    exit 1
+fi
+
+
+ARGS=$( cmd_args "$1" )
+status=$?
+if [[ "$status" != 0 ]] ; then
+    exit 1
+fi
+
+echo $CMD $ARGS
+
+$CMD $ARGS | tr ' ' '\n' | tail -1
diff --git a/tests/gold_tests/pluginTest/uri_signing/signer.json b/tests/gold_tests/pluginTest/uri_signing/signer.json
new file mode 100644
index 0000000..7c602f3
--- /dev/null
+++ b/tests/gold_tests/pluginTest/uri_signing/signer.json
@@ -0,0 +1,18 @@
+{
+ "iss": "issuer",
+ "token_lifetime": 90,
+ "keys": [
+  {
+   "alg": "HS256",
+   "k": "SECRET00",
+   "kid": "0",
+   "kty": "oct"
+  },
+  {
+   "alg": "HS256",
+   "k": "SECRET01",
+   "kid": "1",
+   "kty": "oct"
+  }
+ ]
+}
diff --git a/tests/gold_tests/pluginTest/uri_signing/uri_signing.test.py b/tests/gold_tests/pluginTest/uri_signing/uri_signing.test.py
new file mode 100644
index 0000000..bdb4686
--- /dev/null
+++ b/tests/gold_tests/pluginTest/uri_signing/uri_signing.test.py
@@ -0,0 +1,208 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import os
+import subprocess
+Test.Summary = '''
+Test uri_signing plugin
+'''
+
+Test.ContinueOnFail = False
+
+# Skip if plugins not present.
+Test.SkipUnless(Condition.PluginExists('uri_signing.so'))
+
+server = Test.MakeOriginServer("server")
+
+# Default origin test
+req_header = { "headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n",
+	"timestamp": "1469733493.993",
+	"body": "",
+}
+res_header = { "headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n",
+	"timestamp": "1469733493.993",
+	"body": "",
+}
+server.addResponse("sessionfile.log", req_header, res_header)
+
+# Test case for normal
+req_header = { "headers":
+	"GET /someasset.ts HTTP/1.1\r\nHost: somehost\r\n\r\n",
+	"timestamp": "1469733493.993",
+	"body": "",
+}
+
+res_header = { "headers":
+	"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n",
+	"timestamp": "1469733493.993",
+	"body": "somebody",
+}
+
+server.addResponse("sessionfile.log", req_header, res_header)
+
+# Test case for crossdomain
+req_header = { "headers":
+	"GET /crossdomain.xml HTTP/1.1\r\nHost: somehost\r\n\r\n",
+	"timestamp": "1469733493.993",
+	"body": "",
+}
+
+res_header = { "headers":
+	"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n",
+	"timestamp": "1469733493.993",
+	"body": "<crossdomain></crossdomain>",
+}
+
+server.addResponse("sessionfile.log", req_header, res_header)
+
+# http://user:password@host:port/path;params?query#fragment
+
+# Define default ATS
+ts = Test.MakeATSProcess("ts")
+#ts = Test.MakeATSProcess("ts", "traffic_server_valgrind.sh")
+
+ts.Disk.records_config.update({
+  'proxy.config.diags.debug.enabled': 1,
+  'proxy.config.diags.debug.tags': 'uri_signing|http',
+#  'proxy.config.diags.debug.tags': 'uri_signing',
+  'proxy.config.http.cache.http': 0,  # No cache
+})
+
+# Use unchanged incoming URL.
+ts.Disk.remap_config.AddLine(
+    'map http://somehost/ http://127.0.0.1:{}/'.format(server.Variables.Port) +
+    ' @plugin=uri_signing.so @pparam={}/config.json'.format(Test.RunDirectory)
+)
+
+# Install configuration
+ts.Setup.CopyAs('config.json', Test.RunDirectory)
+ts.Setup.CopyAs('run_sign.sh', Test.RunDirectory)
+ts.Setup.CopyAs('signer.json', Test.RunDirectory)
+#ts.Setup.CopyAs('traffic_server_valgrind.sh', Test.RunDirectory)
+
+curl_and_args = 'curl -q -v -x localhost:{} '.format(ts.Variables.port)
+
+# 0 - reject unsigned request
+tr = Test.AddTestRun("unsigned request")
+ps = tr.Processes.Default
+ps.StartBefore(ts)
+ps.StartBefore(server, ready=When.PortOpen(server.Variables.Port))
+ps.Command = curl_and_args + 'http://somehost/someasset.ts'
+ps.ReturnCode = 0
+ps.Streams.stderr = "gold/403.gold"
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+
+# 1 - accept a passthru request
+tr = Test.AddTestRun("passthru request")
+ps = tr.Processes.Default
+ps.Command = curl_and_args + 'http://somehost/crossdomain.xml'
+ps.ReturnCode = 0
+ps.Streams.stderr = "gold/200.gold"
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+
+# 2 - good token, signed "forever" (run_sign.sh 0)
+tr = Test.AddTestRun("good signed")
+ps = tr.Processes.Default
+ps.Command = curl_and_args + '"http://somehost/someasset.ts?URISigningPackage=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJleHAiOjE5MjMwNTYwODR9.zw_wFQ-wvrWmfPLGj3hAUWn-GOHkiJZi2but4KV0paY"'
+ps.ReturnCode = 0
+ps.Streams.stderr = "gold/200.gold"
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+
+# 3 - expired token (run_sign.sh 1)
+tr = Test.AddTestRun("expired signed")
+ps = tr.Processes.Default
+ps.Command = curl_and_args + '"http://somehost/someasset.ts?URISigningPackage=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJleHAiOjF9.GkdlOPHQc6BqS4Q6x79GeYuVFO2zuGbaPZZsJfD6ir8"'
+ps.ReturnCode = 0
+ps.Streams.stderr = "gold/403.gold"
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+
+# 4 - good token, different key (run_sign.sh 2)
+tr = Test.AddTestRun("good token, second key")
+ps = tr.Processes.Default
+ps.Command = curl_and_args + '"http://somehost/someasset.ts?URISigningPackage=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJleHAiOjE5MjMwNTYwODR9.ozH4sNwgcOlTZT0l4RQlVCH_osxz9yI1HCBesEv-jYg"'
+ps.ReturnCode = 0
+ps.Streams.stderr = "gold/200.gold"
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+
+# 5 - good token, inline
+tr = Test.AddTestRun("good signed")
+ps = tr.Processes.Default
+ps.Command = curl_and_args + '"http://somehost/URISigningPackage=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJleHAiOjE5MjMwNTYwODR9.zw_wFQ-wvrWmfPLGj3hAUWn-GOHkiJZi2but4KV0paY/someasset.ts"'
+ps.ReturnCode = 0
+ps.Streams.stderr = "gold/200.gold"
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+
+# 6 - expired token, inline
+tr = Test.AddTestRun("expired signed")
+ps = tr.Processes.Default
+ps.Command = curl_and_args + '"http://somehost/URISigningPackage=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJleHAiOjF9.GkdlOPHQc6BqS4Q6x79GeYuVFO2zuGbaPZZsJfD6ir8/someasset.ts"'
+ps.ReturnCode = 0
+ps.Streams.stderr = "gold/403.gold"
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+
+# 7 - good token, param
+tr = Test.AddTestRun("good signed, param")
+ps = tr.Processes.Default
+ps.Command = curl_and_args + '"http://somehost/someasset.ts;URISigningPackage=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJleHAiOjE5MjMwNTYwODR9.zw_wFQ-wvrWmfPLGj3hAUWn-GOHkiJZi2but4KV0paY"'
+ps.ReturnCode = 0
+ps.Streams.stderr = "gold/200.gold"
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+
+# 8 - expired token, param
+tr = Test.AddTestRun("expired signed, param")
+ps = tr.Processes.Default
+ps.Command = curl_and_args + '"http://somehost/someasset.ts;URISigningPackage=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJleHAiOjF9.GkdlOPHQc6BqS4Q6x79GeYuVFO2zuGbaPZZsJfD6ir8"'
+ps.ReturnCode = 0
+ps.Streams.stderr = "gold/403.gold"
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+
+# 9 - let's cookie this
+tr = Test.AddTestRun("good signed cookie")
+ps = tr.Processes.Default
+ps.Command = curl_and_args + '"http://somehost/someasset.ts" -H "Cookie: URISigningPackage=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJleHAiOjE5MjMwNTYwODR9.zw_wFQ-wvrWmfPLGj3hAUWn-GOHkiJZi2but4KV0paY"'
+ps.ReturnCode = 0
+ps.Streams.stderr = "gold/200.gold"
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+
+# 10 - expired cookie token
+tr = Test.AddTestRun("expired signed cooked")
+ps = tr.Processes.Default
+ps.Command = curl_and_args + '"http://somehost/someasset.ts" -H "Cookie: URISigningPackage=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJleHAiOjF9.GkdlOPHQc6BqS4Q6x79GeYuVFO2zuGbaPZZsJfD6ir8"'
+ps.ReturnCode = 0
+ps.Streams.stderr = "gold/403.gold"
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+
+# 9 - multiple cookies
+tr = Test.AddTestRun("multiple cookies, expired then good")
+ps = tr.Processes.Default
+ps.Command = curl_and_args + '"http://somehost/someasset.ts" -H "Cookie: URISigningPackage=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJleHAiOjF9.GkdlOPHQc6BqS4Q6x79GeYuVFO2zuGbaPZZsJfD6ir8;URISigningPackage=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJleHAiOjE5MjMwNTYwODR9.zw_wFQ-wvrWmfPLGj3hAUWn-GOHkiJZi2but4KV0paY"'
+ps.ReturnCode = 0
+ps.Streams.stderr = "gold/200.gold"
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts


[trafficserver] 01/02: Add Example URI Signer Python Script

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

zwoop pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit f85548b9d6d0699b18e5cbeb23cdda1078f04f1b
Author: Dylan Souza <dy...@comcast.com>
AuthorDate: Wed Oct 23 11:41:00 2019 -0600

    Add Example URI Signer Python Script
    
    Provide an example script to be used in conjunction with the uri signing
    plugin. This script is meant to serve as an example of how to get started
    with uri signing and could be useful in testing various configs.
    
    (cherry picked from commit 3632eb748de4556dd87a66925ef2ace9a2b2c3fa)
---
 .../uri_signing/python_signer/README.md            | 36 +++++++++++
 .../uri_signing/python_signer/example_config.json  | 33 ++++++++++
 .../uri_signing/python_signer/uri_signer.py        | 74 ++++++++++++++++++++++
 3 files changed, 143 insertions(+)

diff --git a/plugins/experimental/uri_signing/python_signer/README.md b/plugins/experimental/uri_signing/python_signer/README.md
new file mode 100644
index 0000000..baa9b22
--- /dev/null
+++ b/plugins/experimental/uri_signing/python_signer/README.md
@@ -0,0 +1,36 @@
+Python URI Signer
+==================
+
+Given a configuration file and a URI, this python script will generate a signed URI according to the URI signing protocol outlined [here](https://tools.ietf.org/html/draft-ietf-cdni-uri-signing-16): 
+
+The script takes a config file and a uri as command line arguments. It picks one of the keys located in the json file at random
+and embeds a valid JWT as a query string parameter into the uri and prints this new signed URI to standard out. 
+
+** Disclaimer ** 
+Please note that this script is provided as a very simple example of how to implement a signer should not be considered production ready. 
+
+Requirements
+------ 
+
+[python-jose](https://pypi.org/project/python-jose/) library must be installed (pip install python-jose).
+
+Config
+------ 
+
+The config file should be a JSON object that contains the following:
+
+  - `iss`: A string representing the issuer of the token
+  - `token_lifetime`: The lifetime of the token in seconds. Expiry of the token is calculated as now + token_lifetime
+  - `aud`: A string representing the intended audience of the token.
+  - `cdnistt`: Boolean value which if set to true uses cookie signed token transport, allowing the validator of the token to 
+    to issue subsequent tokens via set cookie headers.
+  - `cdniets`: Must be set if using cdnistt. Provides means of setting Expiry Times when generating subsequent tokens. It denotes 
+    the number of seconds to be added to the time at which the JWT is verified that gives the value of the Expiry Time claim of the 
+    next signed JWT.
+  - `keys`: A list of json objects, each one representing a key. Each key should have the following attributes:
+      - `alg`: The Cryptographic algorithm to be used with the key.
+      - `kid`: The key identifier
+      - `kty`: The key type
+      - `k`: The key itself
+
+example_config.json can be used as a template for the configuration file.
diff --git a/plugins/experimental/uri_signing/python_signer/example_config.json b/plugins/experimental/uri_signing/python_signer/example_config.json
new file mode 100644
index 0000000..4039796
--- /dev/null
+++ b/plugins/experimental/uri_signing/python_signer/example_config.json
@@ -0,0 +1,33 @@
+{
+	"iss": "Example Issuer",
+	"token_lifetime": 90,
+	"aud": "Caching Software",
+    "cdnistt": true,
+    "cdniets": 30,
+	"keys": [
+		{
+			"alg": "HS256",
+			"kid": 0,
+			"kty": "oct",
+			"k": "SECRET1"
+		},
+		{
+			"alg": "HS256",
+			"kid": 1,
+			"kty": "oct",
+			"k": "SECRET2"
+		},
+		{
+			"alg": "HS256",
+			"kid": 2,
+			"kty": "oct",
+			"k": "SECRET3"
+		},
+		{
+			"alg": "HS256",
+			"kid": 3,
+			"kty": "oct",
+			"k": "SECRET4"
+		}
+	]
+}
diff --git a/plugins/experimental/uri_signing/python_signer/uri_signer.py b/plugins/experimental/uri_signing/python_signer/uri_signer.py
new file mode 100755
index 0000000..274361e
--- /dev/null
+++ b/plugins/experimental/uri_signing/python_signer/uri_signer.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import argparse
+import random
+import time
+import os
+
+# https://github.com/mpdavis/python-jose
+from jose import jwk, jwt
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-c', '--config',
+                        help="Configuration File",
+                        required=True)
+    parser.add_argument('-u', '--uri',
+                        help="URI to sign",
+                        required=True)
+    args = parser.parse_args()
+
+    with open(args.config, 'r') as f:
+        config = json.load(f)
+
+    keys = config["keys"]
+
+    # Randomly select a key
+    key_index = random.randint(0,len(keys)-1)
+    print("Using Key: " + str(keys[key_index]["kid"]) + " to sign URI.")
+    key = keys[key_index]
+
+    # Build Out claimset
+    claimset = {}
+    if ("iss" in config.keys()):
+        claimset["iss"] = config["iss"]
+
+    if ("token_lifetime" in config.keys()):
+        claimset["exp"] = int(time.time()) + config["token_lifetime"]
+    else:
+        claimset["exp"] = int(time.time()) + 30
+
+    if("aud" in config.keys()):
+        claimset["aud"] = config["aud"]
+
+    if("cdnistt"  in config.keys()):
+        if config["cdnistt"]:
+            claimset["cdnistt"] = 1
+            if("cdniets" in config.keys()):
+                claimset["cdniets"] = config["cdniets"]
+            else:
+                claimset["cdniets"] = 30
+
+    Token = jwt.encode(claimset,key,algorithm=key["alg"])
+
+    print("Signed URL: " + args.uri + "?urisigning=" + Token)
+
+if __name__ == "__main__":
+     main()