packages feed

pi-hoole-0.1.0.0: app/web/Main.hs

--  pi-hoole: lightweight access-control for pijul
--  Copyright (C) 2018 Thomas Letan <contact@thomasletan.fr>
--
--  This program is free software: you can redistribute it and/or modify
--  it under the terms of the GNU Affero General Public License as published
--  by the Free Software Foundation, either version 3 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU Affero General Public License for more details.
--
--  You should have received a copy of the GNU Affero General Public License
--  along with this program.  If not, see <http://www.gnu.org/licenses/>.

{-# LANGUAGE LambdaCase        #-}
{-# LANGUAGE OverloadedStrings #-}

module Main where

import           Control.Applicative       ((<|>))
import           Data.ByteString.Base58    (bitcoinAlphabet, decodeBase58)
import           Data.Map.Strict           (Map, (!?))
import           Data.Maybe                (fromJust, maybe)
import           Data.String               (fromString)
import           Data.Text                 (Text, pack, unpack)
import           Data.Text.Encoding        (decodeUtf8)
import           Data.Yaml                 (decodeFile)
import           Network.HTTP.Types.Method (methodGet)
import           Network.HTTP.Types.Status (status200, status404)
import           Network.Wai               (Application, Request, pathInfo,
                                            requestMethod, responseLBS)
import           Network.Wai.Handler.Warp  (run)
import           PiHoole                   (Action (Log, Patch), Branch (..),
                                            Configuration (repositories),
                                            Pijul (..), Privilege, Repo (..),
                                            Role (Anon), privilegeLe,
                                            recordPijul, requires)
import           System.Directory          (XdgDirectory (..), getXdgDirectory)
import           System.FilePath           (FilePath, (</>))
import           Text.Regex.PCRE           ((=~))

main :: IO ()
main = getConfiguration >>= \case
  Just cfg -> run 8080 (piHooleWeb $ repositories cfg)
  Nothing -> putStrLn "Could not open or parse the configuration file"

  where piHooleWeb :: Map Repo (Map Role Privilege) -> Application
        piHooleWeb cfg req respond =
          let action = requestToPijul req in
          if maybe False (pijulToPrivileges cfg) action
          then do res <- recordPijul (fromJust action)
                  respond $ responseLBS status200 [] (fromString res)
          else do print req
                  respond $ responseLBS status404 [] "Sorry"

pijulToPrivileges :: Map Repo (Map Role Privilege) -> Pijul -> Bool
pijulToPrivileges cfg (Pijul repo action) =
  case cfg !? repo >>= (!? Anon) of
    Just priv -> requires action `privilegeLe` priv
    _         -> False

requestToPijul :: Request -> Maybe Pijul
requestToPijul req
  | isGet req = let path = pathInfo req
                in log path <|> patch path
  | otherwise = Nothing
  where
    log :: [Text] -> Maybe Pijul
    log = logAux ""

    logAux :: FilePath -> [Text] -> Maybe Pijul
    logAux path [".pijul", branch] = Pijul (Repo path) <$> (Log <$> parseBranch branch)
    logAux path (x:rst)  = logAux (path </> unpack x) rst
    logAux _ _ = Nothing

    parseBranch :: Text -> Maybe Branch
    parseBranch branch = case unpack branch =~ changesRegex of
      [[_, branch58]] -> Branch . decodeUtf8 <$> decodeBase58 bitcoinAlphabet (fromString branch58)
      _               -> Nothing

    changesRegex :: String
    changesRegex = "changes\\.(.*)$"

    patch :: [Text] -> Maybe Pijul
    patch = patchAux ""

    patchAux :: FilePath -> [Text] -> Maybe Pijul
    patchAux path [".pijul", "patches", patch] = Pijul (Repo path) <$> (Patch <$> parsePatch patch)
    patchAux path (x:rst)  = patchAux (path </> unpack x) rst
    patchAux _ _ = Nothing

    parsePatch :: Text -> Maybe Text
    parsePatch patch = case unpack patch =~ patchRegex of
      [[_, patch]] -> Just $ pack patch
      _            -> Nothing

    patchRegex :: String
    patchRegex = "(.*)\\.gz"

    isGet :: Request -> Bool
    isGet = (== methodGet) . requestMethod

getConfiguration :: IO (Maybe Configuration)
getConfiguration = getXdgDirectory XdgConfig "pi-hoole/config.yaml" >>= decodeFile