From 81fe574b7bb76fb9214755e805c875c4908a0413 Mon Sep 17 00:00:00 2001 From: Aaron Fischer Date: Wed, 29 Mar 2017 14:39:03 +0200 Subject: [PATCH] Add authentification and authorisation --- env/dev/resources/config.edn | 2 ++ env/prod/resources/config.edn | 2 ++ project.clj | 3 +- resources/templates/detail.html | 2 +- resources/templates/index.html | 2 ++ resources/templates/layout.html | 13 ++++++-- resources/templates/login.html | 26 +++++++++++++++ src/clj/yenu/handler.clj | 15 +++++++-- src/clj/yenu/layout.clj | 5 ++- src/clj/yenu/middleware.clj | 58 ++++++++++++++++++++++++++------- src/clj/yenu/routes/admin.clj | 7 +++- src/clj/yenu/routes/auth.clj | 30 +++++++++++++++++ src/clj/yenu/routes/core.clj | 9 ++--- 13 files changed, 147 insertions(+), 27 deletions(-) create mode 100644 resources/templates/login.html create mode 100644 src/clj/yenu/routes/auth.clj diff --git a/env/dev/resources/config.edn b/env/dev/resources/config.edn index 6f14c22..75955df 100644 --- a/env/dev/resources/config.edn +++ b/env/dev/resources/config.edn @@ -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} diff --git a/env/prod/resources/config.edn b/env/prod/resources/config.edn index b48cfbd..ec36ac6 100644 --- a/env/prod/resources/config.edn +++ b/env/prod/resources/config.edn @@ -1,2 +1,4 @@ {:production true + :creator-password "xxx" + :user-password "yyy" :port 3000} diff --git a/project.clj b/project.clj index 5b4d0aa..2b19329 100644 --- a/project.clj +++ b/project.clj @@ -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" diff --git a/resources/templates/detail.html b/resources/templates/detail.html index 2cd86ca..6fe67bb 100644 --- a/resources/templates/detail.html +++ b/resources/templates/detail.html @@ -31,7 +31,7 @@

{{ image.title }}

{% for tag in tags %} - {{ tag.tagname }} + {{ tag.tagname }} {% endfor %}
diff --git a/resources/templates/index.html b/resources/templates/index.html index ba4ab30..5fca254 100644 --- a/resources/templates/index.html +++ b/resources/templates/index.html @@ -25,6 +25,8 @@ +
+
{% endif %}
diff --git a/resources/templates/layout.html b/resources/templates/layout.html index a183257..d97379f 100644 --- a/resources/templates/layout.html +++ b/resources/templates/layout.html @@ -8,9 +8,11 @@ @@ -52,8 +60,8 @@

-

© 2017 Aaron - Fischer, Beatrice Fischer

+

© 2017 Aaron + & Beatrice Fischer

{% style "/assets/bootstrap/css/bootstrap.min.css" %} @@ -63,6 +71,7 @@ {% script "/assets/jquery/jquery.min.js" %} diff --git a/resources/templates/login.html b/resources/templates/login.html new file mode 100644 index 0000000..4ef0ae3 --- /dev/null +++ b/resources/templates/login.html @@ -0,0 +1,26 @@ +{% extends "layout.html" %} + +{% block content %} + +
+
+
+

Um die Seite anzusehen oder die Aktion auszuführen wird ein Passwort benötigt.

+ +
+ {% csrf-field %} + +
+ + +
+ + +
+
+
+
+{% endblock %} diff --git a/src/clj/yenu/handler.clj b/src/clj/yenu/handler.clj index 6b30711..99df19e 100644 --- a/src/clj/yenu/handler.clj +++ b/src/clj/yenu/handler.clj @@ -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 diff --git a/src/clj/yenu/layout.clj b/src/clj/yenu/layout.clj index 598d424..fb30e21 100644 --- a/src/clj/yenu/layout.clj +++ b/src/clj/yenu/layout.clj @@ -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 diff --git a/src/clj/yenu/middleware.clj b/src/clj/yenu/middleware.clj index bce0d00..bb4f57f 100644 --- a/src/clj/yenu/middleware.clj +++ b/src/clj/yenu/middleware.clj @@ -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)) diff --git a/src/clj/yenu/routes/admin.clj b/src/clj/yenu/routes/admin.clj index af11800..04a5653 100644 --- a/src/clj/yenu/routes/admin.clj +++ b/src/clj/yenu/routes/admin.clj @@ -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"))) diff --git a/src/clj/yenu/routes/auth.clj b/src/clj/yenu/routes/auth.clj new file mode 100644 index 0000000..c2af0f2 --- /dev/null +++ b/src/clj/yenu/routes/auth.clj @@ -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!)) diff --git a/src/clj/yenu/routes/core.clj b/src/clj/yenu/routes/core.clj index d1b1268..a168fbe 100644 --- a/src/clj/yenu/routes/core.clj +++ b/src/clj/yenu/routes/core.clj @@ -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)))