From 30442c0c625939c8002dfd4f4c2d4a29fcc18a43 Mon Sep 17 00:00:00 2001 From: Alex Cabal Date: Thu, 14 Jul 2022 12:50:21 -0500 Subject: [PATCH] Move HTTP auth to PHP --- README.md | 4 +- config/apache/standardebooks.org.conf | 48 ++++------------- config/apache/standardebooks.test.conf | 48 ++++------------- config/sql/se/FeedUserAgents.sql | 6 +++ lib/Core.php | 8 ++- www/bulk-downloads/download.php | 2 +- www/feeds/atom/index.php | 2 +- www/feeds/collection.php | 2 +- www/feeds/download.php | 72 ++++++++++++++++++++++++++ www/feeds/get.php | 2 +- www/feeds/index.php | 2 +- 11 files changed, 110 insertions(+), 86 deletions(-) create mode 100644 config/sql/se/FeedUserAgents.sql create mode 100644 www/feeds/download.php diff --git a/README.md b/README.md index aedcded3..edeebd50 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ PHP 7+ is required. ```shell # Install Apache, PHP, PHP-FPM, and various other dependencies. -sudo apt install -y git composer php-fpm php-cli php-gd php-xml php-apcu php-mbstring php-intl php-curl php-zip apache2 apache2-utils libfcgi0ldbl task-spooler ipv6calc mariadb-server libaprutil1-dbd-mysql attr libapache2-mod-xsendfile +sudo apt install -y git composer php-fpm php-cli php-gd php-xml php-apcu php-mbstring php-intl php-curl php-zip apache2 apache2-utils libfcgi0ldbl task-spooler ipv6calc mariadb-server attr libapache2-mod-xsendfile # Create the site root and logs root and clone this repo into it. sudo mkdir /standardebooks.org/ @@ -26,7 +26,7 @@ echo -e "127.0.0.1\tstandardebooks.test" | sudo tee -a /etc/hosts openssl req -x509 -nodes -days 99999 -newkey rsa:4096 -subj "/CN=standardebooks.test" -keyout /standardebooks.org/web/config/ssl/standardebooks.test.key -sha256 -out /standardebooks.org/web/config/ssl/standardebooks.test.crt # Enable the necessary Apache modules. -sudo a2enmod headers expires ssl rewrite proxy proxy_fcgi authn_dbd authn_socache xsendfile +sudo a2enmod headers expires ssl rewrite proxy proxy_fcgi xsendfile # Link and enable the SE Apache configuration file. sudo ln -s /standardebooks.org/web/config/apache/standardebooks.test.conf /etc/apache2/sites-available/ diff --git a/config/apache/standardebooks.org.conf b/config/apache/standardebooks.org.conf index 5e8644ae..ea12b2e4 100644 --- a/config/apache/standardebooks.org.conf +++ b/config/apache/standardebooks.org.conf @@ -273,8 +273,8 @@ Define webroot /standardebooks.org/web RewriteCond expr "tolower(%{REQUEST_METHOD}) =~ /^post$/" RewriteRule ^/polls/([^/\.]+)/votes$ /polls/votes/post.php?pollurlname=$1 [L] - # Feeds - # Rewrite old links to feeds + # Rewrite rules for feeds eeds + # Redirect old feed URLs RewriteRule ^/(opds|rss|atom)(.*)$ /feeds/$1$2 [R=301,L] # If we ask for /opds/all?query=xyz, rewrite that to the search page. @@ -283,31 +283,12 @@ Define webroot /standardebooks.org/web RewriteRule ^/feeds/(atom|rss)/([^/\.]+)$ /feeds/collection.php?type=$1&name=$2 + RewriteRule ^/feeds/(.+\.xml)$ /feeds/download.php?path=$1 + # Rewrite rules for bulk downloads RewriteRule ^/bulk-downloads/(.+\.zip)$ /bulk-downloads/download.php?path=$1 RewriteRule ^/bulk-downloads/([^/\.]+)$ /bulk-downloads/collection.php?name=$1 - # Enable mod_authn_dbd - # DBDriver mysql - # DBDParams "dbname=se user=www-data" - # # HTTP Basic Auth configuration for /feeds - # - # AuthType Basic - # AuthName "Enter your Patrons Circle email address and leave the password empty." - # Require valid-user - - # # Credentials caching to prevent slamming the DB. socache must be ahead of dbd - # AuthBasicProvider socache dbd - # AuthnCacheProvideFor dbd - # AuthnCacheContext ${domain} - - # # mod_authn_dbd SQL query to authenticate a user - # # The hash is simply the hash of a blank password. We're only interested in the username/API key. - # # We have to do this tortured query instead of a cleaner one, because the AuthDBDUserPWQuery - # # function will only replace %s EXACTLY ONCE. We cannot have more than one %s in the query string. - # AuthDBDUserPWQuery "select '$apr1$13q1pnGf$vQnIj94BXP1EPdL/4ISba.' from Users u inner join Benefits b using (UserId) where %s in (u.Email, u.Uuid) and b.CanAccessFeeds = true limit 1" - # - # Specific config for /bulk-downloads # Both directives are required @@ -316,22 +297,11 @@ Define webroot /standardebooks.org/web # Specific config for /feeds - - ErrorDocument 401 /feeds/401 - - - # Disable HTTP Basic auth for the feed XSL stylesheet and the new releases feeds - Require all granted - - - - # Emit content-types for OPDS feeds, as some clients require a strictly correct content-type in order to work - - Header set Content-Type "application/atom+xml;profile=opds-catalog;kind=acquisition; charset=utf-8" - - - Header set Content-Type "application/atom+xml;profile=opds-catalog;kind=navigation; charset=utf-8" - + + # This must be defined at the top level /feeds/ directory + # Both directives are required + XSendFile on + XSendFilePath /standardebooks.org/web/www/feeds # Emit content-types for RSS/Atom feeds diff --git a/config/apache/standardebooks.test.conf b/config/apache/standardebooks.test.conf index 80affa53..56f8125d 100644 --- a/config/apache/standardebooks.test.conf +++ b/config/apache/standardebooks.test.conf @@ -255,8 +255,8 @@ Define webroot /standardebooks.org/web RewriteCond expr "tolower(%{REQUEST_METHOD}) =~ /^post$/" RewriteRule ^/polls/([^/\.]+)/votes$ /polls/votes/post.php?pollurlname=$1 [L] - # Feeds - # Rewrite old links to feeds + # Rewrite rules for feeds eeds + # Redirect old feed URLs RewriteRule ^/(opds|rss|atom)(.*)$ /feeds/$1$2 [R=301,L] # If we ask for /opds/all?query=xyz, rewrite that to the search page. @@ -265,31 +265,12 @@ Define webroot /standardebooks.org/web RewriteRule ^/feeds/(atom|rss)/([^/\.]+)$ /feeds/collection.php?type=$1&name=$2 + RewriteRule ^/feeds/(.+\.xml)$ /feeds/download.php?path=$1 + # Rewrite rules for bulk downloads RewriteRule ^/bulk-downloads/(.+\.zip)$ /bulk-downloads/download.php?path=$1 RewriteRule ^/bulk-downloads/([^/\.]+)$ /bulk-downloads/collection.php?name=$1 - # Enable mod_authn_dbd - DBDriver mysql - DBDParams "dbname=se user=www-data" - # HTTP Basic Auth configuration for /feeds - - AuthType Basic - AuthName "Enter your Patrons Circle email address and leave the password empty." - Require valid-user - - # Credentials caching to prevent slamming the DB. socache must be ahead of dbd - AuthBasicProvider socache dbd - AuthnCacheProvideFor dbd - AuthnCacheContext ${domain} - - # mod_authn_dbd SQL query to authenticate a user - # The hash is simply the hash of a blank password. We're only interested in the username/API key. - # We have to do this tortured query instead of a cleaner one, because the AuthDBDUserPWQuery - # function will only replace %s EXACTLY ONCE. We cannot have more than one %s in the query string. - AuthDBDUserPWQuery "select '$apr1$13q1pnGf$vQnIj94BXP1EPdL/4ISba.' from Users u inner join Benefits b using (UserId) where %s in (u.Email, u.Uuid) and b.CanAccessFeeds = true limit 1" - - # Specific config for /bulk-downloads # Both directives are required @@ -298,22 +279,11 @@ Define webroot /standardebooks.org/web # Specific config for /feeds - - ErrorDocument 401 /feeds/401 - - - # Disable HTTP Basic auth for the feed XSL stylesheet and the new releases feeds - Require all granted - - - - # Emit content-types for OPDS feeds, as some clients require a strictly correct content-type in order to work - - Header set Content-Type "application/atom+xml;profile=opds-catalog;kind=acquisition; charset=utf-8" - - - Header set Content-Type "application/atom+xml;profile=opds-catalog;kind=navigation; charset=utf-8" - + + # This must be defined at the top level /feeds/ directory + # Both directives are required + XSendFile on + XSendFilePath /standardebooks.org/web/www/feeds # Emit content-types for RSS/Atom feeds diff --git a/config/sql/se/FeedUserAgents.sql b/config/sql/se/FeedUserAgents.sql new file mode 100644 index 00000000..94c6b8c8 --- /dev/null +++ b/config/sql/se/FeedUserAgents.sql @@ -0,0 +1,6 @@ +CREATE TABLE `FeedUserAgents` ( + `UserAgentId` int(11) unsigned NOT NULL AUTO_INCREMENT, + `UserAgent` text NOT NULL, + `Created` datetime NOT NULL, + PRIMARY KEY (`UserAgentId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/lib/Core.php b/lib/Core.php index e1822d80..f55686d1 100644 --- a/lib/Core.php +++ b/lib/Core.php @@ -37,6 +37,12 @@ if($GLOBALS['User'] === null){ // log them in while we're here. $session = new Session(); - $session->Create($httpBasicAuthLogin); + try{ + $session->Create($httpBasicAuthLogin); + $GLOBALS['User'] = $session->User; + } + catch(Exception $ex){ + // Do nothing + } } } diff --git a/www/bulk-downloads/download.php b/www/bulk-downloads/download.php index 25392d68..59c88204 100644 --- a/www/bulk-downloads/download.php +++ b/www/bulk-downloads/download.php @@ -26,7 +26,7 @@ try{ // Everything OK, serve the file using Apache. // The xsendfile Apache module tells Apache to serve the file, including not-modified or etag headers. - // Much more efficien than reading it in PHP and outputting it that way. + // Much more efficient than reading it in PHP and outputting it that way. header('X-Sendfile: ' . WEB_ROOT . $path); header('Content-Type: application/zip'); header('Content-Disposition: attachment; filename="' . basename($path) . '"'); diff --git a/www/feeds/atom/index.php b/www/feeds/atom/index.php index 1b0df25b..2f7f52c4 100644 --- a/www/feeds/atom/index.php +++ b/www/feeds/atom/index.php @@ -17,7 +17,7 @@ require_once('Core.php');

The fifteen latest Standard Ebooks, most-recently-released first.

  • -

    All ebooks

    +

    All ebooks

    https://Email) ?>@/feeds/atom/all

    All Standard Ebooks, most-recently-released first.

  • diff --git a/www/feeds/collection.php b/www/feeds/collection.php index c272ccd0..87e9c55f 100644 --- a/www/feeds/collection.php +++ b/www/feeds/collection.php @@ -68,7 +68,7 @@ catch(Safe\Exceptions\ApcuException $ex){
    • -

      Label) ?>

      +

      Label) ?>

      https://Email) ?>@Url) ?>

    • diff --git a/www/feeds/download.php b/www/feeds/download.php new file mode 100644 index 00000000..0e394dbe --- /dev/null +++ b/www/feeds/download.php @@ -0,0 +1,72 @@ +Benefits->CanAccessFeeds){ + throw new Exceptions\InvalidPermissionsException(); + } + } + + // Everything OK, serve the file using Apache. + // The xsendfile Apache module tells Apache to serve the file, including not-modified or etag headers. + // Much more efficient than reading it in PHP and outputting it that way. + header('X-Sendfile: ' . WEB_ROOT . $path); + + if(preg_match('/^\/feeds\/opds/', $path)){ + header('Content-Type: application/atom+xml;profile=opds-catalog;kind=acquisition; charset=utf-8'); + + if(preg_match('/\/index\.xml$/', $path)){ + header('Content-Type: application/atom+xml;profile=opds-catalog;kind=navigation; charset=utf-8'); + } + } + elseif(preg_match('/^\/feeds\/rss/', $path)){ + header('Content-Type: application/rss+xml'); + } + elseif(preg_match('/^\/feeds\/atom/', $path)){ + header('Content-Type: application/atom+xml'); + } + + exit(); +} +catch(Exceptions\LoginRequiredException $ex){ + header('WWW-Authenticate: Basic realm="Enter your Patrons Circle email address and leave the password empty."'); + http_response_code(401); +} +catch(Exceptions\InvalidPermissionsException $ex){ + http_response_code(403); +} +catch(Exceptions\InvalidFileException $ex){ + Template::Emit404(); +} + +// Print the login info page +include(WEB_ROOT . '/feeds/401.php'); diff --git a/www/feeds/get.php b/www/feeds/get.php index 5dcadbd5..f70837c6 100644 --- a/www/feeds/get.php +++ b/www/feeds/get.php @@ -64,7 +64,7 @@ catch(Exceptions\InvalidCollectionException $ex){
      • -

        +

        https://Email) ?>@/feeds///

      diff --git a/www/feeds/index.php b/www/feeds/index.php index eb487625..adba00a4 100644 --- a/www/feeds/index.php +++ b/www/feeds/index.php @@ -19,7 +19,7 @@ require_once('Core.php');

      To connect your ereading app to our catalog, enter the URL below when prompted by your app: