Skip to content

Commit

Permalink
Move feeds behind Patrons Circle login
Browse files Browse the repository at this point in the history
  • Loading branch information
acabal committed Jul 10, 2022
1 parent 0a32b29 commit 6c60301
Show file tree
Hide file tree
Showing 20 changed files with 367 additions and 183 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
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

# Create the site root and logs root and clone this repo into it.
sudo mkdir /standardebooks.org/
Expand All @@ -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
sudo a2enmod headers expires ssl rewrite proxy proxy_fcgi authn_dbd

# Link and enable the SE Apache configuration file.
sudo ln -s /standardebooks.org/web/config/apache/standardebooks.test.conf /etc/apache2/sites-available/
Expand Down
134 changes: 88 additions & 46 deletions config/apache/standardebooks.org.conf
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,23 @@ SSLStaplingReturnResponderErrors off
Define domain standardebooks.org
Define webroot /standardebooks.org/web

<VirtualHost *:80>
ServerName standardebooks.com
ServerAlias www.standardebooks.com
RedirectPermanent / https://${domain}/
</VirtualHost>

<VirtualHost *:443>
ServerName standardebooks.com
ServerAlias www.standardebooks.com
RedirectPermanent / https://${domain}/

SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/standardebooks.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/standardebooks.com/privkey.pem
Header always set Strict-Transport-Security "max-age=15768000"
</VirtualHost>

<VirtualHost *:80>
ServerName ${domain}
ServerAlias www.${domain}
Expand All @@ -72,7 +89,7 @@ Define webroot /standardebooks.org/web
Header always set Strict-Transport-Security "max-age=15768000"
Header set Content-Security-Policy "default-src 'self';"

<Directory ${webroot}/www/>
<Directory "${webroot}/www/">
# Disable .htaccess files
AllowOverride none

Expand All @@ -88,43 +105,31 @@ Define webroot /standardebooks.org/web

AddType application/x-mobi8-ebook .azw3

<Location ~ ^/ebooks/.+?/downloads/.+$>
<LocationMatch "^/ebooks/.+?/downloads/.+$">
# Serve distributables using the "download" dialog instead of opening in-browser
# Note: the trailing e in the Header directive is required
# In modern browsers this is handled with the a@download attribute, we keep this here for compatibility
SetEnvIf Request_URI ^/ebooks/.+?/downloads/(.+)$ FILENAME=$1
Header set Content-Disposition "attachment; filename=%{FILENAME}e"
</Location>
</LocationMatch>

# We explicitly set the content-type for items in the /vocab/ directory, because Apache doesn't set it for us,
# and we need a content-type header when using the "nosniff" header. See https://bugzilla.mozilla.org/show_bug.cgi?id=1547076
<Location ~ ^/vocab/.+$>
<LocationMatch "^/vocab/.+$">
Header set Content-Type "text/plain"
</location>

<Location ~ ^/feeds/opds>
DirectoryIndex index.xml
</location>

# text/xml allows the page to be displayed in a browser and the encoding to be
# determined from the document and not the HTTP headers. application/rss+xml will cause it to be downloaded.
<Directory ~ ${webroot}/www/feeds/>
<Files ~ (\.xml|search\.php)$>
Header set Content-Type "text/xml; charset=utf-8"
</Files>
</Directory>
</LocationMatch>

# Enable HTTP CORS so that browser-based readers like Readium can access opds and ebooks
# Allow fonts for newsletter emails
# See https://github.com/standardebooks/tools/issues/2
<Location ~ /(ebooks|feeds/opds|fonts)>
<LocationMatch "/(ebooks|feeds/opds|fonts)">
Header set Access-Control-Allow-Origin "*"
</Location>
</LocationMatch>

# We use a different CSP policy for single-page files because our default one doesn't allow inline images or CSS
<Location ~ /text/single-page$>
<LocationMatch "/text/single-page$">
Header set Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline';"
</Location>
</LocationMatch>

# Remove www from requests
RewriteCond %{HTTP_HOST} ^www\.(.+) [NC]
Expand All @@ -137,13 +142,13 @@ Define webroot /standardebooks.org/web
SetEnv proxy-sendcl 1

# Forward all PHP requests to the php-fpm pool for this domain.
<FilesMatch \.php$>
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/${domain}.sock|fcgi://${domain}"
Header set Cache-Control "no-store"
</FilesMatch>

# Set some proxy properties.
<Proxy fcgi://${domain}>
<Proxy "fcgi://${domain}">
ProxySet connectiontimeout=5 timeout=240
</Proxy>

Expand Down Expand Up @@ -190,11 +195,11 @@ Define webroot /standardebooks.org/web
# Redirect index pages
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME}/index.php -f
RewriteRule (.*) /$1/index.php
RewriteRule (.*) $1/index.php

RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME}/index.xml -f
RewriteRule (.*) /$1/index.xml
RewriteRule (.*) $1/index.xml

# Remove newline characters inserted by accident in some email clients
RewriteRule ^(.*)\r\n[\ ]?(.*)$ $1$2 [R=301,N]
Expand Down Expand Up @@ -248,13 +253,6 @@ Define webroot /standardebooks.org/web
RewriteCond %{REQUEST_FILENAME} !^/ebooks/.+?/text.*$
RewriteRule ^/ebooks/([^\.]+?)$ /ebooks/ebook.php?url-path=$1

# If we ask for /opds/all?query=xyz, rewrite that to the search page.
RewriteCond %{QUERY_STRING} ^query=
RewriteRule ^/feeds/(opds|atom|rss)/all.xml$ /feeds/$1/search.php [QSA]

# Rewrite old links to feeds
RewriteRule ^/(opds|rss|atom)(.*)$ /feeds/$1$2 [R=301,L]

# Newsletter
RewriteRule ^/newsletter$ /newsletter/subscriptions/new.php [L]
RewriteRule ^/newsletter/subscriptions/([^/\.]+?)$ /newsletter/subscriptions/get.php?uuid=$1 [L]
Expand All @@ -270,21 +268,65 @@ Define webroot /standardebooks.org/web

RewriteCond expr "tolower(%{REQUEST_METHOD}) =~ /^post$/"
RewriteRule ^/patrons-circle/polls/([^/\.]+)/votes$ /patrons-circle/polls/votes/post.php?pollurlname=$1 [L]
</VirtualHost>

<VirtualHost *:80>
ServerName standardebooks.com
ServerAlias www.standardebooks.com
RedirectPermanent / https://${domain}/
</VirtualHost>
# Feeds
# Rewrite old links to feeds
RewriteRule ^/(opds|rss|atom)(.*)$ /feeds/$1$2 [R=301,L]

<VirtualHost *:443>
ServerName standardebooks.com
ServerAlias www.standardebooks.com
RedirectPermanent / https://${domain}/
# If we ask for /opds/all?query=xyz, rewrite that to the search page.
RewriteCond %{QUERY_STRING} \bquery=
RewriteRule ^/feeds/(opds|atom|rss)/all.xml$ /feeds/$1/search.php [QSA]

SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/standardebooks.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/standardebooks.com/privkey.pem
Header always set Strict-Transport-Security "max-age=15768000"
# Enable mod_authn_dbd
DBDriver mysql
DBDParams "dbname=se user=www-data"
<DirectoryMatch "^${webroot}/www/feeds/(opds|rss|atom).*">
# Enable HTTP Basic auth for feeds
AuthType Basic
AuthName "Enter your Patrons Circle email address or your API key, and a blank password."
Require valid-user
ErrorDocument 401 /feeds/401

# 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 \
( \
select Email, Uuid from Patrons p inner join Users u using (UserId) where p.Ended is null \
union \
select Email, Uuid from FeedUsers fu inner join Users u using (UserId) where fu.Ended is null \
) x where %s in (Email, Uuid) limit 1 \
"

<FilesMatch "^(style\.php|new-releases\.xml|index\.php|index\.xml)$">
# Disable HTTP Basic auth for the feed XSL stylesheet and the new releases feeds
Require all granted
</FilesMatch>
</DirectoryMatch>

