Skip to content

Commit e1488af

Browse files
committed
enforce access-question on anonymous page edits
This commit causes an access-question form field to appear on the edit page for anonymous (logged out) users when require-authentication = none and the access-question variables are non-empty. If the field is present, and if the wrong answer is provided by the user, then the user is returned to the edit page with an error message "Access code is invalid.". This new form field behaves in the same way as the access-question form field on the unauthenticated registration page.
1 parent 1b1f598 commit e1488af

File tree

3 files changed

+70
-37
lines changed

3 files changed

+70
-37
lines changed

README.markdown

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -326,14 +326,26 @@ setting of `math` in the configuration file:
326326
Restricting access
327327
------------------
328328

329-
If you want to limit account creation on your wiki, the easiest way to do this
330-
is to provide an `access-question` in your configuration file. (See the commented
331-
default configuration file.) Nobody will be able to create an account without
332-
knowing the answer to the access question.
329+
If you want to limit account creation or anonymous edits on your wiki, the
330+
easiest way to do this is to provide an `access-question` in your configuration
331+
file (see the commented default configuration file). Nobody will be able to
332+
create an account or make an anonymous edit without knowing the answer to the
333+
access question. Note that anonymous edits are only enabled when
334+
`require-authentication: none`, otherwise `access-question` only manifests itself
335+
in account creation.
333336

334337
Another approach is to use HTTP authentication. (See the config file comments on
335338
`authentication-method`.)
336339

340+
If you only want to restrict bots from creating accounts or editing pages
341+
anonymously, you can use one or more of the following methods:
342+
343+
1. Use the reCAPTCHA service to stop bots from creating accounts (see
344+
`use-recaptcha` configuraiton variable). Currently, `use-recaptcha` does not
345+
restrict anonymous edits.
346+
2. Use the `access-question` configuration variables to frame an access question
347+
that only humans can answer.
348+
337349
Authentication through github
338350
-----------------------------
339351

data/default.conf

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,12 +196,17 @@ recaptcha-public-key:
196196
access-question:
197197
access-question-answers:
198198
# specifies a question that users must answer when they attempt to create
199-
# an account, along with a comma-separated list of acceptable answers.
200-
# This can be used to institute a rudimentary password for signing up as
201-
# a user on the wiki, or as an alternative to reCAPTCHA.
199+
# an account or edit a page anonymously, along with a comma-separated list
200+
# of acceptable answers. This can be used to institute a rudimentary
201+
# password for signing up as a user on the wiki, or as an alternative to
202+
# reCAPTCHA.
202203
# Example:
203204
# access-question: What is the code given to you by Ms. X?
204205
# access-question-answers: RED DOG, red dog
206+
# Another example which shows how it could be used as an alternative to
207+
# reCAPTCHA:
208+
# access-question: how many legs in a tripod?
209+
# access-question-answers: three, Three, 3
205210

206211
rpx-domain:
207212
rpx-key:

