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 {:dev true
:port 3000 :port 3000
:creator-password "creator"
:user-password "user"
;; when :nrepl-port is set the application starts the nREPL server on load ;; when :nrepl-port is set the application starts the nREPL server on load
:nrepl-port 7000} :nrepl-port 7000}

View file

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

View file

@ -31,7 +31,8 @@
[markdown-clj "0.9.98"] [markdown-clj "0.9.98"]
[clj-exif-orientation "0.2.1"] [clj-exif-orientation "0.2.1"]
[markdown-clj "0.9.98"] [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" :min-lein-version "2.0.0"

View file

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

View file

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

View file

@ -8,9 +8,11 @@
<body> <body>
<nav class="navbar navbar-toggleable-md bg-inverse navbar-inverse"> <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"> <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> <span class="navbar-toggler-icon"></span>
</button> </button>
{% endif %}
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<img src="/img/logo.png"> <img src="/img/logo.png">
yenu yenu
@ -18,6 +20,8 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto mt-2 mt-md-0"> <ul class="navbar-nav mr-auto mt-2 mt-md-0">
{% if identity %}
{% ifequal identity ":creator" %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/upload"> <a class="nav-link" href="/upload">
<span class="fa fa-upload"></span> <span class="fa fa-upload"></span>
@ -30,18 +34,22 @@
Kommentarstream Kommentarstream
</a> </a>
</li> </li>
<!--
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/statistics"> <a class="nav-link" href="/statistics">
<span class="fa fa-bar-chart"></span> <span class="fa fa-bar-chart"></span>
Statistik Statistik
</a> </a>
</li> </li>
-->
{% endifequal %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/logout"> <a class="nav-link" href="/logout">
<span class="fa fa-sign-out"></span> <span class="fa fa-sign-out"></span>
Ausloggen Ausloggen
</a> </a>
</li> </li>
{% endif %}
</div> </div>
</nav> </nav>
@ -52,8 +60,8 @@
<div class="col-md-12 text-right mt-4 mb-1"> <div class="col-md-12 text-right mt-4 mb-1">
<hr> <hr>
<p>© 2017 <a href="https://aaron-fischer.net/">Aaron <p>© 2017 <a href="https://aaron-fischer.net/">Aaron</a>
Fischer</a>, <a href="https://instagram.com/juna_eule">Beatrice Fischer</a></p> & <a href="https://instagram.com/juna_eule">Beatrice</a> Fischer</p>
</div> </div>
{% style "/assets/bootstrap/css/bootstrap.min.css" %} {% style "/assets/bootstrap/css/bootstrap.min.css" %}
@ -63,6 +71,7 @@
<script type="text/javascript"> <script type="text/javascript">
var context = "{{servlet-context}}"; var context = "{{servlet-context}}";
var csrfToken = "{{csrf-token}}"; var csrfToken = "{{csrf-token}}";
var identity = "{{identity}}";
</script> </script>
{% script "/assets/jquery/jquery.min.js" %} {% 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 (ns yenu.handler
(:require [compojure.core :refer [routes wrap-routes]] (:require [compojure.core :refer [routes wrap-routes]]
[yenu.layout :refer [error-page]] [yenu.layout :refer [error-page]]
[yenu.routes.auth :refer [auth-routes]]
[yenu.routes.core :refer [core-routes]] [yenu.routes.core :refer [core-routes]]
[yenu.routes.admin :refer [admin-routes]] [yenu.routes.admin :refer [admin-routes]]
[compojure.route :as route] [compojure.route :as route]
@ -14,12 +15,22 @@
(def app-routes (def app-routes
(routes (routes
(-> #'auth-routes
(wrap-routes middleware/wrap-csrf)
(wrap-routes middleware/wrap-identity)
(wrap-routes middleware/wrap-formats))
(-> #'core-routes (-> #'core-routes
(wrap-routes middleware/wrap-csrf) (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 (-> #'admin-routes
(wrap-routes middleware/wrap-csrf) (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 (route/not-found
(:body (:body
(error-page {:status 404 (error-page {:status 404

View file

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

View file

@ -1,14 +1,21 @@
(ns yenu.middleware (ns yenu.middleware
(:require [yenu.env :refer [defaults]] (:require [yenu.env :refer [defaults]]
[clojure.tools.logging :as log] [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.anti-forgery :refer [wrap-anti-forgery]]
[ring.middleware.webjars :refer [wrap-webjars]] [ring.middleware.webjars :refer [wrap-webjars]]
[ring.middleware.format :refer [wrap-restful-format]] [ring.middleware.format :refer [wrap-restful-format]]
[yenu.config :refer [env]] [yenu.config :refer [env]]
[ring.middleware.flash :refer [wrap-flash]] [ring.middleware.flash :refer [wrap-flash]]
[immutant.web.middleware :refer [wrap-session]] [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])) (:import [javax.servlet ServletContext]))
(defn wrap-context [handler] (defn wrap-context [handler]
@ -44,6 +51,11 @@
{:status 403 {:status 403
:title "Invalid anti-forgery token"})})) :title "Invalid anti-forgery token"})}))
(defn wrap-identity [handler]
(fn [request]
(binding [*identity* (get-in request [:session :identity])]
(handler request))))
(defn wrap-formats [handler] (defn wrap-formats [handler]
(let [wrapped (wrap-restful-format (let [wrapped (wrap-restful-format
handler handler
@ -53,6 +65,28 @@
;; since they're not compatible with this middleware ;; since they're not compatible with this middleware
((if (:websocket? request) handler wrapped) request)))) ((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] (defn wrap-base [handler]
(-> ((:middleware defaults) handler) (-> ((:middleware defaults) handler)
wrap-webjars wrap-webjars

View file

@ -42,4 +42,9 @@
(-> (upload-file file) (-> (upload-file file)
(images/process-image) (images/process-image)
(add-image-to-database title description tags)) (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)] (let [filename (str hash "." ext)]
(file-response (images/data-path "gallery" type filename)))) (file-response (images/data-path "gallery" type filename))))
(defroutes core-routes (defroutes core-routes
(GET "/" [] (GET "/" []
(redirect "/page/1")) (redirect "/page/1"))
@ -48,10 +49,4 @@
;;:ext #"(png|jpg)" ;;:ext #"(png|jpg)"
] ]
[type hash ext] [type hash ext]
(image-file type hash ext)) (image-file type hash ext)))
(GET "/statistics" []
(layout/render "statistics.html"))
(GET "/comments" []
(layout/render "comments.html")))