# Emit content-types for OPDS feeds, as some clients require a strictly correct content-type in order to work
<DirectoryMatch "^${webroot}/www/feeds/opds">
Header set Content-Type "application/atom+xml;profile=opds-catalog;kind=acquisition; charset=utf-8"

<FilesMatch "^index\.xml$">
Header set Content-Type "application/atom+xml;profile=opds-catalog;kind=navigation; charset=utf-8"
</FilesMatch>
</DirectoryMatch>

# Emit content-types for RSS/Atom feeds
<DirectoryMatch "^${webroot}/www/feeds/(?<feedtype>rss|atom)/.+">
# Note the trailing e is required to complete the environmental variable reference
Header set Content-Type "application/%{MATCH_FEEDTYPE}e+xml; charset=utf-8"

<FilesMatch "^index\.php$">
Header set Content-Type "application/xhtml+xml; charset=utf-8"
</FilesMatch>
</DirectoryMatch>
</VirtualHost>
106 changes: 74 additions & 32 deletions config/apache/standardebooks.test.conf
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Define webroot /standardebooks.org/web
Header always set Strict-Transport-Security "max-age=15768000"
Header set Content-Security-Policy "default-src 'self';"

<Directory ${webroot}/www/>
<Directory "${webroot}/www/">
# Disable .htaccess files
AllowOverride none

Expand All @@ -87,43 +87,31 @@ Define webroot /standardebooks.org/web

AddType application/x-mobi8-ebook .azw3

<Location ~ ^/ebooks/.+?/downloads/.+$>
<LocationMatch "^/ebooks/.+?/downloads/.+$">
# Serve distributables using the "download" dialog instead of opening in-browser
# Note: the trailing e in the Header directive is required
# In modern browsers this is handled with the a@download attribute, we keep this here for compatibility
SetEnvIf Request_URI ^/ebooks/.+?/downloads/(.+)$ FILENAME=$1
Header set Content-Disposition "attachment; filename=%{FILENAME}e"
</Location>
</LocationMatch>

# We explicitly set the content-type for items in the /vocab/ directory, because Apache doesn't set it for us,
# and we need a content-type header when using the "nosniff" header. See https://bugzilla.mozilla.org/show_bug.cgi?id=1547076
<Location ~ ^/vocab/.+$>
<LocationMatch "^/vocab/.+$">
Header set Content-Type "text/plain"
</location>

<Location ~ ^/feeds/opds>
DirectoryIndex index.xml
</location>

# text/xml allows the page to be displayed in a browser and the encoding to be
# determined from the document and not the HTTP headers. application/rss+xml will cause it to be downloaded.
<Directory ~ ${webroot}/www/feeds/>
<Files ~ (\.xml|search\.php)$>
Header set Content-Type "text/xml; charset=utf-8"
</Files>
</Directory>
</LocationMatch>

# Enable HTTP CORS so that browser-based readers like Readium can access opds and ebooks
# Allow fonts for newsletter emails
# See https://github.com/standardebooks/tools/issues/2
<Location ~ /(ebooks|feeds/opds|fonts)>
<LocationMatch "/(ebooks|feeds/opds|fonts)">
Header set Access-Control-Allow-Origin "*"
</Location>
</LocationMatch>

# We use a different CSP policy for single-page files because our default one doesn't allow inline images or CSS
<Location ~ /text/single-page$>
<LocationMatch "/text/single-page$">
Header set Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline';"
</Location>
</LocationMatch>

# Remove www from requests
RewriteCond %{HTTP_HOST} ^www\.(.+) [NC]
Expand All @@ -136,13 +124,13 @@ Define webroot /standardebooks.org/web
SetEnv proxy-sendcl 1

# Forward all PHP requests to the php-fpm pool for this domain.
<FilesMatch \.php$>
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/${domain}.sock|fcgi://${domain}"
Header set Cache-Control "no-store"
</FilesMatch>

# Set some proxy properties.
<Proxy fcgi://${domain}>
<Proxy "fcgi://${domain}">
ProxySet connectiontimeout=5 timeout=240
</Proxy>

