Add authentification and authorisation
This commit is contained in:
parent
421acedc8b
commit
81fe574b7b
13 changed files with 147 additions and 27 deletions
2
env/dev/resources/config.edn
vendored
2
env/dev/resources/config.edn
vendored
|
@ -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}
|
||||||
|
|
2
env/prod/resources/config.edn
vendored
2
env/prod/resources/config.edn
vendored
|
@ -1,2 +1,4 @@
|
||||||
{:production true
|
{:production true
|
||||||
|
:creator-password "xxx"
|
||||||
|
:user-password "yyy"
|
||||||
:port 3000}
|
:port 3000}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
<hr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="clearfix">
|
<div class="clearfix">
|
||||||
|
|
|
@ -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" %}
|
||||||
|
|
26
resources/templates/login.html
Normal file
26
resources/templates/login.html
Normal 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 %}
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")))
|
||||||
|
|
30
src/clj/yenu/routes/auth.clj
Normal file
30
src/clj/yenu/routes/auth.clj
Normal 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!))
|
|
@ -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")))
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue