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

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


The following commit(s) were added to refs/heads/master by this push:
     new d674b634a0 access_control plugin: Generate Session Cookie by "exp=0" 
(#12039)
d674b634a0 is described below

commit d674b634a084abcbc5ca50b9a25b51c1bd44508d
Author: Masaori Koshiba <[email protected]>
AuthorDate: Fri Feb 21 08:22:18 2025 +0900

    access_control plugin: Generate Session Cookie by "exp=0" (#12039)
    
    * access_control plugin: Generate Session Cookie by "exp=0"
    
    * Simplify expectation os AuTest
---
 doc/admin-guide/plugins/access_control.en.rst      |  2 +-
 .../experimental/access_control/access_control.cc  |  3 +-
 plugins/experimental/access_control/plugin.cc      |  1 +
 .../access_control/access_control.test.py          | 81 ++++++++++++++++++++++
 .../pluginTest/access_control/etc/hmac_keys.txt    |  7 ++
 .../replays/access_control.replay.yaml             | 75 ++++++++++++++++++++
 6 files changed, 167 insertions(+), 2 deletions(-)

diff --git a/doc/admin-guide/plugins/access_control.en.rst 
b/doc/admin-guide/plugins/access_control.en.rst
index a060e35f0f..89c25818fc 100644
--- a/doc/admin-guide/plugins/access_control.en.rst
+++ b/doc/admin-guide/plugins/access_control.en.rst
@@ -248,7 +248,7 @@ Query-Param-Style Named Claim format
    * if any claim value contains ``&`` or ``=`` escaping would be necessary 
(i.e. through Percent-Encoding [6]_)
    * the size of the access token cannot be larger then 4K to limit the amount 
of data the application_ could fit in the opaque claims, in general the access 
token is meant to be small since it could end up stored in a cookie and be sent 
as part of lots and lots of requests.
    * during signing the access token payload should end with ``&md=`` and the 
calculated `message digest`_ would be appended directly to the payload to form 
the token (see the example below).
-
+   * the ``exp=0`` is a special case to control this plugin to generate a 
session cookie (no ``Expires=`` attibute in the ``set-cookie`` header).
 
 Let us say we have a user `Kermit the Frog 
<https://en.wikipedia.org/wiki/Kermit_the_Frog>`_ and a user `Michigan J. Frog 
<https://en.wikipedia.org/wiki/Michigan_J._Frog>`_ who are part of a `target 
audience`_ ``frogs-in-a-well`` and a user `Nemo the Clownfish 
<https://en.wikipedia.org/wiki/Finding_Nemo>`_ who is part of a `target 
audience`_ ``fish-in-a-sea``.
 
diff --git a/plugins/experimental/access_control/access_control.cc 
b/plugins/experimental/access_control/access_control.cc
index eb30c10d23..0cfd8daac9 100644
--- a/plugins/experimental/access_control/access_control.cc
+++ b/plugins/experimental/access_control/access_control.cc
@@ -155,7 +155,8 @@ AccessToken::validateTiming(time_t time)
   /* Validate and check expiration timestamp */
   if (!_expiration.empty()) {
     if (0 == (t = string2time(_expiration))) {
-      return _state = INVALID_FIELD_VALUE;
+      // Case of setting Session Cookie by absense of Expires attribute
+      return _state = VALID;
     } else {
       if (time > t) {
         return _state = TOO_LATE;
diff --git a/plugins/experimental/access_control/plugin.cc 
b/plugins/experimental/access_control/plugin.cc
index d0077859d4..c3c9bc177e 100644
--- a/plugins/experimental/access_control/plugin.cc
+++ b/plugins/experimental/access_control/plugin.cc
@@ -376,6 +376,7 @@ contHandleAccessControl(const TSCont contp, TSEvent event, 
void *edata)
               /** Currently Access Token implementation requires expiration to 
be set but the following is still a good
                * consideration. Set the cookie Expires field to the token 
expiration field set by the origin if the time specified
                * is invalid or not specified then don't set Expires attribute.
+               * The "exp=0" is a special case to make Session Cookie by NOT 
appending the Expires attribute.
                * @todo TBD may be adding a default / overriding Expires 
attribute configured by parameter would make sense ? */
               time_t t = token->getExpiration();
               if (0 != t) {
diff --git a/tests/gold_tests/pluginTest/access_control/access_control.test.py 
b/tests/gold_tests/pluginTest/access_control/access_control.test.py
new file mode 100644
index 0000000000..89d5d246f9
--- /dev/null
+++ b/tests/gold_tests/pluginTest/access_control/access_control.test.py
@@ -0,0 +1,81 @@
+'''
+Test access control plugin behaviors
+'''
+#  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 hashlib
+import os
+import re
+
+Test.Summary = '''
+Test access control plugin behaviors
+'''
+
+Test.SkipUnless(Condition.PluginExists('access_control.so'))
+
+
+class AccessControlTest:
+    replayFile = "replays/access_control.replay.yaml"
+
+    def __init__(self):
+        self.setupOriginServer()
+        self.setupTS()
+
+    def setupOriginServer(self):
+        self.server = Test.MakeVerifierServerProcess("verifier-server", 
self.replayFile)
+
+    def setupTS(self):
+        self.ts = Test.MakeATSProcess("ts", enable_tls=True)
+        self.ts.addDefaultSSLFiles()
+        self.ts.Disk.ssl_multicert_config.AddLine("dest_ip=* 
ssl_cert_name=server.pem ssl_key_name=server.key")
+        self.ts.Disk.records_config.update(
+            {
+                "proxy.config.diags.debug.enabled": 1,
+                "proxy.config.diags.debug.tags": "http|access_control",
+                "proxy.config.http.insert_response_via_str": 2,
+                'proxy.config.ssl.server.cert.path': 
f"{self.ts.Variables.SSLDir}",
+                'proxy.config.ssl.server.private_key.path': 
f"{self.ts.Variables.SSLDir}",
+                'proxy.config.ssl.client.alpn_protocols': 'http/1.1',
+                'proxy.config.ssl.client.verify.server.policy': 'PERMISSIVE',
+            })
+
+        self.ts.Setup.Copy("etc/hmac_keys.txt")
+        self.ts.Disk.remap_config.AddLines(
+            {
+                f'''map / 
https://127.0.0.1:{self.server.Variables.https_port}/ \
+    @plugin=access_control.so \
+    @pparam=--symmetric-keys-map={Test.RunDirectory}/hmac_keys.txt \
+    @pparam=--check-cookie=TokenCookie \
+    @pparam=--extract-subject-to-header=@TokenSubject \
+    @pparam=--extract-tokenid-to-header=@TokenId \
+    @pparam=--extract-status-to-header=@TokenStatus \
+    @pparam=--token-response-header=TokenRespHdr'''
+            })
+
+    def run(self):
+        tr = Test.AddTestRun("Session Cookie")
+        tr.AddVerifierClientProcess(
+            "verifier-client",
+            self.replayFile,
+            http_ports=[self.ts.Variables.port],
+            https_ports=[self.ts.Variables.ssl_port],
+            other_args='--thread-limit 1')
+        tr.Processes.Default.StartBefore(self.ts)
+        tr.Processes.Default.StartBefore(self.server)
+        tr.StillRunningAfter = self.ts
+        tr.StillRunningAfter = self.server
+
+
+AccessControlTest().run()
diff --git a/tests/gold_tests/pluginTest/access_control/etc/hmac_keys.txt 
b/tests/gold_tests/pluginTest/access_control/etc/hmac_keys.txt
new file mode 100644
index 0000000000..88278a86da
--- /dev/null
+++ b/tests/gold_tests/pluginTest/access_control/etc/hmac_keys.txt
@@ -0,0 +1,7 @@
+key1=PEIFtmunx9
+key2=BtYjpTbH6a
+key3=SS75kgYonh
+key4=qMmCV2vUsu
+key5=YfMxMaygax
+key6=tVeuPtfJP8
+key7=oplEZT5CpB
diff --git 
a/tests/gold_tests/pluginTest/access_control/replays/access_control.replay.yaml 
b/tests/gold_tests/pluginTest/access_control/replays/access_control.replay.yaml
new file mode 100644
index 0000000000..93fe053890
--- /dev/null
+++ 
b/tests/gold_tests/pluginTest/access_control/replays/access_control.replay.yaml
@@ -0,0 +1,75 @@
+#  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.
+
+sessions:
+- protocol: [{ name: tls, sni: test }]
+
+  transactions:
+  # 1: Regular request
+  # ```
+  # 
payload='sub=frogs-in-a-well&exp=1577836800&nbf=1514764800&iat=1514160000&tid=1234567890&kid=key1&st=HMAC-SHA-256&md='
+  # signature=$(echo -n ${payload} | openssl dgst -sha256 -hmac "PEIFtmunx9")
+  # TokenRespHdr=${payload}${signature}
+  # ```
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /test-1/
+      headers:
+        fields:
+          - [ uuid, 1]
+          - [ Host, example.com ]
+
+    server-response:
+      status: 200
+      headers:
+        fields:
+          - [ TokenRespHdr, 
sub=frogs-in-a-well&exp=4073587200&nbf=1514764800&iat=1514160000&tid=1234567890&kid=key1&st=HMAC-SHA-256&md=d128cb2ee353515f3e09afb2010345a9ea1d9ce3bb49d10b2508eaca29c74d78
 ]
+          - [ Content-Length, 1024 ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+          - [ set-cookie, { value: Expires=, as: contains } ]
+
+  # 2: Origin server set "exp=0" and ATS returns Session Cookie
+  # ```
+  # 
payload='sub=frogs-in-a-well&exp=0&nbf=1514764800&iat=1514160000&tid=1234567890&kid=key1&st=HMAC-SHA-256&md='
+  # signature=$(echo -n ${payload} | openssl dgst -sha256 -hmac "PEIFtmunx9")
+  # TokenRespHdr=${payload}${signature}
+  # ```
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /test-2/
+      headers:
+        fields:
+          - [ uuid, 2]
+          - [ Host, example.com ]
+
+    server-response:
+      status: 200
+      headers:
+        fields:
+          - [ TokenRespHdr, 
sub=frogs-in-a-well&exp=0&nbf=1514764800&iat=1514160000&tid=1234567890&kid=key1&st=HMAC-SHA-256&md=e67dce2fcfaf169dde0fe0e6ecf14bea7658bffbe3f1b30c22b048432e9cc41f
 ]
+          - [ Content-Length, 1024 ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+          - [ set-cookie, { value: Expires=, not: contains } ]

Reply via email to