Skip to content

Instantly share code, notes, and snippets.

@cjgratacos
Last active November 29, 2017 21:02
Show Gist options
  • Save cjgratacos/bfcb8638b974e44827df0c1bac333ce2 to your computer and use it in GitHub Desktop.
Save cjgratacos/bfcb8638b974e44827df0c1bac333ce2 to your computer and use it in GitHub Desktop.
See Issue #223 on contentacms/contenta_jsonapi: Add origin matcher for wildcard matching

asm89/stacks-cors

This folder contains patches for the library ams89/stack-cors that can't be applied due to some particular cirmcumstance.

Patches

pull_request_42.patch

  • Patch created on:
    • November 29 2017 06:22:06 PM (1511979726)
  • Original source:
    • The patch is based out of the ams89/stack-cors PR #42
  • Description patch:
    • A Github PR Patch for #42 was been used in the ContentaCMS Project before. This had to change due that in the ams89/stack-cors 1.1.0 release, a .gitattributes file was added which specifies that the test folder should be ignored on export. Since composer downloads packages as a git archive, the gitattributes is applied, and files in the original repo are ignored when downloaded with composer. The PR which we were using as a patch modifies tests on the test folder and the test folder is one of the folder that is ignored in the .gitattributes and making the patch failing when applied. For this reason pull_request_42.patch was created. It contains only the changes made to the src/ folder on #42.
  • Notes:
    • If the PR changes, we have no way of tracking it and the currently patch will be outdated. There is a low possibility of this happening since the PR has not been updated since 8 months from the time of writing.
  • Instructions to regenerate patch:
      $ git clone [email protected]:asm89/stack-cors.git
      $ cd stack-cors
      $ git checkout feat-originmatch
      $ git diff master -- src > pull_request_42.patch
      s rm <contentacms_root>/patches/asm89_stack-cors/pull_request_42
      $ cp pull_request_42 <contentacms_root>/patches/asm89_stack-cors/
      $ cd ../
      $ rm -rf stack-cors
diff --git a/src/Asm89/Stack/CorsService.php b/src/Asm89/Stack/CorsService.php
index 09dc44b..d6e5b17 100644
--- a/src/Asm89/Stack/CorsService.php
+++ b/src/Asm89/Stack/CorsService.php
@@ -174,9 +174,16 @@ class CorsService
// allow all '*' flag
return true;
}
+
$origin = $request->headers->get('Origin');
- return in_array($origin, $this->options['allowedOrigins']);
+ foreach ($this->options['allowedOrigins'] as $allowedOrign) {
+ if (OriginMatcher::matches($allowedOrign, $origin)) {
+ return true;
+ }
+ }
+
+ return false;
}
private function checkMethod(Request $request)
diff --git a/src/Asm89/Stack/OriginMatcher.php b/src/Asm89/Stack/OriginMatcher.php
new file mode 100644
index 0000000..0113516
--- /dev/null
+++ b/src/Asm89/Stack/OriginMatcher.php
@@ -0,0 +1,118 @@
+<?php
+
+/*
+ * This file is part of asm89/stack-cors.
+ *
+ * (c) Alexander <[email protected]>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Asm89\Stack;
+
+class OriginMatcher
+{
+ public static function matches($pattern, $origin)
+ {
+ if ($pattern === $origin) {
+ return true;
+ }
+
+ $scheme = parse_url($origin, PHP_URL_SCHEME);
+ $host = parse_url($origin, PHP_URL_HOST);
+ $port = parse_url($origin, PHP_URL_PORT);
+
+ $schemePattern = static::parseOriginPattern($pattern, PHP_URL_SCHEME);
+ $hostPattern = static::parseOriginPattern($pattern, PHP_URL_HOST);
+ $portPattern = static::parseOriginPattern($pattern, PHP_URL_PORT);
+
+ $schemeMatches = static::schemeMatches($schemePattern, $scheme);
+ $hostMatches = static::hostMatches($hostPattern, $host);
+ $portMatches = static::portMatches($portPattern, $port);
+
+ return $schemeMatches && $hostMatches && $portMatches;
+ }
+
+ public static function schemeMatches($pattern, $scheme)
+ {
+ return is_null($pattern) || $pattern === $scheme;
+ }
+
+ public static function hostMatches($pattern, $host)
+ {
+ $patternComponents = array_reverse(explode('.', $pattern));
+ $hostComponents = array_reverse(explode('.', $host));
+
+ foreach ($patternComponents as $index => $patternComponent) {
+ if ($patternComponent === '*') {
+ return true;
+ }
+ if (!isset($hostComponents[$index])) {
+ return false;
+ }
+ if ($hostComponents[$index] !== $patternComponent) {
+ return false;
+ }
+ }
+
+ return count($patternComponents) === count($hostComponents);
+ }
+
+ public static function portMatches($pattern, $port)
+ {
+ if ($pattern === "*") {
+ return true;
+ }
+
+ if ((string)$pattern === "") {
+ return (string)$port === "";
+ }
+
+ if (preg_match('/\A\d+\z/', $pattern)) {
+ return (string)$pattern === (string)$port;
+ }
+
+ if (preg_match('/\A(?P<from>\d+)-(?P<to>\d+)\z/', $pattern, $captured)) {
+ return $captured['from'] <= $port && $port <= $captured['to'];
+ }
+
+ throw new \InvalidArgumentException("Invalid port pattern: ${pattern}");
+ }
+
+ public static function parseOriginPattern($originPattern, $component = -1)
+ {
+ $matched = preg_match(
+ '!\A
+ (?: (?P<scheme> ([a-z][a-z0-9+\-.]*) ):// )?
+ (?P<host> (?:\*|[\w-]+)(?:\.[\w-]+)* )
+ (?: :(?P<port> (?: \*|\d+(?:-\d+)? ) ) )?
+ \z!x',
+ $originPattern,
+ $captured
+ );
+
+ if (!$matched) {
+ throw new \InvalidArgumentException("Invalid origin pattern ${originPattern}");
+ }
+
+ $components = [
+ 'scheme' => $captured['scheme'] ?: null,
+ 'host' => $captured['host'],
+ 'port' => array_key_exists('port', $captured) ? $captured['port'] : null,
+ ];
+
+ switch ($component) {
+ case -1:
+ return $components;
+ case PHP_URL_SCHEME:
+ return $components['scheme'];
+ case PHP_URL_HOST:
+ return $components['host'];
+ case PHP_URL_PORT:
+ return $components['port'];
+ }
+
+ throw new \InvalidArgumentException("Invalid component: ${component}");
+ }
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment