Add authentification and authorisation

This commit is contained in:
Aaron Fischer 2017-03-29 14:39:03 +02:00
parent 421acedc8b
commit 81fe574b7b
13 changed files with 147 additions and 27 deletions

View file

@ -1,4 +1,6 @@
{:dev true
:port 3000
:creator-password "creator"
:user-password "user"
;; when :nrepl-port is set the application starts the nREPL server on load
:nrepl-port 7000}

View file

@ -1,2 +1,4 @@
{:production true
:creator-password "xxx"
:user-password "yyy"
:port 3000}

View file

@ -31,7 +31,8 @@
[markdown-clj "0.9.98"]
[clj-exif-orientation "0.2.1"]
[markdown-clj "0.9.98"]
[clj-time "0.8.0"]]
[clj-time "0.8.0"]
[buddy/buddy-auth "1.4.1"]]
:min-lein-version "2.0.0"

View file

@ -31,7 +31,7 @@
<h1>{{ image.title }}</h1>
<div id="image-tags">
{% for tag in tags %}
<span class="tag tag-info">{{ tag.tagname }}</span>
<span class="badge badge-info">{{ tag.tagname }}</span>
{% endfor %}
</div>
</div>

View file

@ -25,6 +25,8 @@
</li>
</ul>
</nav>
<div class="clearfix"></div>
<hr>
{% endif %}
<div class="clearfix">

View file

@ -8,9 +8,11 @@
<body>
<nav class="navbar navbar-toggleable-md bg-inverse navbar-inverse">
{% if identity %}
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
{% endif %}
<a class="navbar-brand" href="/">
<img src="/img/logo.png">
yenu
@ -18,6 +20,8 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto mt-2 mt-md-0">
{% if identity %}
{% ifequal identity ":creator" %}
<li class="nav-item">
<a class="nav-link" href="/upload">
<span class="fa fa-upload"></span>
@ -30,18 +34,22 @@
Kommentarstream
</a>
</li>
<!--
<li class="nav-item">
<a class="nav-link" href="/statistics">
<span class="fa fa-bar-chart"></span>
Statistik
</a>
</li>
-->
{% endifequal %}
<li class="nav-item">
<a class="nav-link" href="/logout">
<span class="fa fa-sign-out"></span>
Ausloggen
</a>
</li>
{% endif %}
</div>
</nav>
@ -52,8 +60,8 @@
<div class="col-md-12 text-right mt-4 mb-1">
<hr>
<p>© 2017 <a href="https://aaron-fischer.net/">Aaron
Fischer</a>, <a href="https://instagram.com/juna_eule">Beatrice Fischer</a></p>
<p>© 2017 <a href="https://aaron-fischer.net/">Aaron</a>
& <a href="https://instagram.com/juna_eule">Beatrice</a> Fischer</p>
</div>
{% style "/assets/bootstrap/css/bootstrap.min.css" %}
@ -63,6 +71,7 @@
<script type="text/javascript">
var context = "{{servlet-context}}";
var csrfToken = "{{csrf-token}}";
var identity = "{{identity}}";
</script>
{% script "/assets/jquery/jquery.min.js" %}

View file

@ -0,0 +1,26 @@
{% extends "layout.html" %}
{% block content %}
<div class="container mt-5">
<div class="row justify-content-md-center">
<div class="col-lg-5 col-sm-12">
<p>Um die Seite anzusehen oder die Aktion auszuführen wird ein Passwort benötigt.</p>
<form action="/login" method="POST" class="form-horizontal">
{% csrf-field %}
<div class="form-group">
<label for="title">Passwort</label>
<input type="password" name="password" class="form-control">
</div>
<button type="submit" class="btn btn-success">
Login
<span class="fa fa-arrow-right"></span>
</button>
</form>
</div>
</div>
</div>
{% endblock %}

View file

