gonito/Handler/YourAccount.hs

178 lines
7.4 KiB
Haskell
Raw Normal View History

2015-09-30 20:15:33 +02:00
module Handler.YourAccount where
import Import
2016-12-03 13:36:32 +01:00
import Yesod.Form.Bootstrap3 (BootstrapFormLayout (..), renderBootstrap3, bfs)
2015-09-30 20:15:33 +02:00
2016-05-03 10:21:40 +02:00
import Data.Conduit.Binary
import qualified Data.ByteString as S
import qualified Data.ByteString.Lazy as L
2017-02-18 10:26:02 +01:00
2022-01-19 10:48:38 +01:00
import System.Directory
import System.Process
import System.IO
import Handler.Common (passwordConfirmField, updatePassword, isPasswordAcceptable, tooWeakPasswordMessage)
import Handler.Shared
2015-09-30 20:15:33 +02:00
getYourAccountR :: Handler Html
getYourAccountR = do
userId <- requireAuthId
user <- runDB $ get404 userId
2017-09-28 11:29:48 +02:00
enableTriggerToken userId (userTriggerToken user)
2022-01-19 10:48:38 +01:00
mIndividualKey <- fetchIndividualKey $ userLocalId user
2015-11-11 22:37:25 +01:00
keyS <- runDB $ selectFirst [PublicKeyUser ==. userId] []
let key = publicKeyPubkey <$> entityVal <$> keyS
(formWidget, formEnctype) <- generateFormPost (yourAccountForm (userName user) (userLocalId user) key (userAltRepoScheme user) (userIsAnonymous user))
2015-09-30 20:15:33 +02:00
defaultLayout $ do
setTitle "Your account"
$(widgetFile "your-account")
2022-01-19 10:48:38 +01:00
fetchIndividualKey :: Maybe Text -> Handler (Maybe Text)
fetchIndividualKey Nothing = return Nothing
fetchIndividualKey (Just localId) = do
arenaDir <- arena
let individualKeysDir = arenaDir ++ "/individual-keys"
liftIO $ createDirectoryIfMissing True individualKeysDir
let individualKeyPath = (unpack individualKeysDir) ++ "/" ++ (unpack localId)
let individualComment = (unpack localId) ++ "@gonito"
let individualPubKeyPath = individualKeyPath ++ ".pub"
isKeyGenerated <- liftIO $ doesFileExist individualPubKeyPath
if not isKeyGenerated
then
do
2022-01-19 12:46:23 +01:00
_ <- liftIO $ callProcess "/usr/bin/ssh-keygen" ["-t", "RSA", "-f", individualKeyPath, "-N", "", "-C", individualComment]
2022-01-19 10:48:38 +01:00
return ()
else
return ()
fhandle <- liftIO $ openFile individualPubKeyPath ReadMode
contents <- liftIO $ System.IO.hGetContents fhandle
return $ Just $ pack contents
2015-09-30 20:15:33 +02:00
postYourAccountR :: Handler Html
postYourAccountR = do
((result, formWidget), formEnctype) <- runFormPost (yourAccountForm Nothing Nothing Nothing Nothing False)
userId <- requireAuthId
2016-12-03 15:19:11 +01:00
user <- runDB $ get404 userId
2017-09-28 11:29:48 +02:00
enableTriggerToken userId (userTriggerToken user)
2016-12-03 13:36:32 +01:00
let accountData = case result of
2015-09-30 20:15:33 +02:00
FormSuccess res -> Just res
_ -> Nothing
2022-01-19 12:46:23 +01:00
mIndividualKey <- case accountData of
Just (name, localId, mPassword, sshPubKey, mAltRepoScheme, avatarFile, anonimised) -> do
if checkPassword mPassword
then
2022-01-19 12:46:23 +01:00
do
mIndKey <- fetchIndividualKey localId
updateUserAccount userId name localId mPassword sshPubKey mAltRepoScheme avatarFile anonimised
2022-01-19 12:46:23 +01:00
return mIndKey
else
do
tooWeakPasswordMessage
return Nothing
Nothing -> do
setMessage $ toHtml ("Something went wrong, probably the password did not match" :: Text)
2022-01-19 12:46:23 +01:00
return Nothing
2015-09-30 20:15:33 +02:00
defaultLayout $ do
setTitle "Your account"
$(widgetFile "your-account")
checkPassword :: Maybe Text -> Bool
checkPassword Nothing = True
checkPassword (Just "") = True
checkPassword (Just passwd) = isPasswordAcceptable passwd
2015-09-30 20:15:33 +02:00
2018-09-01 12:01:35 +02:00
autocompleteOff :: (RenderMessage master msg2, RenderMessage master msg1) => msg1 -> msg2 -> FieldSettings master
2017-09-23 15:04:40 +02:00
autocompleteOff name tooltip = setts { fsAttrs = (fsAttrs setts) ++ [("autocomplete", "nope")]}
where setts = (bfs name) { fsTooltip = Just $ SomeMessage tooltip }
yourAccountForm :: Maybe Text -> Maybe Text -> Maybe Text -> Maybe Text -> Bool -> Form (Maybe Text, Maybe Text, Maybe Text, Maybe Text, Maybe Text, Maybe FileInfo, Bool)
yourAccountForm maybeName maybeLocalId maybeSshPubKey maybeAltRepoScheme anonimised = renderBootstrap3 BootstrapBasicForm $ (,,,,,,)
2017-09-23 15:04:40 +02:00
<$> aopt textField (fieldWithTooltip MsgAccountName MsgAccountNameTooltip) (Just maybeName)
<*> aopt textField (autocompleteOff MsgId MsgIdTooltip) (Just maybeLocalId)
2016-12-03 14:14:39 +01:00
<*> aopt passwordConfirmField (bfs MsgPassword) Nothing
2017-09-23 15:04:40 +02:00
<*> aopt textField (fieldWithTooltip MsgSshPubKey MsgSshPubKeyTooltip) (Just maybeSshPubKey)
<*> aopt textField (fieldWithTooltip MsgAltRepoScheme MsgAltRepoSchemeTooltip) (Just maybeAltRepoScheme)
2016-05-03 10:21:40 +02:00
<*> fileAFormOpt (bfs MsgAvatar)
2017-03-18 14:51:09 +01:00
<*> areq checkBoxField (bfs MsgWantToBeAnonimised) (Just anonimised)
updateUserAccount :: Key User -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe FileInfo -> Bool -> Handler ()
updateUserAccount userId name maybeLocalId maybePassword maybeSshPubKey maybeAltRepoScheme maybeAvatarFile anonimised = do
updateJustName userId name
2016-05-03 10:21:40 +02:00
updateAvatar userId maybeAvatarFile
updateLocalIdAndPubKey userId maybeLocalId maybeSshPubKey
2017-03-18 14:51:09 +01:00
updateAnonimity userId anonimised
updateAltRepoScheme userId maybeAltRepoScheme
case maybePassword of
Nothing -> return ()
Just "" -> return ()
Just p -> updatePassword userId (Just p)
2016-05-03 10:21:40 +02:00
updateAvatar :: Key User -> Maybe FileInfo -> Handler ()
updateAvatar _ Nothing = return ()
updateAvatar userId (Just avatarFile) = do
fileBytes <- runResourceT $ fileSource avatarFile $$ sinkLbs
runDB $ update userId [UserAvatar =. Just (S.pack . L.unpack $ fileBytes)]
updateLocalIdAndPubKey :: Key User -> Maybe Text -> Maybe Text -> Handler ()
updateLocalIdAndPubKey userId (Just localId) maybeSshPubKey = do
if isLocalIdAcceptable localId
then
do
otherTheSame <- runDB $ selectFirst [UserLocalId ==. (Just localId), UserId !=. userId] []
case otherTheSame of
Just _ -> do
setMessage $ toHtml ("ID already used" :: Text)
Nothing -> do
user <- runDB $ get404 userId
case userLocalId user of
Just prevLocalId -> do
unless (prevLocalId == localId) $ setMessage $ toHtml ("only the administrator can change your ID" :: Text)
2016-03-14 21:12:54 +01:00
Nothing -> do
runDB $ update userId [UserLocalId =. Just localId]
setMessage $ toHtml ("ID set" :: Text)
2015-11-11 22:37:25 +01:00
runDB $ deleteWhere [PublicKeyUser ==. userId]
case maybeSshPubKey of
Just key -> do
2016-12-03 13:36:32 +01:00
_ <- runDB $ insert $ PublicKey {
2015-11-11 22:37:25 +01:00
publicKeyUser=userId,
publicKeyPubkey=key }
2016-03-14 21:12:54 +01:00
setMessage $ toHtml ("SSH public key added; now it may take 10 minutes for the keys to be active, please be patient" :: Text)
2015-11-11 22:37:25 +01:00
return ()
Nothing -> return ()
else
setMessage $ toHtml ("unexpected ID (use only lower-case letters, digits and hyphens, start with a letter)" :: Text)
updateLocalIdAndPubKey _ Nothing (Just _) = do
setMessage $ toHtml ("SSH public key cannot be added without an ID" :: Text)
updateLocalIdAndPubKey _ Nothing Nothing = return ()
updateAltRepoScheme :: Key User -> Maybe Text -> Handler ()
updateAltRepoScheme userId mAltRepoScheme = runDB $ update userId [UserAltRepoScheme =. mAltRepoScheme]
updateJustName :: Key User -> Maybe Text -> Handler ()
updateJustName userId name = runDB $ update userId [UserName =. name]
2016-05-03 10:21:40 +02:00
2017-03-18 14:51:09 +01:00
updateAnonimity :: Key User -> Bool -> Handler ()
updateAnonimity userId anonimised = runDB $ update userId [UserIsAnonymous =. anonimised]
2016-05-03 10:21:40 +02:00
getAvatarR :: UserId -> Handler TypedContent
getAvatarR userId = do
user <- runDB $ get404 userId
case userAvatar user of
Just avatarBytes -> do
addHeader "Content-Disposition" "attachment; filename=\"avatar.png\""
sendResponse (typePng, toContent avatarBytes)
Nothing -> do
sendFile typeSvg "static/images/male-avatar.svg"