src/Network/Gitit/Handlers.hs

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ import qualified Control.Exception as E
6767
import System.FilePath
6868
import Network.Gitit.State
6969
import Text.XHtml hiding ( (</>), dir, method, password, rev )
70-
import qualified Text.XHtml as X ( method )
70+
import qualified Text.XHtml as X ( method, password )
7171
import Data.List (intercalate, intersperse, delete, nub, sortBy, find, isPrefixOf, inits, sort, (\\))
7272
import Data.List.Split (wordsBy)
7373
import Data.Maybe (fromMaybe, mapMaybe, isJust, catMaybes)
@@ -501,6 +501,7 @@ editPage' params = do
501501
fs <- getFileStore
502502
page <- getPage
503503
cfg <- getConfig
504+
mbUser <- getLoggedInUser
504505
let getRevisionAndText = E.catch
505506
(do c <- liftIO $ retrieve fs (pathForPage page $ defaultExtension cfg) rev
506507
-- even if pRevision is set, we return revId of latest
@@ -529,12 +530,19 @@ editPage' params = do
529530
then [strAttr "readonly" "yes",
530531
strAttr "style" "color: gray"]
531532
else []
533+
let accessQ = case mbUser of
534+
Just _ -> noHtml
535+
Nothing -> case accessQuestion cfg of
536+
Nothing -> noHtml
537+
Just (prompt, _) -> label ! [thefor "accessCode"] << prompt +++ br +++
538+
X.password "accessCode" +++ br
532539
base' <- getWikiBase
533540
let editForm = gui (base' ++ urlForPage page) ! [identifier "editform"] <<
534541
[ sha1Box
535542
, textarea ! (readonly ++ [cols "80", name "editedText",
536543
identifier "editedText"]) << raw
537544
, br
545+
, accessQ
538546
, label ! [thefor "logMsg"] << "Description of changes:"
539547
, br
540548
, textfield "logMsg" ! (readonly ++ [value (logMsg `orIfNull` defaultSummary cfg) ])
@@ -630,39 +638,47 @@ updatePage = withData $ \(params :: Params) -> do
630638
Just b -> applyPreCommitPlugins b
631639
let logMsg = pLogMsg params `orIfNull` defaultSummary cfg
632640
let oldSHA1 = pSHA1 params
641+
let accessCode = pAccessCode params
642+
let isValidAccessCode = case mbUser of
643+
Just _ -> True
644+
Nothing -> case accessQuestion cfg of
645+
Nothing -> True
646+
Just (_, answers) -> accessCode `elem` answers
633647
fs <- getFileStore
634648
base' <- getWikiBase
635649
if null . filter (not . isSpace) $ logMsg
636650
then withMessages ["Description cannot be empty."] editPage
637-
else do
638-
when (length editedText > fromIntegral (maxPageSize cfg)) $
639-
error "Page exceeds maximum size."
640-
-- check SHA1 in case page has been modified, merge
641-
modifyRes <- if null oldSHA1
642-
then liftIO $ create fs (pathForPage page $ defaultExtension cfg)
643-
(Author user email) logMsg editedText >>
644-
return (Right ())
645-
else do
646-
expireCachedFile (pathForPage page $ defaultExtension cfg) `mplus` return ()
647-
liftIO $ E.catch (modify fs (pathForPage page $ defaultExtension cfg)
648-
oldSHA1 (Author user email) logMsg
649-
editedText)
650-
(\e -> if e == Unchanged
651-
then return (Right ())
652-
else E.throwIO e)
653-
case modifyRes of
654-
Right () -> seeOther (base' ++ urlForPage page) $ toResponse $ p << "Page updated"
655-
Left (MergeInfo mergedWithRev conflicts mergedText) -> do
656-
let mergeMsg = "The page has been edited since you checked it out. " ++
657-
"Changes from revision " ++ revId mergedWithRev ++
658-
" have been merged into your edits below. " ++
659-
if conflicts
660-
then "Please resolve conflicts and Save."
661-
else "Please review and Save."
662-
editPage' $
663-
params{ pEditedText = Just mergedText,
664-
pSHA1 = revId mergedWithRev,
665-
pMessages = [mergeMsg] }
651+
else if not isValidAccessCode
652+
then withMessages ["Access code is invalid."] editPage
653+
else do
654+
when (length editedText > fromIntegral (maxPageSize cfg)) $
655+
error "Page exceeds maximum size."
656+
-- check SHA1 in case page has been modified, merge
657+
modifyRes <- if null oldSHA1
658+
then liftIO $ create fs (pathForPage page $ defaultExtension cfg)
659+
(Author user email) logMsg editedText >>
660+
return (Right ())
661+
else do
662+
expireCachedFile (pathForPage page $ defaultExtension cfg) `mplus` return ()
663+
liftIO $ E.catch (modify fs (pathForPage page $ defaultExtension cfg)
664+
oldSHA1 (Author user email) logMsg
665+
editedText)
666+
(\e -> if e == Unchanged
667+
then return (Right ())
668+
else E.throwIO e)
669+
case modifyRes of
670+
Right () -> seeOther (base' ++ urlForPage page) $ toResponse $ p << "Page updated"
671+
Left (MergeInfo mergedWithRev conflicts mergedText) -> do
672+
let mergeMsg = "The page has been edited since you checked it out. " ++
673+
"Changes from revision " ++ revId mergedWithRev ++
674+
" have been merged into your edits below. " ++
675+
if conflicts
676+
then "Please resolve conflicts and Save."
677+
else "Please review and Save."
678+
editPage' $
679+
params{ pEditedText = Just mergedText,
680+
pSHA1 = revId mergedWithRev,
681+
pMessages = [mergeMsg] }
666682

667683
indexPage :: Handler
668684
indexPage = do

0 commit comments

Comments
 (0)