@ -1,6 +1,7 @@
(ns yenu.handler
(:require [compojure.core :refer [routes wrap-routes]]
[yenu.layout :refer [error-page]]
[yenu.routes.auth :refer [auth-routes]]
[yenu.routes.core :refer [core-routes]]
[yenu.routes.admin :refer [admin-routes]]
[compojure.route :as route]
@ -14,12 +15,22 @@
(def app-routes
(routes
(-> #'auth-routes
(wrap-routes middleware/wrap-csrf)
(wrap-routes middleware/wrap-identity)
(wrap-routes middleware/wrap-formats))
(-> #'core-routes
(wrap-routes middleware/wrap-csrf)
(wrap-routes middleware/wrap-formats))
(wrap-routes middleware/wrap-formats)
(wrap-routes middleware/wrap-identity)
(wrap-routes middleware/wrap-auth)
)
(-> #'admin-routes
(wrap-routes middleware/wrap-csrf)
(wrap-routes middleware/wrap-formats))
(wrap-routes middleware/wrap-formats)
(wrap-routes middleware/wrap-identity)
(wrap-routes middleware/wrap-auth)
)
(route/not-found
(:body
(error-page {:status 404

View file

@ -8,6 +8,7 @@
[clj-time.format :as time-format])
(:use [markdown.core]))
(declare ^:dynamic *identity*)
(declare ^:dynamic *app-context*)
(parser/set-resource-path! (clojure.java.io/resource "templates"))
(parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field)))
@ -26,7 +27,9 @@
(assoc params
:page template
:csrf-token *anti-forgery-token*
:servlet-context *app-context*)))
:servlet-context *app-context*
:identity *identity*
)))
"text/html; charset=utf-8"))
(defn error-page

View file

@ -1,14 +1,21 @@
(ns yenu.middleware
(:require [yenu.env :refer [defaults]]
[clojure.tools.logging :as log]
[yenu.layout :refer [*app-context* error-page]]
[yenu.layout :refer [*app-context* *identity* error-page]]
[ring.middleware.anti-forgery :refer [wrap-anti-forgery]]
[ring.middleware.webjars :refer [wrap-webjars]]
[ring.middleware.format :refer [wrap-restful-format]]
[yenu.config :refer [env]]
[ring.middleware.flash :refer [wrap-flash]]
[immutant.web.middleware :refer [wrap-session]]
[ring.middleware.defaults :refer [site-defaults wrap-defaults]])
[ring.middleware.defaults :refer [site-defaults wrap-defaults]]
[buddy.auth.middleware :refer [wrap-authentication wrap-authorization]]
[buddy.auth.accessrules :refer [wrap-access-rules]]
[buddy.auth.backends.session :refer [session-backend]]
[buddy.auth.accessrules :refer [restrict]]
[ring.util.response :refer [redirect]]
[buddy.auth.accessrules :refer [success error]]
[buddy.auth :refer [authenticated?]])
(:import [javax.servlet ServletContext]))
(defn wrap-context [handler]
@ -38,29 +45,56 @@
(defn wrap-csrf [handler]
(wrap-anti-forgery
handler
{:error-response
(error-page
{:status 403
:title "Invalid anti-forgery token"})}))
handler
{:error-response
(error-page
{:status 403
:title "Invalid anti-forgery token"})}))
(defn wrap-identity [handler]
(fn [request]
(binding [*identity* (get-in request [:session :identity])]
(handler request))))
(defn wrap-formats [handler]
(let [wrapped (wrap-restful-format
handler
{:formats [:json-kw :transit-json :transit-msgpack]})]
handler
{:formats [:json-kw :transit-json :transit-msgpack]})]
(fn [request]
;; disable wrap-formats for websockets
;; since they're not compatible with this middleware
((if (:websocket? request) handler wrapped) request))))
(defn wrap-auth [handler]
(let [backend (session-backend)]
(-> handler
(wrap-access-rules {:rules rules :on-error on-error})
(wrap-authentication backend)
(wrap-authorization backend))))
(defn on-error [request response]
(redirect "/login"))
(defn creator-access [request]
(let [identity (:identity request)]
(if (= identity :creator)
true
(error "Not a creator."))))
(def rules
[{:uris ["/upload" "/statistics" "/comments"]
:handler creator-access}
{:pattern #"^/.*"
:handler authenticated?}])
(defn wrap-base [handler]
(-> ((:middleware defaults) handler)
wrap-webjars
wrap-flash
(wrap-session {:cookie-attrs {:http-only true}})
(wrap-defaults
(-> site-defaults
(assoc-in [:security :anti-forgery] false)
(dissoc :session)))
(-> site-defaults
(assoc-in [:security :anti-forgery] false)
(dissoc :session)))
wrap-context
wrap-internal-error))

View file

@ -42,4 +42,9 @@
(-> (upload-file file)
(images/process-image)
(add-image-to-database title description tags))
(redirect "/")))
(redirect "/"))
(GET "/statistics" []
(layout/render "statistics.html"))
(GET "/comments" []
(layout/render "comments.html")))

View file

@ -0,0 +1,30 @@
(ns yenu.routes.auth
(:require [yenu.layout :as layout]
[compojure.core :refer [defroutes GET POST]]
[yenu.config :refer [env]]
[ring.util.response :refer [redirect]]))
(defn valid-identity [password]
(cond
(= password (:creator-password env)) :creator
(= password (:user-password env)) :user))
(defn login! [request]
(let [password (get-in request [:form-params "password"])
session (:session request)
user-identity (valid-identity password)]
(if user-identity
(let [updated-session (assoc session :identity user-identity)]
(-> (redirect "/")
(assoc :session updated-session))))))
(defn logout! [request]
(-> (redirect "/")
(assoc :session {})))
(defroutes auth-routes
(GET "/login" []
(layout/render "login.html"))
(POST "/login" [] login!)
(GET "/logout" [] logout!))

View file

@ -35,6 +35,7 @@
(let [filename (str hash "." ext)]
(file-response (images/data-path "gallery" type filename))))
(defroutes core-routes
(GET "/" []
(redirect "/page/1"))
@ -48,10 +49,4 @@
;;:ext #"(png|jpg)"
]
[type hash ext]
(image-file type hash ext))
(GET "/statistics" []
(layout/render "statistics.html"))
(GET "/comments" []
(layout/render "comments.html")))
(image-file type hash ext)))