From c76c82177041bb77de96343c1cebd7e3198b0110 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Sat, 9 Sep 2017 21:50:54 +0100 Subject: [PATCH] Version 0.99.9-SNAPSHOT --- project.clj | 3 +- resources/public/content/Deploying Smeagol.md | 16 ++-- .../public/content/Developing Smeagol.md | 4 +- src/smeagol/configuration.clj | 5 +- src/smeagol/handler.clj | 81 ++++++++++--------- src/smeagol/layout.clj | 45 +++++++---- src/smeagol/middleware.clj | 6 +- src/smeagol/routes/wiki.clj | 78 +++++++++--------- src/smeagol/util.clj | 6 +- 9 files changed, 135 insertions(+), 109 deletions(-) diff --git a/project.clj b/project.clj index f933a28..d564801 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject smeagol "0.99.8" +(defproject smeagol "0.99.9-SNAPSHOT" :description "A simple Git-backed Wiki inspired by Gollum" :url "https://github.com/simon-brooke/smeagol" :license {:name "GNU General Public License,version 2.0 or (at your option) any later version" @@ -14,6 +14,7 @@ [com.taoensso/tower "3.0.2" :exclusions [com.taoensso/encore]] [crypto-password "0.2.0"] [environ "1.1.0"] + [hiccup "1.0.5"] [im.chit/cronj "1.4.4"] [lib-noir "0.9.9" :exclusions [org.clojure/tools.reader]] [markdown-clj "0.9.99" :exclusions [com.keminglabs/cljx]] diff --git a/resources/public/content/Deploying Smeagol.md b/resources/public/content/Deploying Smeagol.md index 3395fd4..c41d526 100644 --- a/resources/public/content/Deploying Smeagol.md +++ b/resources/public/content/Deploying Smeagol.md @@ -3,25 +3,25 @@ To deploy Smeagol as a stand-alone application, either download the jar file for lein bower install lein ring uberjar - -This will create a jar file in the `target` directory, named `smeagol-`*VERSION*`-standalone.jar`. -Smeagol cannot access either its configuration or its content from the jar file. Consequently you should set up three environment variables: +This will create a jar file in the `target` directory, named `smeagol-`*VERSION*`-standalone.jar`. -1. **SMEAGOL_CONFIG** should be the full or relative pathname of a Smeagol [[Configuration]] file; -2. **SMEAGOL\_CONTENT\_DIR** should be the full or relative pathname of the directory from which Smeagol should serve content (which may initially be empty, but must be writable by the process which runs Smeagol)' -3. **SMEAGOL_PASSWD** should be the full or relative pathname of a Smeagol Passwd file - see [[Security and authentication]]. This file must contain an entry for at least your initial user, and, if you want to administer users through the user interface, must be writable by the process which runs Smeagol); +Smeagol cannot access either its configuration or its content from the jar file, as otherwise they would not be editable. Consequently you should set up three environment variables: + +1. `SMEAGOL_CONFIG` should be the full or relative pathname of a Smeagol [[Configuration]] file; +2. `SMEAGOL_CONTENT_DIR` should be the full or relative pathname of the directory from which Smeagol should serve content (which may initially be empty, but must be writable by the process which runs Smeagol)' +3. `SMEAGOL_PASSWD` should be the full or relative pathname of a Smeagol Passwd file - see [[Security and authentication]]. This file must contain an entry for at least your initial user, and, if you want to administer users through the user interface, must be writable by the process which runs Smeagol; You can run the jar file with: java -jar smeagol-VERSION-standalone.jar - + ## Deploying within a servlet container To deploy Smeagol within a servlet container, either download the jar file for the release you want to deploy, or clone the source and compile it with: lein bower install lein ring uberwar - + This will create a war file in the `target` directory, named `smeagol-`*VERSION*`-standalone.war`. Deploy this to your servlet container in the normal way; details will depend on your container. Instructions for Tomcat are [here](https://tomcat.apache.org/tomcat-8.0-doc/deployer-howto.html). The problem with this is that unless the environment variables (see above) were already set up in the environment of the servlet container at the time when the servlet container were launched, Smeagol will run with its built-in defaults. This will run perfectly satisfactorily provided your servlet container is configured to unpack war files, which most are. diff --git a/resources/public/content/Developing Smeagol.md b/resources/public/content/Developing Smeagol.md index 2c13447..79f40d5 100644 --- a/resources/public/content/Developing Smeagol.md +++ b/resources/public/content/Developing Smeagol.md @@ -9,11 +9,11 @@ To start a web server for the application during development, run: lein bower install lein ring server - + This should start a development server, and open a new window or tab in your default browser with the default page of the wiki loaded into it. ## Editing -I generally use [LightTable]() as my `Clojure` editor, but it doesn't really matter what you use; if you run Smeagol as described above, then all changes you make in the code (and save) will instantly be applied to the running system. This makes for a productive development environment. +I generally use [LightTable](http://lighttable.com/) as my `Clojure` editor, but it doesn't really matter what you use; if you run Smeagol as described above, then all changes you make in the code (and save) will instantly be applied to the running system. This makes for a productive development environment. ## Documentation It is my intention that the code should be sufficiently well documented to be easy to understand. Documentation may be generated from the code by running diff --git a/src/smeagol/configuration.clj b/src/smeagol/configuration.clj index 32be1b4..b207081 100644 --- a/src/smeagol/configuration.clj +++ b/src/smeagol/configuration.clj @@ -46,4 +46,7 @@ (def config "The actual configuration, as a map." - (read-string (slurp config-file-path))) + (try + (read-string (slurp config-file-path)) + (catch Exception any + (throw (Exception. "Could not load configuration" any))))) diff --git a/src/smeagol/handler.clj b/src/smeagol/handler.clj index 9cfd6a0..2b2c0f9 100644 --- a/src/smeagol/handler.clj +++ b/src/smeagol/handler.clj @@ -1,7 +1,8 @@ (ns ^{:doc "Set up, configure, and clean up after the wiki server." :author "Simon Brooke"} smeagol.handler - (:require [compojure.core :refer [defroutes]] + (:require [clojure.java.io :as cjio] + [compojure.core :refer [defroutes]] [compojure.route :as route] [cronj.core :as cronj] [environ.core :refer [env]] @@ -46,23 +47,6 @@ (route/resources "/") (route/not-found "Not Found")) -(defn init - "init will be called once when - app is deployed as a servlet on - an app server such as Tomcat - put any initialization code here" - [] - (timbre/merge-config! - {:appenders - {:rotor (rotor/rotor-appender - {:path "smeagol.log" - :max-size (* 512 1024) - :backlog 10})}}) - (if (env :dev) (parser/cache-off!)) - ;;start the expired session cleanup job - (cronj/start! session-manager/cleanup-job) - (timbre/info "\n-=[ smeagol started successfully" - (when (env :dev) "using the development profile") "]=-")) (defn destroy "destroy will be called when your application @@ -72,28 +56,53 @@ (cronj/shutdown! session-manager/cleanup-job) (timbre/info "shutdown complete!")) + +(defn init + "init will be called once when + app is deployed as a servlet on + an app server such as Tomcat + put any initialization code here" + [] + (try + (timbre/merge-config! + {:appenders + {:rotor (rotor/rotor-appender + {:path "smeagol.log" + :max-size (* 512 1024) + :backlog 10})}}) + (cronj/start! session-manager/cleanup-job) + (if (env :dev) (parser/cache-off!)) + ;;start the expired session cleanup job + (timbre/info "\n-=[ smeagol started successfully" + (when (env :dev) "using the development profile") "]=-") + (catch Exception any + (timbre/error "Failure during startup" any) + (destroy)))) + ;; timeout sessions after 30 minutes (def session-defaults {:timeout (* 60 30) :timeout-response (redirect "/")}) -(defn- mk-defaults - "set to true to enable XSS protection" - [xss-protection?] - (-> site-defaults - (update-in [:session] merge session-defaults) - (assoc-in [:security :anti-forgery] xss-protection?))) + +(defn- make-defaults + "set to true to enable XSS protection" + [xss-protection?] + (-> site-defaults + (update-in [:session] merge session-defaults) + (assoc-in [:security :anti-forgery] xss-protection?))) + (def app (app-handler - ;; add your application routes here - [wiki-routes base-routes] - ;; add custom middleware here - :middleware (load-middleware) - :ring-defaults (mk-defaults false) - ;; add access rules here - :access-rules [{:redirect "/auth" - :rule user-access}] - ;; serialize/deserialize the following data formats - ;; available formats: - ;; :json :json-kw :yaml :yaml-kw :edn :yaml-in-html - :formats [:json-kw :edn :transit-json])) + ;; add your application routes here + [wiki-routes base-routes] + ;; add custom middleware here + :middleware (load-middleware) + :ring-defaults (make-defaults true) + ;; add access rules here + :access-rules [{:redirect "/auth" + :rule user-access}] + ;; serialize/deserialize the following data formats + ;; available formats: + ;; :json :json-kw :yaml :yaml-kw :edn :yaml-in-html + :formats [:json-kw :edn :transit-json])) diff --git a/src/smeagol/layout.clj b/src/smeagol/layout.clj index 09aabd7..34a7d0a 100644 --- a/src/smeagol/layout.clj +++ b/src/smeagol/layout.clj @@ -2,12 +2,16 @@ (ns ^{:doc "Render a page as HTML." :author "Simon Brooke"} smeagol.layout - (:require [clojure.string :as s] + (:require [clojure.java.io :as cjio] + [clojure.string :as s] [compojure.response :refer [Renderable]] [environ.core :refer [env]] + [hiccup.core :refer [html]] [ring.util.anti-forgery :refer [anti-forgery-field]] [ring.util.response :refer [content-type response]] [selmer.parser :as parser] + [smeagol.configuration :refer [config]] + [smeagol.sanity :refer :all] [smeagol.util :as util] [taoensso.timbre :as timbre])) @@ -51,23 +55,30 @@ (deftype RenderableTemplate [template params] Renderable (render [this request] - (content-type - (->> (assoc params - (keyword (s/replace template #".html" "-selected")) "active" - :i18n (util/get-messages request) - :dev (env :dev) - :servlet-context - (if-let [context (:servlet-context request)] - ;; If we're not inside a serlvet environment (for - ;; example when using mock requests), then - ;; .getContextPath might not exist - (try (.getContextPath context) + (try + (content-type + (->> (assoc params + (keyword (s/replace template #".html" "-selected")) "active" + :i18n (util/get-messages request) + :dev (env :dev) + :servlet-context + (if-let [context (:servlet-context request)] + ;; If we're not inside a serlvet environment (for + ;; example when using mock requests), then + ;; .getContextPath might not exist + (try (.getContextPath context) (catch IllegalArgumentException _ context)))) - (parser/render-file (str template-path template)) - response) - "text/html; charset=utf-8"))) + (parser/render-file (str template-path template)) + response) + "text/html; charset=utf-8") + (catch Exception any + (show-sanity-check-error any))))) -(defn render [template & [params]] - (RenderableTemplate. template params)) +(defn render + [template & [params]] + (try + (RenderableTemplate. template params) + (catch Exception any + (show-sanity-check-error any)))) diff --git a/src/smeagol/middleware.clj b/src/smeagol/middleware.clj index ecf835c..82ccb59 100644 --- a/src/smeagol/middleware.clj +++ b/src/smeagol/middleware.clj @@ -40,13 +40,11 @@ (def development-middleware [wrap-error-page - wrap-exceptions - wrap-anti-forgery]) + wrap-exceptions]) (def production-middleware - [#(wrap-internal-error % :log (fn [e] (timbre/error e))) - wrap-anti-forgery]) + [#(wrap-internal-error % :log (fn [e] (timbre/error e)))]) (defn load-middleware [] diff --git a/src/smeagol/routes/wiki.clj b/src/smeagol/routes/wiki.clj index 810bd5d..6b2e219 100644 --- a/src/smeagol/routes/wiki.clj +++ b/src/smeagol/routes/wiki.clj @@ -17,6 +17,7 @@ [smeagol.history :as hist] [smeagol.layout :as layout] [smeagol.routes.admin :as admin] + [smeagol.sanity :refer [show-sanity-check-error]] [smeagol.util :as util] [smeagol.uploads :as ul] [taoensso.timbre :as timbre])) @@ -83,6 +84,7 @@ ([request] (edit-page request (util/get-message :default-page-title request) ".md" "edit.html" "_edit-side-bar.md")) ([request default suffix template side-bar] + (show-sanity-check-error) (let [params (keywordize-keys (:params request)) src-text (:src params) page (or (:page params) default) @@ -113,21 +115,23 @@ (defn wiki-page "Render the markdown page specified in this `request`, if any. If none found, redirect to edit-page" [request] - (let [params (keywordize-keys (:params request)) - page (or (:page params) (util/get-message :default-page-title "Introduction" request)) - file-name (str page ".md") - file-path (cjio/file util/content-dir file-name) - exists? (.exists (clojure.java.io/as-file file-path))] - (cond exists? - (do - (timbre/info (format "Showing page '%s' from file '%s'" page file-path)) - (layout/render "wiki.html" - (merge (util/standard-params request) - {:title page - :page page - :content (md->html (slurp file-path)) - :editable true}))) - true (response/redirect (str "/edit?page=" page))))) + (or + (show-sanity-check-error) + (let [params (keywordize-keys (:params request)) + page (or (:page params) (util/get-message :default-page-title "Introduction" request)) + file-name (str page ".md") + file-path (cjio/file util/content-dir file-name) + exists? (.exists (clojure.java.io/as-file file-path))] + (cond exists? + (do + (timbre/info (format "Showing page '%s' from file '%s'" page file-path)) + (layout/render "wiki.html" + (merge (util/standard-params request) + {:title page + :page page + :content (md->html (slurp file-path)) + :editable true}))) + true (response/redirect (str "/edit?page=" page)))))) (defn history-page @@ -202,27 +206,29 @@ (defn auth-page "Render the auth page" [request] - (let [params (keywordize-keys (:form-params request)) - username (:username params) - password (:password params) - action (:action params) - user (session/get :user) - redirect-to (or (:redirect-to params) "/wiki")] - (cond - (= action (util/get-message :logout-label request)) - (do - (timbre/info (str "User " user " logging out")) - (session/remove! :user) - (response/redirect redirect-to)) - (and username password (auth/authenticate username password)) - (do - (session/put! :user username) - (response/redirect redirect-to)) - true - (layout/render "auth.html" - (merge (util/standard-params request) - {:title (if user (str (util/get-message :logout-link request) " " user) (util/get-message :login-link request)) - :redirect-to ((:headers request) "referer")}))))) + (or + (show-sanity-check-error) + (let [params (keywordize-keys (:form-params request)) + username (:username params) + password (:password params) + action (:action params) + user (session/get :user) + redirect-to (or (:redirect-to params) "/wiki")] + (cond + (= action (util/get-message :logout-label request)) + (do + (timbre/info (str "User " user " logging out")) + (session/remove! :user) + (response/redirect redirect-to)) + (and username password (auth/authenticate username password)) + (do + (session/put! :user username) + (response/redirect redirect-to)) + true + (layout/render "auth.html" + (merge (util/standard-params request) + {:title (if user (str (util/get-message :logout-link request) " " user) (util/get-message :login-link request)) + :redirect-to ((:headers request) "referer")})))))) (defn passwd-page diff --git a/src/smeagol/util.clj b/src/smeagol/util.clj index 2059881..69cce68 100644 --- a/src/smeagol/util.clj +++ b/src/smeagol/util.clj @@ -51,18 +51,16 @@ :version (System/getProperty "smeagol.version")})) -(defn raw-get-messages +(defn- raw-get-messages "Return the most acceptable messages collection we have given the `Accept-Language` header in this `request`." [request] (merge (i18n/get-messages ((:headers request) "accept-language") -;; (cjio/file (io/resource-path) "i18n") "i18n" "en-GB") - config) - ) + config)) (def get-messages (memoize raw-get-messages))