Expand Down Expand Up @@ -189,11 +177,11 @@ Define webroot /standardebooks.org/web
# Redirect index pages
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME}/index.php -f
RewriteRule (.*) /$1/index.php
RewriteRule (.*) $1/index.php

RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME}/index.xml -f
RewriteRule (.*) /$1/index.xml
RewriteRule (.*) $1/index.xml

# Remove newline characters inserted by accident in some email clients
RewriteRule ^(.*)\r\n[\ ]?(.*)$ $1$2 [R=301,N]
Expand Down Expand Up @@ -247,13 +235,6 @@ Define webroot /standardebooks.org/web
RewriteCond %{REQUEST_FILENAME} !^/ebooks/.+?/text.*$
RewriteRule ^/ebooks/([^\.]+?)$ /ebooks/ebook.php?url-path=$1

# If we ask for /opds/all?query=xyz, rewrite that to the search page.
RewriteCond %{QUERY_STRING} ^query=
RewriteRule ^/feeds/(opds|atom|rss)/all.xml$ /feeds/$1/search.php [QSA]

# Rewrite old links to feeds
RewriteRule ^/(opds|rss|atom)(.*)$ /feeds/$1$2 [R=301,L]

# Newsletter
RewriteRule ^/newsletter$ /newsletter/subscriptions/new.php [L]
RewriteRule ^/newsletter/subscriptions/([^/\.]+?)$ /newsletter/subscriptions/get.php?uuid=$1 [L]
Expand All @@ -269,4 +250,65 @@ Define webroot /standardebooks.org/web

RewriteCond expr "tolower(%{REQUEST_METHOD}) =~ /^post$/"
RewriteRule ^/patrons-circle/polls/([^/\.]+)/votes$ /patrons-circle/polls/votes/post.php?pollurlname=$1 [L]

# Feeds
# Rewrite old links to feeds
RewriteRule ^/(opds|rss|atom)(.*)$ /feeds/$1$2 [R=301,L]

# If we ask for /opds/all?query=xyz, rewrite that to the search page.
RewriteCond %{QUERY_STRING} \bquery=
RewriteRule ^/feeds/(opds|atom|rss)/all.xml$ /feeds/$1/search.php [QSA]

# Enable mod_authn_dbd
DBDriver mysql
DBDParams "dbname=se user=www-data"
<DirectoryMatch "^${webroot}/www/feeds/(opds|rss|atom).*">
# Enable HTTP Basic auth for feeds
AuthType Basic
AuthName "Enter your Patrons Circle email address or your API key, and a blank password."
Require valid-user
ErrorDocument 401 /feeds/401

# 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 \
( \
select Email, Uuid from Patrons p inner join Users u using (UserId) where p.Ended is null \
union \
select Email, Uuid from FeedUsers fu inner join Users u using (UserId) where fu.Ended is null \
) x where %s in (Email, Uuid) limit 1 \
"

<FilesMatch "^(style\.php|new-releases\.xml|index\.php|index\.xml)$">
# Disable HTTP Basic auth for the feed XSL stylesheet and the new releases feeds
Require all granted
</FilesMatch>
</DirectoryMatch>

# Emit content-types for OPDS feeds, as some clients require a strictly correct content-type in order to work
<DirectoryMatch "^${webroot}/www/feeds/opds">
Header set Content-Type "application/atom+xml;profile=opds-catalog;kind=acquisition; charset=utf-8"

<FilesMatch "^index\.xml$">
Header set Content-Type "application/atom+xml;profile=opds-catalog;kind=navigation; charset=utf-8"
</FilesMatch>
</DirectoryMatch>

# Emit content-types for RSS/Atom feeds
<DirectoryMatch "^${webroot}/www/feeds/(?<feedtype>rss|atom)/.+">
# Note the trailing e is required to complete the environmental variable reference
Header set Content-Type "application/%{MATCH_FEEDTYPE}e+xml; charset=utf-8"

<FilesMatch "^index\.php$">
Header set Content-Type "application/xhtml+xml; charset=utf-8"
</FilesMatch>
</DirectoryMatch>
</VirtualHost>
Loading

0 comments on commit 6c60301

Please sign in to comment.