Merge pull request #80 from Psycojoker/encrypt

Global password access
This commit is contained in:
abeudin 2017-10-04 01:07:33 +02:00 committed by GitHub
commit 6740a78fd1
16 changed files with 536 additions and 427 deletions

View File

@ -49,6 +49,7 @@ entry like any other gallery.
**NOTE**: expect the "static: " option to disappear quite soon for a more **NOTE**: expect the "static: " option to disappear quite soon for a more
generic approach to "choose your page style". generic approach to "choose your page style".
Global settings Global settings
_______________ _______________
@ -229,6 +230,14 @@ Is option can be use too in gallery settings if you use multi level gallery::
reverse: true reverse: true
Password access
~~~~~~~~~~~~~~~
If you wanna protect all the website by password::
title: Gallery
password: my_super_password
Gallery settings.yaml Gallery settings.yaml
--------------------- ---------------------

View File

@ -17,8 +17,6 @@ import shutil
import socketserver import socketserver
import http.server import http.server
from subprocess import check_output
import ruamel.yaml as yaml import ruamel.yaml as yaml
from docopt import docopt from docopt import docopt
@ -27,7 +25,7 @@ from path import Path
from jinja2 import Environment, FileSystemLoader from jinja2 import Environment, FileSystemLoader
from .cache import CACHE from .cache import CACHE
from .utils import error, warning, okgreen from .utils import error, warning, okgreen, makeform, encrypt
DEFAULTS = { DEFAULTS = {
@ -446,7 +444,6 @@ def create_cover(gallery_name, gallery_settings, gallery_path):
def build_gallery(settings, gallery_settings, gallery_path, template): def build_gallery(settings, gallery_settings, gallery_path, template):
gallery_index_template = template.get_template("gallery-index.html") gallery_index_template = template.get_template("gallery-index.html")
page_template = template.get_template("page.html") page_template = template.get_template("page.html")
encrypted_template = template.get_template("encrypted.html")
# this should probably be a factory # this should probably be a factory
Image.base_dir = Path(".").joinpath(gallery_path) Image.base_dir = Path(".").joinpath(gallery_path)
@ -476,16 +473,9 @@ def build_gallery(settings, gallery_settings, gallery_path, template):
open(Path("build").joinpath(gallery_path, "index.html"), "wb").write(html) open(Path("build").joinpath(gallery_path, "index.html"), "wb").write(html)
if gallery_settings.get("password"): if gallery_settings.get("password") or settings.get("password"):
template_to_render = encrypted_template password = gallery_settings.get("password", settings.get("password"))
password = gallery_settings.get("password") html = encrypt(password, template, gallery_path, settings, gallery_settings)
index_plain = Path("build").joinpath(gallery_path, "index.html")
encrypted = check_output('cat %s | openssl enc -e -base64 -A -aes-256-cbc -pass pass:"%s"' % (index_plain, password), shell=True)
html = template_to_render.render(
settings=settings,
gallery=gallery_settings,
ciphertext=str(encrypted, 'utf-8'),
).encode("Utf-8")
open(Path("build").joinpath(gallery_path, "index.html"), "wb").write(html) open(Path("build").joinpath(gallery_path, "index.html"), "wb").write(html)
@ -524,18 +514,10 @@ def build_gallery(settings, gallery_settings, gallery_path, template):
open(Path("build").joinpath(gallery_light_path, "index.html"), "wb").write(html) open(Path("build").joinpath(gallery_light_path, "index.html"), "wb").write(html)
if gallery_settings.get("password"): if gallery_settings.get("password") or settings.get("password"):
light_template_to_render = light_templates.get_template("encrypted.html") from_template = light_templates.get_template("form.html")
template_to_render = encrypted_template html = encrypt(password, light_templates, gallery_light_path, settings, gallery_settings)
password = gallery_settings.get("password")
index_plain = Path("build").joinpath(gallery_light_path, "index.html")
encrypted = check_output('cat %s | openssl enc -e -base64 -A -aes-256-cbc -pass pass:"%s"' % (index_plain, password), shell=True)
html = light_template_to_render.render(
settings=settings,
gallery=gallery_settings,
ciphertext=str(encrypted, 'utf-8'),
).encode("Utf-8")
open(Path("build").joinpath(gallery_light_path, "index.html"), "wb").write(html) open(Path("build").joinpath(gallery_light_path, "index.html"), "wb").write(html)
@ -565,6 +547,12 @@ def build_index(settings, galleries_cover, templates, gallery_path='', sub_index
open(Path("build").joinpath(gallery_path, "index.html"), "wb").write(html) open(Path("build").joinpath(gallery_path, "index.html"), "wb").write(html)
if settings.get("password"):
password = settings.get("password")
html = encrypt(password, templates, gallery_path, settings, None)
open(Path("build").joinpath(gallery_path, "index.html"), "wb").write(html)
def main(): def main():
arguments = docopt(__doc__, version='0.6') arguments = docopt(__doc__, version='0.6')

View File

@ -0,0 +1,93 @@
.staticrypt-hr {
margin-top: 20px;
margin-bottom: 20px;
border: 0;
border-top: 1px solid #eee;
}
.staticrypt-page {
width: 360px;
padding: 8% 0 0;
margin: auto;
box-sizing: border-box;
}
.staticrypt-form {
position: relative;
z-index: 1;
background: #FFFFFF;
max-width: 360px;
margin: 0 auto 100px;
padding: 45px;
text-align: center;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
}
.staticrypt-form input {
outline: 0;
background: #292525;
width: 100%;
border: 0;
margin: 0 0 15px;
padding: 15px;
box-sizing: border-box;
font-size: 14px;
}
.staticrypt-form .staticrypt-decrypt-button {
text-transform: uppercase;
outline: 0;
background: #91C25F;
width: 100%;
border: 0;
padding: 15px;
color: #FFFFFF;
font-size: 14px;
cursor: pointer;
}
.staticrypt-html {
height: 100%;
}
.staticrypt-body {
background: #FFF; /* fallback for old browsers */
font-family: "Arial", sans-serif;
}
.staticrypt-instructions {
margin-top: -1em;
margin-bottom: 1em;
}
.staticrypt-title {
font-size: 1.5em;
}
footer {
position: relative;
right: 0;
bottom: 0;
left: 0;
margin-top: 6em;
text-align: center;
font-family: 'crimson', serif;
font-size: 11px;
color: #555;
background-color: #EEE;
border-top: solid 2px #DDD;
padding-bottom: 10px;
padding-top: 14px;
}
footer p {
margin: 0;
}
footer a {
text-decoration: none;
font-weight: 600;
font-family: 'montserrat', sans-serif;
color: #111;
}

View File

@ -520,69 +520,3 @@ span.left img, span.right img {
.clear { .clear {
clear: both; clear: both;
} }
.staticrypt-hr {
margin-top: 20px;
margin-bottom: 20px;
border: 0;
border-top: 1px solid #eee;
}
.staticrypt-page {
width: 360px;
padding: 8% 0 0;
margin: auto;
box-sizing: border-box;
}
.staticrypt-form {
position: relative;
z-index: 1;
background: #FFFFFF;
max-width: 360px;
margin: 0 auto 100px;
padding: 45px;
text-align: center;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
}
.staticrypt-form input {
outline: 0;
background: #292525;
width: 100%;
border: 0;
margin: 0 0 15px;
padding: 15px;
box-sizing: border-box;
font-size: 14px;
}
.staticrypt-form .staticrypt-decrypt-button {
text-transform: uppercase;
outline: 0;
background: #91C25F;
width: 100%;
border: 0;
padding: 15px;
color: #FFFFFF;
font-size: 14px;
cursor: pointer;
}
.staticrypt-html {
height: 100%;
}
.staticrypt-body {
background: #FFF; /* fallback for old browsers */
font-family: "Arial", sans-serif;
}
.staticrypt-instructions {
margin-top: -1em;
margin-bottom: 1em;
}
.staticrypt-title {
font-size: 1.5em;
}

View File

@ -1,8 +1,13 @@
{% if gallery %}
{% set pathstatic = ".." %}
{% else %}
{% set pathstatic = "." %}
{% endif %}
<!doctype html> <!doctype html>
<html class="staticrypt-html"> <html class="staticrypt-html">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>{{ gallery.title }} · {{ settings.title }}</title> <title>{% if gallery %}{{ gallery.title }} · {% endif %}{{ settings.title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<!-- do not cache this page --> <!-- do not cache this page -->
@ -11,55 +16,46 @@
<meta http-equiv="expires" content="0"/> <meta http-equiv="expires" content="0"/>
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT"/> <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT"/>
<meta http-equiv="pragma" content="no-cache"/> <meta http-equiv="pragma" content="no-cache"/>
<link type="text/css" rel="stylesheet" href="../static/css/style-page.css" media="screen,projection"/> <link type="text/css" rel="stylesheet" href="{{ pathstatic }}/static/css/encrypt.css" media="screen,projection"/>
</head> </head>
<body id="encrypt-body" class="staticrypt-body">
<body class="staticrypt-body"> <script type="text/javascript" src="{{ pathstatic }}/static/js/crypto-js.min.js" charset="utf-8"></script>
<div class="staticrypt-page">
<div class="staticrypt-form">
<div class="staticrypt-instructions">
<img id="logo" src="./../static/img/logo.svg">
<p class="staticrypt-title">{{ gallery.title }}</p>
</div>
<hr class="staticrypt-hr">
<form id="staticrypt-form" action="#" method="post">
<div id="error" style="color: red; padding-bottom: 10px; height: 20px;"></div>
<input id="staticrypt-password"
type="password"
name="password"
placeholder="passphrase"
autofocus/>
<input type="submit" class="staticrypt-decrypt-button" value="ENTER"/>
</form>
</div>
</div>
<footer style="position: absolute;">
<p>Generated using <a href="https://github.com/psycojoker/prosopopee">Prosopopée</a> · content under <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a> · atom logo by <a href="https://thenounproject.com/jjjon/">Jonathan Li</a> under <a href="https://creativecommons.org/licenses/by/3.0/">CC-BY</a></p>
</footer>
<script type="text/javascript" src="../static/js/crypto-js.min.js" charset="utf-8"></script>
<script> <script>
document.getElementById('staticrypt-form').addEventListener('submit', function(e) { var form = '{{ form }}';
e.preventDefault(); var encryptedMsg = '{{ ciphertext }}';
var passphrase = document.getElementById('staticrypt-password').value,
encryptedMsg = '{{ ciphertext }}';
try{
var plainHTML = CryptoJS.AES.decrypt(encryptedMsg, passphrase).toString(CryptoJS.enc.Utf8);
}
catch(err) {
document.getElementById("error").innerHTML = "Wrong keyword entry."
return;
}
if (sessionStorage.getItem("password")) {
var passphrase = sessionStorage.getItem("password");
var plainHTML = CryptoJS.AES.decrypt(encryptedMsg, passphrase).toString(CryptoJS.enc.Utf8);
document.open();
document.write(plainHTML); document.write(plainHTML);
document.close(); document.close();
}); } else {
var plainHTML = CryptoJS.enc.Base64.parse(form).toString(CryptoJS.enc.Utf8);
document.open();
document.write(plainHTML);
document.close();
document.getElementById('staticrypt-form').addEventListener('submit', function(e) {
e.preventDefault();
var passphrase = document.getElementById('staticrypt-password').value;
try{
var plainHTML = CryptoJS.AES.decrypt(encryptedMsg, passphrase).toString(CryptoJS.enc.Utf8);
}
catch(err) {
document.getElementById("error").innerHTML = "Wrong keyword entry.";
return;
}
sessionStorage.setItem("password", passphrase);
document.open();
document.write(plainHTML);
document.close();
});
}
</script> </script>
</body> </body>
</html> </html>

View File

@ -0,0 +1,21 @@
<div class="staticrypt-page">
<div class="staticrypt-form">
<div class="staticrypt-instructions">
<img id="logo" src="./../static/img/logo.svg">
<p class="staticrypt-title">{% if gallery %}{{ gallery.title }}{% else %}{{ settings.title }}{% endif %}</p>
</div>
<hr class="staticrypt-hr">
<form id="staticrypt-form" action="#" method="post">
<div id="error" style="color: red; padding-bottom: 10px; height: 20px;"></div>
<input id="staticrypt-password"
type="password"
name="password"
placeholder="passphrase"
autofocus/>
<input type="submit" class="staticrypt-decrypt-button" value="ENTER"/>
</form>
</div>
</div>
<footer style="position: absolute;">
<p>Generated using <a href="https://github.com/psycojoker/prosopopee">Prosopopée</a> · content under <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a> · atom logo by <a href="https://thenounproject.com/jjjon/">Jonathan Li</a> under <a href="https://creativecommons.org/licenses/by/3.0/">CC-BY</a></p>
</footer>

View File

@ -1,178 +1,185 @@
html {
background: #efefef;
}
body { body {
color: #222; background: #fff;
font-family: 'montserrat', sans-serif; width: 800px;
background-color: #FBFBFB; max-width: 95%;
margin: 0; margin: 20px auto;
margin-bottom: 20px;
border-radius: 4px;
margin-bottom: 100px;
font-family: sans-serif;
} }
a { a {
text-decoration: none; text-decoration: none;
} }
.galleries-grid { .galleries-grid {
width: 100%; width: 100%;
height: 100%; height: 100%;
position: relative; position: relative;
text-align: center; text-align: center;
margin: 0 auto; margin: 0 auto;
padding: 0px; padding: 0px;
} }
.galleries-line { .galleries-line {
width: 100%; width: 100%;
height: 100%; height: 100%;
margin-bottom: -4px; /* YOLO */ margin-bottom: -4px; /* YOLO */
} }
.covers-1 .gallery-square { .covers-1 .gallery-square {
width: 100%; width: 100%;
height: 100%; height: 100%;
margin: auto; margin: auto;
padding-bottom: 47%; padding-bottom: 47%;
position: relative; position: relative;
} }
.covers-2 .gallery-square { .covers-2 .gallery-square {
width: 50%; width: 50%;
height: 100%; height: 100%;
margin: 0 0 0; margin: 0 0 0;
padding-bottom: 47%; padding-bottom: 47%;
position: relative; position: relative;
display: inline-block; display: inline-block;
} }
.covers-3 .gallery-square { .covers-3 .gallery-square {
width: 33.333333333%; width: 33.333333333%;
height: 100%; height: 100%;
margin: 0; margin: 0;
padding-bottom: 47%; padding-bottom: 47%;
position: relative; position: relative;
display: inline-block; display: inline-block;
} }
.gallery-square > a { .gallery-square > a {
position: absolute; position: absolute;
top: 0px; top: 0px;
left: 0px; left: 0px;
z-index: 555; z-index: 555;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
@keyframes darken { @keyframes darken {
from {background-color: rgba(0, 0, 0, 0);} from {background-color: rgba(0, 0, 0, 0);}
to {background-color: rgba(0, 0, 0, 0.3);} to {background-color: rgba(0, 0, 0, 0.3);}
} }
.gallery-square > a:hover { .gallery-square > a:hover {
animation-name: darken; animation-name: darken;
animation-duration: 0.15s; animation-duration: 0.15s;
animation-iteration-count: 1; animation-iteration-count: 1;
background-color: rgba(0, 0, 0, 0.3); background-color: rgba(0, 0, 0, 0.3);
} }
.gallery-cover { .gallery-cover {
position: absolute; position: absolute;
top: 0px; top: 0px;
left: 0px; left: 0px;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-position: center center; background-position: center center;
background-size: cover; background-size: cover;
} }
.gallery-title { .gallery-title {
color: white; color: white;
width: 100%; width: 100%;
position: absolute; position: absolute;
top: initial; top: initial;
bottom: 0px; bottom: 0px;
text-align: center; text-align: center;
z-index: 3; z-index: 3;
background: transparent linear-gradient(rgba(255, 255, 255, 0) 0%, transparent 1%, rgba(0, 0, 0, 0.07) 26%, rgba(0, 0, 0, 0.5) 71%, rgba(0, 0, 0, 0.7) 100%) repeat scroll 0% 0%; background: transparent linear-gradient(rgba(255, 255, 255, 0) 0%, transparent 1%, rgba(0, 0, 0, 0.07) 26%, rgba(0, 0, 0, 0.5) 71%, rgba(0, 0, 0, 0.7) 100%) repeat scroll 0% 0%;
padding: 20% 0 10px 0; padding: 20% 0 10px 0;
} }
.gallery-header { .gallery-header {
text-align: center; text-align: center;
margin-top: 8em; margin-bottom: 6.5em;
margin-bottom: 6.5em;
} }
.static-header { .static-header {
margin-bottom: 0px; margin-bottom: 0px;
} }
#logo { #logo {
width: 10%; width: 10%;
} }
.gallery-header > h1 { .gallery-header > h1 {
font-size: 2.6vw; font-size: 2.6vw;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 3px; letter-spacing: 3px;
margin-bottom: 0; margin-bottom: 0;
} }
.gallery-header > h4 { .gallery-header > h4 {
font-size: 1.4vw; font-size: 1.4vw;
color: #444; color: #444;
font-style: italic; font-style: italic;
font-weight: normal; font-weight: normal;
font-family: 'crimson', serif; font-family: 'crimson', serif;
margin-top: .5em; margin-top: .5em;
} }
.gallery-header > hr { .gallery-header > hr {
width: 14%; width: 14%;
margin-top: 3.5em; margin-top: 3.5em;
color: #BBB; color: #BBB;
} }
.gallery-title > h2 { .gallery-title > h2 {
text-transform: uppercase; text-transform: uppercase;
margin-bottom: .2em; margin-bottom: .2em;
letter-spacing: 2px; letter-spacing: 2px;
font-size: 1.7vw; font-size: 1.7vw;
} }
.gallery-title > h3 { .gallery-title > h3 {
font-style: italic; font-style: italic;
margin-top: 0; margin-top: 0;
margin-bottom: .7em; margin-bottom: .7em;
font-family: 'crimson', serif; font-family: 'crimson', serif;
font-weight: normal; font-weight: normal;
} }
.gallery-datetime { .gallery-datetime {
font-family: 'crimson', serif; font-family: 'crimson', serif;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 2px; letter-spacing: 2px;
font-size: 11px; font-size: 11px;
} }
footer { footer {
margin-top: 7em; margin-top: 7em;
text-align: center; text-align: center;
position: relative; position: relative;
font-family: 'crimson', serif; font-family: 'crimson', serif;
font-size: 11px; font-size: 11px;
color: #555; color: #555;
background-color: #EEE; background-color: #EEE;
border-top: solid 2px #DDD; border-top: solid 2px #DDD;
padding-bottom: 10px; padding-bottom: 10px;
padding-top: 14px; padding-top: 14px;
} }
footer p { footer p {
margin: 0; margin: 0;
} }
footer a { footer a {
text-decoration: none; text-decoration: none;
font-weight: 600; font-weight: 600;
font-family: 'montserrat', sans-serif; font-family: 'montserrat', sans-serif;
color: #111; color: #111;
} }
nav { nav {
@ -226,76 +233,76 @@ nav ul a {
} }
nav ul li > a.item-menu::before { nav ul li > a.item-menu::before {
content: "/"; content: "/";
margin-right: 22px; margin-right: 22px;
font-size: 18px; font-size: 18px;
line-height: 1; line-height: 1;
color: #ebebeb; color: #ebebeb;
} }
.gallery-tag { .gallery-tag {
font-size: 13px; font-size: 13px;
text-transform: uppercase; text-transform: uppercase;
font-style: normal; font-style: normal;
display: inline; display: inline;
font-family: "adobe-garamond-pro", serif; font-family: "adobe-garamond-pro", serif;
} }
.gallery-tag span { .gallery-tag span {
font-size: 12px; font-size: 12px;
border-bottom: solid 1px rgba(255,255,255,0.2); border-bottom: solid 1px rgba(255,255,255,0.2);
display: inline-block; display: inline-block;
margin: 0 0 0 3px; margin: 0 0 0 3px;
font-weight: bold; font-weight: bold;
font-family: "europa", sans-serif; font-family: "europa", sans-serif;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 3px; letter-spacing: 3px;
font-style: normal; font-style: normal;
} }
.gallery-cover video.fillWidth { .gallery-cover video.fillWidth {
height: 100%; height: 100%;
position: absolute; position: absolute;
top: 0px; top: 0px;
left: 0px; left: 0px;
} }
.gallery-cover img.fillWidth { .gallery-cover img.fillWidth {
height: 100%; height: 100%;
} }
.back-to-home hr { .back-to-home hr {
width: 14%; width: 14%;
color: #DDD; color: #DDD;
} }
.back-to-home #logo { .back-to-home #logo {
background: transparent url("../img/logo.svg") no-repeat scroll center top / cover; background: transparent url("../img/logo.svg") no-repeat scroll center top / cover;
border-radius: 100em; border-radius: 100em;
border: 7px solid black; border: 7px solid black;
margin: auto; margin: auto;
margin-top: 1em; margin-top: 1em;
height: 150px; height: 150px;
width: 150px; width: 150px;
color: transparent; color: transparent;
font-size: 40px; font-size: 40px;
} }
.back-to-home a { .back-to-home a {
position: relative; position: relative;
text-decoration: none; text-decoration: none;
color: transparent; color: transparent;
} }
.back-to-home #logo:hover { .back-to-home #logo:hover {
background-color: black; background-color: black;
text-decoration: none; text-decoration: none;
color: white; color: white;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
text-align: center; text-align: center;
text-transform: uppercase; text-transform: uppercase;
font-family: 'montserrat', sans-serif; font-family: 'montserrat', sans-serif;
font-weight: bold; font-weight: bold;
} }

View File

@ -7,7 +7,7 @@
<html class="staticrypt-html"> <html class="staticrypt-html">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>{{ gallery.title }} · {{ settings.title }}</title> <title>{% if gallery %}{{ gallery.title }} · {% endif %}{{ settings.title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<!-- do not cache this page --> <!-- do not cache this page -->
@ -18,52 +18,40 @@
<meta http-equiv="pragma" content="no-cache"/> <meta http-equiv="pragma" content="no-cache"/>
<link type="text/css" rel="stylesheet" href="{{ pathstatic }}/static/css/style-page.css" media="screen,projection"/> <link type="text/css" rel="stylesheet" href="{{ pathstatic }}/static/css/style-page.css" media="screen,projection"/>
</head> </head>
<body class="staticrypt-body"> <body class="staticrypt-body">
<div class="staticrypt-page"> <script type="text/javascript" src="{{ pathstatic }}/static/js/crypto-js.min.js" charset="utf-8"></script>
<div class="staticrypt-form"> <script>
<div class="staticrypt-instructions"> var form = '{{ form }}';
<p class="staticrypt-title">{{ gallery.title }}</p> var encryptedMsg = '{{ ciphertext }}';
</div>
<hr class="staticrypt-hr"> if (sessionStorage.getItem("password")) {
var passphrase = sessionStorage.getItem("password");
var plainHTML = CryptoJS.AES.decrypt(encryptedMsg, passphrase).toString(CryptoJS.enc.Utf8);
document.write(plainHTML);
document.close();
} else {
var plainHTML = CryptoJS.enc.Base64.parse(form).toString(CryptoJS.enc.Utf8);
document.write(plainHTML);
document.close();
}
<form id="staticrypt-form" action="#" method="post">
<div id="error" style="color: red; padding-bottom: 10px; height: 20px;"></div>
<input id="staticrypt-password"
type="password"
name="password"
placeholder="passphrase"
autofocus/>
<input type="submit" class="staticrypt-decrypt-button" value="ENTER"/>
</form>
</div>
</div>
<footer>
<p>Generated using <a href="https://github.com/psycojoker/prosopopee">Prosopopée</a> · content under <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a> · atom logo by <a href="https://thenounproject.com/jjjon/">Jonathan Li</a> under <a href="https://creativecommons.org/licenses/by/3.0/">CC-BY</a></p>
</footer>
<script type="text/javascript" src="{{ pathstatic }}/static/js/crypto-js.min.js" charset="utf-8"></script>
<script>
document.getElementById('staticrypt-form').addEventListener('submit', function(e) { document.getElementById('staticrypt-form').addEventListener('submit', function(e) {
e.preventDefault(); e.preventDefault();
var passphrase = document.getElementById('staticrypt-password').value, var passphrase = document.getElementById('staticrypt-password').value;
encryptedMsg = '{{ ciphertext }}';
try{ try{
var plainHTML = CryptoJS.AES.decrypt(encryptedMsg, passphrase).toString(CryptoJS.enc.Utf8); var plainHTML = CryptoJS.AES.decrypt(encryptedMsg, passphrase).toString(CryptoJS.enc.Utf8);
} }
catch(err) { catch(err) {
document.getElementById("error").innerHTML = "Wrong keyword entry." document.getElementById("error").innerHTML = "Wrong keyword entry.";
return; return;
} }
sessionStorage.setItem("password", passphrase);
document.write(plainHTML); document.write(plainHTML);
document.close(); document.close();
}); });
</script> </script>
</body> </body>
</html> </html>

View File

@ -0,0 +1,24 @@
<div class="staticrypt-page">
<div class="staticrypt-form">
<div class="staticrypt-instructions">
<p class="staticrypt-title">{% if gallery %}{{ gallery.title }}{% else %}{{ settings.title }}{% endif %}</p>
</div>
<hr class="staticrypt-hr">
<form id="staticrypt-form" action="#" method="post">
<div id="error" style="color: red; padding-bottom: 10px; height: 20px;"></div>
<input id="staticrypt-password"
type="password"
name="password"
placeholder="passphrase"
autofocus/>
<input type="submit" class="staticrypt-decrypt-button" value="ENTER"/>
</form>
</div>
</div>
<footer>
<p>Generated using <a href="https://github.com/psycojoker/prosopopee">Prosopopée</a> · content under <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a> · atom logo by <a href="https://thenounproject.com/jjjon/">Jonathan Li</a> under <a href="https://creativecommons.org/licenses/by/3.0/">CC-BY</a></p>
</footer>

View File

@ -22,7 +22,7 @@
{% block content %} {% block content %}
<div class="galleries-grid"> <div class="galleries-grid">
{% for galleries_line in galleries|reverse|batch(3)|reverse %} {% for galleries_line in galleries|reverse|batch(1)|reverse %}
<div class="galleries-line covers-{{ galleries_line|length }}"> <div class="galleries-line covers-{{ galleries_line|length }}">
{% for gallery in galleries_line|reverse %}<!-- comment tricks against space between inline-block {% for gallery in galleries_line|reverse %}<!-- comment tricks against space between inline-block
--><div class="gallery-square"> --><div class="gallery-square">

View File

@ -0,0 +1,72 @@
.staticrypt-hr {
margin-top: 20px;
margin-bottom: 20px;
border: 0;
border-top: 1px solid #eee;
}
.staticrypt-page {
width: 360px;
padding: 8% 0 0;
margin: auto;
box-sizing: border-box;
}
.staticrypt-form {
position: relative;
z-index: 1;
background: #FFFFFF;
max-width: 360px;
margin: 0 auto 100px;
padding: 45px;
text-align: center;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
}
.staticrypt-form input {
outline: 0;
background: #292525;
width: 100%;
border: 0;
margin: 0 0 15px;
padding: 15px;
box-sizing: border-box;
font-size: 14px;
}
.staticrypt-form .staticrypt-decrypt-button {
text-transform: uppercase;
outline: 0;
background: #91C25F;
width: 100%;
border: 0;
padding: 15px;
color: #FFFFFF;
font-size: 14px;
cursor: pointer;
}
.staticrypt-html {
height: 100%;
}
.staticrypt-body {
background: #37474f; /* fallback for old browsers */
font-family: "Arial", sans-serif;
}
.staticrypt-instructions {
margin-top: -1em;
margin-bottom: 1em;
}
.staticrypt-title {
font-size: 1.5em;
}
#footer-enc {
position: absolute;
right: 0;
bottom: 0;
left: 0;
}

View File

@ -215,69 +215,3 @@ span.right {
.clear { .clear {
clear: both; clear: both;
} }
.staticrypt-hr {
margin-top: 20px;
margin-bottom: 20px;
border: 0;
border-top: 1px solid #eee;
}
.staticrypt-page {
width: 360px;
padding: 8% 0 0;
margin: auto;
box-sizing: border-box;
}
.staticrypt-form {
position: relative;
z-index: 1;
background: #FFFFFF;
max-width: 360px;
margin: 0 auto 100px;
padding: 45px;
text-align: center;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
}
.staticrypt-form input {
outline: 0;
background: #292525;
width: 100%;
border: 0;
margin: 0 0 15px;
padding: 15px;
box-sizing: border-box;
font-size: 14px;
}
.staticrypt-form .staticrypt-decrypt-button {
text-transform: uppercase;
outline: 0;
background: #91C25F;
width: 100%;
border: 0;
padding: 15px;
color: #FFFFFF;
font-size: 14px;
cursor: pointer;
}
.staticrypt-html {
height: 100%;
}
.staticrypt-body {
background: #37474f; /* fallback for old browsers */
font-family: "Arial", sans-serif;
}
.staticrypt-instructions {
margin-top: -1em;
margin-bottom: 1em;
}
.staticrypt-title {
font-size: 1.5em;
}

View File

@ -1,8 +1,13 @@
{% if gallery %}
{% set pathstatic = ".." %}
{% else %}
{% set pathstatic = "." %}
{% endif %}
<!doctype html> <!doctype html>
<html class="staticrypt-html"> <html class="staticrypt-html">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>{{ gallery.title }} · {{ settings.title }}</title> <title>{% if gallery %}{{ gallery.title }} · {% endif %}{{ settings.title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<!-- do not cache this page --> <!-- do not cache this page -->
@ -11,59 +16,43 @@
<meta http-equiv="expires" content="0"/> <meta http-equiv="expires" content="0"/>
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT"/> <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT"/>
<meta http-equiv="pragma" content="no-cache"/> <meta http-equiv="pragma" content="no-cache"/>
<link type="text/css" rel="stylesheet" href="../static/css/style-page.css" media="screen,projection"/> <link type="text/css" rel="stylesheet" href="{{ pathstatic }}/static/css/encrypt.css" media="screen,projection"/>
<link type="text/css" rel="stylesheet" href="../static/css/materialize.css" media="screen,projection"/> <link type="text/css" rel="stylesheet" href="{{ pathstatic }}/static/css/materialize.css" media="screen,projection"/>
</head> </head>
<body class="staticrypt-body"> <body class="staticrypt-body">
<div class="staticrypt-page"> <script type="text/javascript" src="{{ pathstatic }}/static/js/crypto-js.min.js" charset="utf-8"></script>
<div class="staticrypt-form"> <script>
<div class="staticrypt-instructions"> var form = '{{ form }}';
<p class="staticrypt-title">{{ gallery.title }}</p> var encryptedMsg = '{{ ciphertext }}';
</div>
<hr class="staticrypt-hr"> if (sessionStorage.getItem("password")) {
var passphrase = sessionStorage.getItem("password");
var plainHTML = CryptoJS.AES.decrypt(encryptedMsg, passphrase).toString(CryptoJS.enc.Utf8);
document.write(plainHTML);
document.close();
} else {
var plainHTML = CryptoJS.enc.Base64.parse(form).toString(CryptoJS.enc.Utf8);
document.write(plainHTML);
document.close();
}
<form id="staticrypt-form" action="#" method="post">
<div id="error" style="color: red; padding-bottom: 10px; height: 20px;"></div>
<input id="staticrypt-password"
type="password"
name="password"
placeholder="passphrase"
autofocus/>
<button class="btn waves-effect waves-light" type="submit" name="action">ENTER
</button>
</form>
</div>
</div>
<footer class="page-footer blue-grey darken-1">
<div class="footer-copyright blue-grey darken-2">
<div class="container center">
Generated using <a href="https://github.com/psycojoker/prosopopee">Prosopopée</a> · content under <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>
</div>
</div>
</footer>
<script type="text/javascript" src="../static/js/crypto-js.min.js" charset="utf-8"></script>
<script>
document.getElementById('staticrypt-form').addEventListener('submit', function(e) { document.getElementById('staticrypt-form').addEventListener('submit', function(e) {
e.preventDefault(); e.preventDefault();
var passphrase = document.getElementById('staticrypt-password').value, var passphrase = document.getElementById('staticrypt-password').value;
encryptedMsg = '{{ ciphertext }}';
try{ try{
var plainHTML = CryptoJS.AES.decrypt(encryptedMsg, passphrase).toString(CryptoJS.enc.Utf8); var plainHTML = CryptoJS.AES.decrypt(encryptedMsg, passphrase).toString(CryptoJS.enc.Utf8);
} }
catch(err) { catch(err) {
document.getElementById("error").innerHTML = "Wrong keyword entry." document.getElementById("error").innerHTML = "Wrong keyword entry.";
return; return;
} }
sessionStorage.setItem("password", passphrase);
document.write(plainHTML); document.write(plainHTML);
document.close(); document.close();
}); });
</script> </script>
</body> </body>
</html> </html>

View File

@ -0,0 +1,29 @@
<div class="staticrypt-page">
<div class="staticrypt-form">
<div class="staticrypt-instructions">
<p class="staticrypt-title">{% if gallery %}{{ gallery.title }}{% else %}{{ settings.title }}{% endif %}</p>
</div>
<hr class="staticrypt-hr">
<form id="staticrypt-form" action="#" method="post">
<div id="error" style="color: red; padding-bottom: 10px; height: 20px;"></div>
<input id="staticrypt-password"
type="password"
name="password"
placeholder="passphrase"
autofocus/>
<button class="btn waves-effect waves-light" type="submit" name="action">ENTER
</button>
</form>
</div>
</div>
<footer id="footer-enc" class="page-footer blue-grey darken-1">
<div class="footer-copyright blue-grey darken-2">
<div class="container center">
Generated using <a href="https://github.com/psycojoker/prosopopee">Prosopopée</a> · content under <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>
</div>
</div>
</footer>

View File

@ -1,5 +1,13 @@
import sys import sys
import base64
from subprocess import check_output
from path import Path
from jinja2 import Environment, FileSystemLoader
from builtins import str
class bcolors: class bcolors:
OKGREEN = '\033[92m' OKGREEN = '\033[92m'
@ -25,3 +33,20 @@ def warning(logging, warning_message):
def okgreen(logging, ok_message): def okgreen(logging, ok_message):
sys.stderr.write("%s%s: %s%s" % (bcolors.OKGREEN, logging, bcolors.ENDC, ok_message)) sys.stderr.write("%s%s: %s%s" % (bcolors.OKGREEN, logging, bcolors.ENDC, ok_message))
sys.stderr.write("\n") sys.stderr.write("\n")
def makeform(template, settings, gallery_settings):
from_template = template.get_template("form.html")
form = base64.b64encode(from_template.render(settings=settings, gallery=gallery_settings).encode("Utf-8"))
return str(form, 'utf-8')
def encrypt(password, template, gallery_path, settings, gallery_settings):
encrypted_template = template.get_template("encrypted.html")
index_plain = Path("build").joinpath(gallery_path, "index.html")
encrypted = check_output('cat %s | openssl enc -e -base64 -A -aes-256-cbc -pass pass:"%s"' % (index_plain, password), shell=True)
html = encrypted_template.render(
settings=settings,
form=makeform(template, settings, gallery_settings),
ciphertext=str(encrypted, 'utf-8'),
gallery=gallery_settings,
).encode("Utf-8")
return html

View File

@ -12,7 +12,7 @@ except ImportError:
setup(name='prosopopee', setup(name='prosopopee',
version='0.6', version='0.7',
description='A static website generator that allows you to tell a story with your pictures', description='A static website generator that allows you to tell a story with your pictures',
author='Laurent Peuch', author='Laurent Peuch',
long_description=read_md('README.md') + "\n\n\n" + open('docs/changelog.rst').read(), long_description=read_md('README.md') + "\n\n\n" + open('docs/changelog.rst').read(),