Skip to content

Instantly share code, notes, and snippets.

@hdenk
Created February 8, 2011 03:37
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hdenk/815806 to your computer and use it in GitHub Desktop.
Save hdenk/815806 to your computer and use it in GitHub Desktop.
clojure webapp with http-authentication using ring/moustache, deployable as foo.war
// gradle-build that uses meikel brandmeyers clojure-plugin for gradle (clojuresque)
// see http://bitbucket.org/kotarak/clojuresque
// use a standard maven directory-layout -> /src/main/clojure, /src/main/webapp
apply plugin: 'clojure'
apply plugin: 'war'
apply plugin: 'project-report'
warnOnReflection = false
aotCompile = true // needed for gen-class in servlet.clj
repositories {
clojureReleasesRepo() // <- clojure-system
clojarsRepo() // <- ring, moustache
mavenCentral() // <- jetty et al.
}
dependencies {
compile 'org.clojure:clojure:1.2.0'
compile 'org.clojure:clojure-contrib:1.2.0'
compile 'ring:ring:0.3.5'
// workaround gradle-issue 1303 using client-module
compile module('net.cgrand:moustache:1.0.0') {
dependency 'org.clojure:clojure:1.2.0'
//dependency 'ring:ring-core:0.3.5'
}
providedCompile 'javax.servlet:servlet-api:2.5@jar'
providedCompile 'ring:ring-jetty-adapter:0.3.5@jar'
providedRuntime 'org.mortbay.jetty:jetty:6.1.14@jar'
providedRuntime 'org.mortbay.jetty:jetty-util:6.1.14@jar'
}
task explodedWar(type: Copy) {
from war.rootSpec // reuses the declaration from the war task
into new File(buildDir, 'exploded-war')
}
(ns webapp.core
(:use
net.cgrand.moustache
ring.util.response
[ring.middleware file params session]
ring.adapter.jetty))
; http://localhost:8080/foo -> home
; http://localhost:8080/foo/bar -> home (err=not-logged-in)
; http://localhost:8080/foo/login?user=doe&password=test -> bar: doe
; http://localhost:8080/foo/admin -> home (err=not-admin)
; http://localhost:8080/foo/login?user=admin&password=secret -> bar: admin
; http://localhost:8080/foo/admin -> admin
; http://localhost:8080/foo/logout -> home
(defn authenticate-user
[{params :params}]
(let [user (params "user") password (params "password")]
(if (or (and (= user "doe") (= password "test"))
(and (= user "admin") (= password "secret")))
(assoc (redirect "/foo/bar") :session {:user user})
(redirect "/foo?err=login-error"))))
(defn authenticated?
[session]
(seq (:user session)))
(defn admin-user?
[session]
(= "admin" (:user session)))
(defn wrap-user-security
[app]
(fn [req]
(let [uri (:uri req) session (:session req)]
(if (or (authenticated? session)
(.startsWith uri "/css/") ; *.css
(.startsWith uri "/scripts/")) ; *.js
(app req)
(redirect "/foo?err=not-logged-in")))))
(defn wrap-admin-security
[app]
(fn [req]
(let [session (:session req)]
(if (admin-user? session)
(app req)
(redirect "/foo?err=not-admin")))))
(defn logout
[req]
(assoc (redirect "/foo")
:session nil))
(defn page-not-found [_]
{:status 404 :headers {"Content-Type" "text/html"}
:body "<html><body><p>page not found</p></body></html>"})
(defn home_handler [req]
(response "<html><body><p>home</p></body></html>"))
(defn bar_handler [req]
(response (str "<html><body><p>bar: " (-> req :session :user) "</p></body></html>")))
(defn admin_handler [req]
(response "<html><body><p>admin</p></body></html>"))
; using composition, url-mapping und access-control
; can be implemented in a quite elegant way
(def routes
(app
(wrap-session)
["foo"] home_handler
["foo" "login"] (wrap-params authenticate-user)
["foo" &] (app
(wrap-user-security)
; switch wrap-file before building the war !
;(wrap-file "/path-to-your-webapps-dir/webapps/foo")
(wrap-file "src/main/webapp")
["bar"] bar_handler
["logout"] logout
["admin" &] (app
(wrap-admin-security)
[""] admin_handler
[&] pass)
[&] pass)
[&] page-not-found))
; comment the following line and switch (wrap-file *)
; before building the war with cmd: gradle clean war
(doto (Thread. #(run-jetty #'routes {:port 8080})) .start)
(ns webapp.servlet
(:use [ring.util.servlet :only (defservice)])
(:use [ring.middleware.stacktrace :only (wrap-stacktrace)])
(:use [webapp.core :only (routes)])
(:gen-class :extends javax.servlet.http.HttpServlet))
(def app
(-> #'routes
(wrap-stacktrace)))
(defservice app)
<web-app>
<display-name>foo</display-name>
<servlet>
<servlet-name>foo</servlet-name>
<servlet-class>webapp.servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.csv</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.gif</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.ico</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>foo</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
@hdenk
Copy link
Author

hdenk commented Feb 10, 2011

Links

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment