--secret command for datasette publish

Closes #787
This commit is contained in:
Simon Willison 2020-06-11 09:02:03 -07:00 committed by GitHub
commit 98632f0a87
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 58 additions and 14 deletions

View file

@ -165,6 +165,12 @@ def plugins(all, plugins_dir):
) )
@click.option("--spatialite", is_flag=True, help="Enable SpatialLite extension") @click.option("--spatialite", is_flag=True, help="Enable SpatialLite extension")
@click.option("--version-note", help="Additional note to show on /-/versions") @click.option("--version-note", help="Additional note to show on /-/versions")
@click.option(
"--secret",
help="Secret used for signing secure values, such as signed cookies",
envvar="DATASETTE_PUBLISH_SECRET",
default=lambda: os.urandom(32).hex(),
)
@click.option( @click.option(
"-p", "--port", default=8001, help="Port to run the server on, defaults to 8001", "-p", "--port", default=8001, help="Port to run the server on, defaults to 8001",
) )
@ -187,6 +193,7 @@ def package(
install, install,
spatialite, spatialite,
version_note, version_note,
secret,
port, port,
**extra_metadata **extra_metadata
): ):
@ -203,16 +210,17 @@ def package(
with temporary_docker_directory( with temporary_docker_directory(
files, files,
"datasette", "datasette",
metadata, metadata=metadata,
extra_options, extra_options=extra_options,
branch, branch=branch,
template_dir, template_dir=template_dir,
plugins_dir, plugins_dir=plugins_dir,
static, static=static,
install, install=install,
spatialite, spatialite=spatialite,
version_note, version_note=version_note,
extra_metadata, secret=secret,
extra_metadata=extra_metadata,
port=port, port=port,
): ):
args = ["docker", "build"] args = ["docker", "build"]

View file

@ -47,6 +47,7 @@ def publish_subcommand(publish):
install, install,
plugin_secret, plugin_secret,
version_note, version_note,
secret,
title, title,
license, license,
license_url, license_url,
@ -120,6 +121,7 @@ def publish_subcommand(publish):
install, install,
spatialite, spatialite,
version_note, version_note,
secret,
extra_metadata, extra_metadata,
environment_variables, environment_variables,
): ):

View file

@ -1,5 +1,6 @@
from ..utils import StaticMount from ..utils import StaticMount
import click import click
import os
import shutil import shutil
import sys import sys
@ -52,6 +53,12 @@ def add_common_publish_arguments_and_options(subcommand):
click.option( click.option(
"--version-note", help="Additional note to show on /-/versions" "--version-note", help="Additional note to show on /-/versions"
), ),
click.option(
"--secret",
help="Secret used for signing secure values, such as signed cookies",
envvar="DATASETTE_PUBLISH_SECRET",
default=lambda: os.urandom(32).hex(),
),
click.option("--title", help="Title for metadata"), click.option("--title", help="Title for metadata"),
click.option("--license", help="License label for metadata"), click.option("--license", help="License label for metadata"),
click.option("--license_url", help="License URL for metadata"), click.option("--license_url", help="License URL for metadata"),

View file

@ -35,6 +35,7 @@ def publish_subcommand(publish):
install, install,
plugin_secret, plugin_secret,
version_note, version_note,
secret,
title, title,
license, license,
license_url, license_url,
@ -100,6 +101,7 @@ def publish_subcommand(publish):
static, static,
install, install,
version_note, version_note,
secret,
extra_metadata, extra_metadata,
): ):
app_name = None app_name = None
@ -144,6 +146,7 @@ def temporary_heroku_directory(
static, static,
install, install,
version_note, version_note,
secret,
extra_metadata=None, extra_metadata=None,
): ):
extra_metadata = extra_metadata or {} extra_metadata = extra_metadata or {}

View file

@ -278,10 +278,13 @@ def make_dockerfile(
install, install,
spatialite, spatialite,
version_note, version_note,
secret,
environment_variables=None, environment_variables=None,
port=8001, port=8001,
): ):
cmd = ["datasette", "serve", "--host", "0.0.0.0"] cmd = ["datasette", "serve", "--host", "0.0.0.0"]
environment_variables = environment_variables or {}
environment_variables["DATASETTE_SECRET"] = secret
for filename in files: for filename in files:
cmd.extend(["-i", filename]) cmd.extend(["-i", filename])
cmd.extend(["--cors", "--inspect-file", "inspect-data.json"]) cmd.extend(["--cors", "--inspect-file", "inspect-data.json"])
@ -324,7 +327,7 @@ CMD {cmd}""".format(
environment_variables="\n".join( environment_variables="\n".join(
[ [
"ENV {} '{}'".format(key, value) "ENV {} '{}'".format(key, value)
for key, value in (environment_variables or {}).items() for key, value in environment_variables.items()
] ]
), ),
files=" ".join(files), files=" ".join(files),
@ -348,6 +351,7 @@ def temporary_docker_directory(
install, install,
spatialite, spatialite,
version_note, version_note,
secret,
extra_metadata=None, extra_metadata=None,
environment_variables=None, environment_variables=None,
port=8001, port=8001,
@ -381,6 +385,7 @@ def temporary_docker_directory(
install, install,
spatialite, spatialite,
version_note, version_note,
secret,
environment_variables, environment_variables,
port=port, port=port,
) )

View file

@ -17,6 +17,9 @@ Options:
--install TEXT Additional packages (e.g. plugins) to install --install TEXT Additional packages (e.g. plugins) to install
--spatialite Enable SpatialLite extension --spatialite Enable SpatialLite extension
--version-note TEXT Additional note to show on /-/versions --version-note TEXT Additional note to show on /-/versions
--secret TEXT Secret used for signing secure values, such as signed
cookies
-p, --port INTEGER Port to run the server on, defaults to 8001 -p, --port INTEGER Port to run the server on, defaults to 8001
--title TEXT Title for metadata --title TEXT Title for metadata
--license TEXT License label for metadata --license TEXT License label for metadata

View file

@ -15,6 +15,9 @@ Options:
datasette-auth-github client_id xxx datasette-auth-github client_id xxx
--version-note TEXT Additional note to show on /-/versions --version-note TEXT Additional note to show on /-/versions
--secret TEXT Secret used for signing secure values, such as signed
cookies
--title TEXT Title for metadata --title TEXT Title for metadata
--license TEXT License label for metadata --license TEXT License label for metadata
--license_url TEXT License URL for metadata --license_url TEXT License URL for metadata

View file

@ -15,6 +15,9 @@ Options:
datasette-auth-github client_id xxx datasette-auth-github client_id xxx
--version-note TEXT Additional note to show on /-/versions --version-note TEXT Additional note to show on /-/versions
--secret TEXT Secret used for signing secure values, such as signed
cookies
--title TEXT Title for metadata --title TEXT Title for metadata
--license TEXT License label for metadata --license TEXT License label for metadata
--license_url TEXT License URL for metadata --license_url TEXT License URL for metadata

View file

@ -536,6 +536,7 @@ Let's say you want to build a plugin that adds a ``datasette publish my_hosting_
install, install,
plugin_secret, plugin_secret,
version_note, version_note,
secret,
title, title,
license, license,
license_url, license_url,

View file

@ -15,7 +15,7 @@ FROM python:3.8
COPY . /app COPY . /app
WORKDIR /app WORKDIR /app
ENV DATASETTE_SECRET 'sekrit'
RUN pip install -U datasette RUN pip install -U datasette
RUN datasette inspect test.db --inspect-file inspect-data.json RUN datasette inspect test.db --inspect-file inspect-data.json
ENV PORT {port} ENV PORT {port}
@ -33,7 +33,7 @@ def test_package(mock_call, mock_which):
mock_call.side_effect = capture mock_call.side_effect = capture
with runner.isolated_filesystem(): with runner.isolated_filesystem():
open("test.db", "w").write("data") open("test.db", "w").write("data")
result = runner.invoke(cli.cli, ["package", "test.db"]) result = runner.invoke(cli.cli, ["package", "test.db", "--secret", "sekrit"])
assert 0 == result.exit_code assert 0 == result.exit_code
mock_call.assert_has_calls([mock.call(["docker", "build", "."])]) mock_call.assert_has_calls([mock.call(["docker", "build", "."])])
assert EXPECTED_DOCKERFILE.format(port=8001) == capture.captured assert EXPECTED_DOCKERFILE.format(port=8001) == capture.captured
@ -48,6 +48,8 @@ def test_package_with_port(mock_call, mock_which):
runner = CliRunner() runner = CliRunner()
with runner.isolated_filesystem(): with runner.isolated_filesystem():
open("test.db", "w").write("data") open("test.db", "w").write("data")
result = runner.invoke(cli.cli, ["package", "test.db", "-p", "8080"]) result = runner.invoke(
cli.cli, ["package", "test.db", "-p", "8080", "--secret", "sekrit"]
)
assert 0 == result.exit_code assert 0 == result.exit_code
assert EXPECTED_DOCKERFILE.format(port=8080) == capture.captured assert EXPECTED_DOCKERFILE.format(port=8080) == capture.captured

View file

@ -172,6 +172,8 @@ def test_publish_cloudrun_plugin_secrets(mock_call, mock_output, mock_which):
"client_id", "client_id",
"x-client-id", "x-client-id",
"--show-files", "--show-files",
"--secret",
"x-secret",
], ],
) )
dockerfile = ( dockerfile = (
@ -184,6 +186,7 @@ COPY . /app
WORKDIR /app WORKDIR /app
ENV DATASETTE_AUTH_GITHUB_CLIENT_ID 'x-client-id' ENV DATASETTE_AUTH_GITHUB_CLIENT_ID 'x-client-id'
ENV DATASETTE_SECRET 'x-secret'
RUN pip install -U datasette RUN pip install -U datasette
RUN datasette inspect test.db --inspect-file inspect-data.json RUN datasette inspect test.db --inspect-file inspect-data.json
ENV PORT 8001 ENV PORT 8001

View file

@ -247,6 +247,7 @@ def test_temporary_docker_directory_uses_hard_link():
install=[], install=[],
spatialite=False, spatialite=False,
version_note=None, version_note=None,
secret="secret",
) as temp_docker: ) as temp_docker:
hello = os.path.join(temp_docker, "hello") hello = os.path.join(temp_docker, "hello")
assert "world" == open(hello).read() assert "world" == open(hello).read()
@ -274,6 +275,7 @@ def test_temporary_docker_directory_uses_copy_if_hard_link_fails(mock_link):
install=[], install=[],
spatialite=False, spatialite=False,
version_note=None, version_note=None,
secret=None,
) as temp_docker: ) as temp_docker:
hello = os.path.join(temp_docker, "hello") hello = os.path.join(temp_docker, "hello")
assert "world" == open(hello).read() assert "world" == open(hello).read()
@ -297,11 +299,13 @@ def test_temporary_docker_directory_quotes_args():
install=[], install=[],
spatialite=False, spatialite=False,
version_note="$PWD", version_note="$PWD",
secret="secret",
) as temp_docker: ) as temp_docker:
df = os.path.join(temp_docker, "Dockerfile") df = os.path.join(temp_docker, "Dockerfile")
df_contents = open(df).read() df_contents = open(df).read()
assert "'$PWD'" in df_contents assert "'$PWD'" in df_contents
assert "'--$HOME'" in df_contents assert "'--$HOME'" in df_contents
assert "ENV DATASETTE_SECRET 'secret'" in df_contents
def test_compound_keys_after_sql(): def test_compound_keys_after_sql():