From 95d33f20f6976296e0d1855f409fc086fd06aa7e Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Mon, 23 Oct 2023 09:39:13 +0100 Subject: [PATCH] imp:web: access control UX cleanups (fix #834) Changes: 1. rename the sandstorm "manage" permission to "edit" (old permission names: view, add, manage; new permission names: view, add, edit). Rationale: "edit" best describes this permission's current powers, to users and to operators. If we ever added more manager-type features we'd want that to be a new permission, not a rename of the existing one (which would change the powers of existing users). 2. rename the sandstorm roles for consistency with permissions (old role names: viewer, editor, manager; new role names: viewer, adder, editor) Rationale: it's needed to avoid confusion. 3. add a new option: --allow=view|add|edit|sandstorm (default: add). 'sandstorm' sets permissions according to the X-Sandstorm-Permissions header. Drop the --capabilities and --capabilities-header options. Rationale: it's simpler and more intuitive. 4. replace "capability" with "permission" in ui/docs/code. Rationale: consistent with the above, more familiar. --- .sandstorm/sandstorm-pkgdef.capnp | 37 ++++---- hledger-web/Hledger/Web/Foundation.hs | 23 ++--- hledger-web/Hledger/Web/Handler/AddR.hs | 8 +- hledger-web/Hledger/Web/Handler/EditR.hs | 4 +- hledger-web/Hledger/Web/Handler/JournalR.hs | 4 +- hledger-web/Hledger/Web/Handler/MiscR.hs | 38 ++++----- hledger-web/Hledger/Web/Handler/RegisterR.hs | 4 +- hledger-web/Hledger/Web/Handler/UploadR.hs | 4 +- hledger-web/Hledger/Web/Import.hs | 2 +- hledger-web/Hledger/Web/WebOptions.hs | 88 +++++++++++--------- hledger-web/hledger-web.m4.md | 8 +- hledger-web/package.yaml | 1 + hledger-web/templates/default-layout.hamlet | 6 +- hledger-web/templates/journal.hamlet | 4 +- hledger-web/templates/register.hamlet | 2 +- 15 files changed, 120 insertions(+), 113 deletions(-) diff --git a/.sandstorm/sandstorm-pkgdef.capnp b/.sandstorm/sandstorm-pkgdef.capnp index a2d11feaf..19d88bdcb 100644 --- a/.sandstorm/sandstorm-pkgdef.capnp +++ b/.sandstorm/sandstorm-pkgdef.capnp @@ -54,7 +54,7 @@ const pkgdef :Spk.PackageDefinition = ( #marketBig = (svg = embed "path/to/market-big-300x300.svg"), ), - website = "http://hledger.org", + website = "https://hledger.org", # This should be the app's main website url. codeUrl = "https://github.com/simonmichael/hledger", @@ -204,41 +204,38 @@ const pkgdef :Spk.PackageDefinition = ( description = (defaultText = "grants ability to append transactions to the ledger"), ), ( - name = "manage", - title = (defaultText = "manage"), - description = (defaultText = "grants ability to modify or replace the entire ledger"), + name = "edit", + title = (defaultText = "edit"), + description = (defaultText = "grants ability to modify transactions and directives or erase the entire ledger"), ), ], roles = [ # Roles are logical collections of permissions. For instance, your app may have # a "viewer" role and an "editor" role ( - title = (defaultText = "manager"), # Name of the role. Shown in the Sandstorm UI to indicate which users have which roles. - - permissions = [true, true, true], + title = (defaultText = "viewer"), # An array indicating which permissions this role carries. # It should be the same length as the permissions array in # viewInfo, and the order of the lists must match. - - verbPhrase = (defaultText = "has full access to the ledger"), + permissions = [true, false, false], # Brief explanatory text to show in the sharing UI indicating # what a user assigned this role will be able to do with the grain. - - description = (defaultText = "managers can modify the ledger in any way."), + verbPhrase = (defaultText = "can view the ledger"), # Prose describing what this role means, suitable for a tool tip or similar help text. + description = (defaultText = "viewers can only view the ledger."), + ), + ( + title = (defaultText = "adder"), + permissions = [true, true, false], + verbPhrase = (defaultText = "can append new transactions"), + description = (defaultText = "adders can view the ledger and add new transactions to it."), ), ( title = (defaultText = "editor"), - permissions = [true, true, false], - verbPhrase = (defaultText = "can append new transactions"), - description = (defaultText = "editors can view the ledger or append new transactions to it."), - ), - ( - title = (defaultText = "viewer"), - permissions = [true, false, false], - verbPhrase = (defaultText = "can view the ledger"), - description = (defaultText = "viewers can only view the ledger."), + permissions = [true, true, true], + verbPhrase = (defaultText = "has full access to the ledger"), + description = (defaultText = "editors can change or erase transactions and directives."), ), ], ), diff --git a/hledger-web/Hledger/Web/Foundation.hs b/hledger-web/Hledger/Web/Foundation.hs index 4066ed4d5..c151a9d29 100644 --- a/hledger-web/Hledger/Web/Foundation.hs +++ b/hledger-web/Hledger/Web/Foundation.hs @@ -114,7 +114,7 @@ instance Yesod App where master <- getYesod here <- fromMaybe RootR <$> getCurrentRoute - VD{opts, j, qparam, q, qopts, caps} <- getViewData + VD{opts, j, qparam, q, qopts, perms} <- getViewData msg <- getMessage showSidebar <- shouldShowSidebar @@ -198,7 +198,7 @@ data ViewData = VD , qparam :: Text -- ^ the current "q" request parameter , q :: Query -- ^ a query parsed from the q parameter , qopts :: [QueryOpt] -- ^ query options parsed from the q parameter - , caps :: [Capability] -- ^ capabilities enabled for this request + , perms :: [Permission] -- ^ permissions enabled for this request (by --allow and/or X-Sandstorm-Permissions) } deriving (Show) instance Show Text.Blaze.Markup where show _ = "" @@ -233,16 +233,19 @@ getViewData = do -- if either of the above gave an error, display it maybe (pure ()) (setMessage . toHtml) $ mjerr <|> mqerr - -- do some permissions checking - caps <- case capabilitiesHeader_ opts of - Nothing -> return (capabilities_ opts) - Just h -> do + -- find out which permissions are enabled + perms <- case allow_ opts of + -- if started with --allow=sandstorm, take permissions from X-Sandstorm-Permissions header + SandstormAccess -> do + let h = "X-Sandstorm-Permissions" hs <- fmap (BC.split ',' . snd) . filter ((== h) . fst) . requestHeaders <$> waiRequest - fmap join . for (join hs) $ \x -> case capabilityFromBS x of - Left e -> [] <$ addMessage "" ("Unknown permission: " <> toHtml (BC.unpack e)) - Right c -> pure [c] + fmap join . for (join hs) $ \x -> case parsePermission x of + Left e -> [] <$ addMessage "" ("Unknown permission: " <> toHtml e) + Right p -> pure [p] + -- otherwise take them from the access level specified by --allow's access level + cliaccess -> pure $ accessLevelToPermissions cliaccess - return VD{opts, today, j, qparam, q, qopts, caps} + return VD{opts, today, j, qparam, q, qopts, perms} checkServerSideUiEnabled :: Handler () checkServerSideUiEnabled = do diff --git a/hledger-web/Hledger/Web/Handler/AddR.hs b/hledger-web/Hledger/Web/Handler/AddR.hs index 80ac72f68..f9ed64d98 100644 --- a/hledger-web/Hledger/Web/Handler/AddR.hs +++ b/hledger-web/Hledger/Web/Handler/AddR.hs @@ -30,8 +30,8 @@ getAddR = do postAddR :: Handler () postAddR = do checkServerSideUiEnabled - VD{caps, j, today} <- getViewData - when (CapAdd `notElem` caps) (permissionDenied "Missing the 'add' capability") + VD{perms, j, today} <- getViewData + when (AddPermission `notElem` perms) (permissionDenied "Missing the 'add' permission") ((res, view), enctype) <- runFormPost $ addForm j today case res of @@ -59,8 +59,8 @@ postAddR = do -- The web form handler above should probably use PUT as well. putAddR :: Handler RepJson putAddR = do - VD{caps, j, opts} <- getViewData - when (CapAdd `notElem` caps) (permissionDenied "Missing the 'add' capability") + VD{perms, j, opts} <- getViewData + when (AddPermission `notElem` perms) (permissionDenied "Missing the 'add' permission") (r :: Result Transaction) <- parseCheckJsonBody case r of diff --git a/hledger-web/Hledger/Web/Handler/EditR.hs b/hledger-web/Hledger/Web/Handler/EditR.hs index 9e83ead40..2ec2017dd 100644 --- a/hledger-web/Hledger/Web/Handler/EditR.hs +++ b/hledger-web/Hledger/Web/Handler/EditR.hs @@ -31,8 +31,8 @@ getEditR f = do postEditR :: FilePath -> Handler () postEditR f = do checkServerSideUiEnabled - VD {caps, j} <- getViewData - when (CapManage `notElem` caps) (permissionDenied "Missing the 'manage' capability") + VD {perms, j} <- getViewData + when (EditPermission `notElem` perms) (permissionDenied "Missing the 'edit' permission") (f', txt) <- journalFile404 f j ((res, view), enctype) <- runFormPost (editForm f' txt) diff --git a/hledger-web/Hledger/Web/Handler/JournalR.hs b/hledger-web/Hledger/Web/Handler/JournalR.hs index 168efe803..10419e103 100644 --- a/hledger-web/Hledger/Web/Handler/JournalR.hs +++ b/hledger-web/Hledger/Web/Handler/JournalR.hs @@ -20,8 +20,8 @@ import Hledger.Web.Widget.Common getJournalR :: Handler Html getJournalR = do checkServerSideUiEnabled - VD{caps, j, q, opts, qparam, qopts, today} <- getViewData - when (CapView `notElem` caps) (permissionDenied "Missing the 'view' capability") + VD{perms, j, q, opts, qparam, qopts, today} <- getViewData + when (ViewPermission `notElem` perms) (permissionDenied "Missing the 'view' permission") let title = case inAccount qopts of Nothing -> "General Journal" Just (a, inclsubs) -> "Transactions in " <> a <> if inclsubs then "" else " (excluding subaccounts)" diff --git a/hledger-web/Hledger/Web/Handler/MiscR.hs b/hledger-web/Hledger/Web/Handler/MiscR.hs index 102e19023..f9919dbdf 100644 --- a/hledger-web/Hledger/Web/Handler/MiscR.hs +++ b/hledger-web/Hledger/Web/Handler/MiscR.hs @@ -38,17 +38,17 @@ getRootR = do getManageR :: Handler Html getManageR = do checkServerSideUiEnabled - VD{caps, j} <- getViewData - when (CapManage `notElem` caps) (permissionDenied "Missing the 'manage' capability") + VD{perms, j} <- getViewData + when (EditPermission `notElem` perms) (permissionDenied "Missing the 'edit' permission") defaultLayout $ do - setTitle "Manage journal" + setTitle "Edit journal" $(widgetFile "manage") getDownloadR :: FilePath -> Handler TypedContent getDownloadR f = do checkServerSideUiEnabled - VD{caps, j} <- getViewData - when (CapManage `notElem` caps) (permissionDenied "Missing the 'manage' capability") + VD{perms, j} <- getViewData + when (EditPermission `notElem` perms) (permissionDenied "Missing the 'edit' permission") (f', txt) <- journalFile404 f j addHeader "Content-Disposition" ("attachment; filename=\"" <> T.pack f' <> "\"") sendResponse ("text/plain" :: ByteString, toContent txt) @@ -57,50 +57,50 @@ getDownloadR f = do getVersionR :: Handler TypedContent getVersionR = do - VD{caps} <- getViewData - when (CapView `notElem` caps) (permissionDenied "Missing the 'view' capability") + VD{perms} <- getViewData + when (ViewPermission `notElem` perms) (permissionDenied "Missing the 'view' permission") selectRep $ do provideJson $ packageversion getAccountnamesR :: Handler TypedContent getAccountnamesR = do - VD{caps, j} <- getViewData - when (CapView `notElem` caps) (permissionDenied "Missing the 'view' capability") + VD{perms, j} <- getViewData + when (ViewPermission `notElem` perms) (permissionDenied "Missing the 'view' permission") selectRep $ do provideJson $ journalAccountNames j getTransactionsR :: Handler TypedContent getTransactionsR = do - VD{caps, j} <- getViewData - when (CapView `notElem` caps) (permissionDenied "Missing the 'view' capability") + VD{perms, j} <- getViewData + when (ViewPermission `notElem` perms) (permissionDenied "Missing the 'view' permission") selectRep $ do provideJson $ jtxns j getPricesR :: Handler TypedContent getPricesR = do - VD{caps, j} <- getViewData - when (CapView `notElem` caps) (permissionDenied "Missing the 'view' capability") + VD{perms, j} <- getViewData + when (ViewPermission `notElem` perms) (permissionDenied "Missing the 'view' permission") selectRep $ do provideJson $ map priceDirectiveToMarketPrice $ jpricedirectives j getCommoditiesR :: Handler TypedContent getCommoditiesR = do - VD{caps, j} <- getViewData - when (CapView `notElem` caps) (permissionDenied "Missing the 'view' capability") + VD{perms, j} <- getViewData + when (ViewPermission `notElem` perms) (permissionDenied "Missing the 'view' permission") selectRep $ do provideJson $ (M.keys . jinferredcommodities) j getAccountsR :: Handler TypedContent getAccountsR = do - VD{caps, j} <- getViewData - when (CapView `notElem` caps) (permissionDenied "Missing the 'view' capability") + VD{perms, j} <- getViewData + when (ViewPermission `notElem` perms) (permissionDenied "Missing the 'view' permission") selectRep $ do provideJson $ flattenAccounts $ mapAccounts (accountSetDeclarationInfo j) $ ledgerRootAccount $ ledgerFromJournal Any j getAccounttransactionsR :: Text -> Handler TypedContent getAccounttransactionsR a = do - VD{caps, j} <- getViewData - when (CapView `notElem` caps) (permissionDenied "Missing the 'view' capability") + VD{perms, j} <- getViewData + when (ViewPermission `notElem` perms) (permissionDenied "Missing the 'view' permission") let rspec = defreportspec thisacctq = Acct $ accountNameToAccountRegex a -- includes subs diff --git a/hledger-web/Hledger/Web/Handler/RegisterR.hs b/hledger-web/Hledger/Web/Handler/RegisterR.hs index 7ae4a8014..72ad75082 100644 --- a/hledger-web/Hledger/Web/Handler/RegisterR.hs +++ b/hledger-web/Hledger/Web/Handler/RegisterR.hs @@ -26,8 +26,8 @@ import Hledger.Web.Widget.Common getRegisterR :: Handler Html getRegisterR = do checkServerSideUiEnabled - VD{caps, j, q, opts, qparam, qopts, today} <- getViewData - when (CapView `notElem` caps) (permissionDenied "Missing the 'view' capability") + VD{perms, j, q, opts, qparam, qopts, today} <- getViewData + when (ViewPermission `notElem` perms) (permissionDenied "Missing the 'view' permission") let (a,inclsubs) = fromMaybe ("all accounts",True) $ inAccount qopts s1 = if inclsubs then "" else " (excluding subaccounts)" diff --git a/hledger-web/Hledger/Web/Handler/UploadR.hs b/hledger-web/Hledger/Web/Handler/UploadR.hs index 2b657eb1d..af4eca37e 100644 --- a/hledger-web/Hledger/Web/Handler/UploadR.hs +++ b/hledger-web/Hledger/Web/Handler/UploadR.hs @@ -35,8 +35,8 @@ getUploadR f = do postUploadR :: FilePath -> Handler () postUploadR f = do checkServerSideUiEnabled - VD {caps, j} <- getViewData - when (CapManage `notElem` caps) (permissionDenied "Missing the 'manage' capability") + VD {perms, j} <- getViewData + when (EditPermission `notElem` perms) (permissionDenied "Missing the 'edit' permission") (f', _) <- journalFile404 f j ((res, view), enctype) <- runFormPost (uploadForm f') diff --git a/hledger-web/Hledger/Web/Import.hs b/hledger-web/Hledger/Web/Import.hs index abc86e647..cadbfe77f 100644 --- a/hledger-web/Hledger/Web/Import.hs +++ b/hledger-web/Hledger/Web/Import.hs @@ -23,4 +23,4 @@ import Text.Blaze as Import (Markup) import Hledger.Web.Foundation as Import import Hledger.Web.Settings as Import import Hledger.Web.Settings.StaticFiles as Import -import Hledger.Web.WebOptions as Import (Capability(..)) +import Hledger.Web.WebOptions as Import (Permission(..)) diff --git a/hledger-web/Hledger/Web/WebOptions.hs b/hledger-web/Hledger/Web/WebOptions.hs index ea0428e8e..cbf8878d3 100644 --- a/hledger-web/Hledger/Web/WebOptions.hs +++ b/hledger-web/Hledger/Web/WebOptions.hs @@ -6,17 +6,17 @@ module Hledger.Web.WebOptions where import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as BC import Data.ByteString.UTF8 (fromString) -import Data.CaseInsensitive (CI, mk) import Data.Default (Default(def)) import Data.Maybe (fromMaybe) -import qualified Data.Text as T import Data.Text (Text) import System.Environment (getArgs) import Network.Wai as WAI import Network.Wai.Middleware.Cors +import Safe (lastMay) import Hledger.Cli hiding (packageversion, progname, prognameandversion) import Hledger.Web.Settings (defhost, defport, defbaseurl) +import qualified Data.Text as T -- cf Hledger.Cli.Version @@ -76,15 +76,10 @@ webflags = "FILEURL" "set the static files url (default: BASEURL/static)" , flagReq - ["capabilities"] - (\s opts -> Right $ setopt "capabilities" s opts) - "CAP[,CAP..]" - "enable the view, add, and/or manage capabilities (default: view,add)" - , flagReq - ["capabilities-header"] - (\s opts -> Right $ setopt "capabilities-header" s opts) - "HTTPHEADER" - "read capabilities to enable from a HTTP header, like X-Sandstorm-Permissions (default: disabled)" + ["allow"] + (\s opts -> Right $ setopt "allow" s opts) + "view|add|edit" + "set the user's access level for changing data (default: `add`). (There is also `sandstorm`, used when running on Sandstorm.)" , flagNone ["test"] (setboolopt "test") @@ -124,8 +119,7 @@ data WebOpts = WebOpts , port_ :: !Int , base_url_ :: !String , file_url_ :: !(Maybe String) - , capabilities_ :: ![Capability] - , capabilitiesHeader_ :: !(Maybe (CI ByteString)) + , allow_ :: !AccessLevel , cliopts_ :: !CliOpts , socket_ :: !(Maybe String) } deriving (Show) @@ -139,8 +133,7 @@ defwebopts = WebOpts , port_ = def , base_url_ = "" , file_url_ = Nothing - , capabilities_ = [CapView, CapAdd] - , capabilitiesHeader_ = Nothing + , allow_ = AddAccess , cliopts_ = def , socket_ = Nothing } @@ -153,15 +146,15 @@ rawOptsToWebOpts rawopts = cliopts <- rawOptsToCliOpts rawopts let h = fromMaybe defhost $ maybestringopt "host" rawopts p = fromMaybe defport $ maybeposintopt "port" rawopts - b = - maybe (defbaseurl h p) stripTrailingSlash $ - maybestringopt "base-url" rawopts - caps' = T.splitOn "," . T.pack =<< listofstringopt "capabilities" rawopts - caps = case traverse capabilityFromText caps' of - Left e -> error' ("Unknown capability: " ++ T.unpack e) -- PARTIAL: - Right [] -> [CapView, CapAdd] - Right xs -> xs + b = maybe (defbaseurl h p) stripTrailingSlash $ maybestringopt "base-url" rawopts sock = stripTrailingSlash <$> maybestringopt "socket" rawopts + access = + case lastMay $ listofstringopt "allow" rawopts of + Nothing -> AddAccess + Just t -> + case parseAccessLevel t of + Right al -> al + Left err -> error' ("Unknown access level: " ++ err) -- PARTIAL: return defwebopts { serve_ = case sock of @@ -173,8 +166,7 @@ rawOptsToWebOpts rawopts = , port_ = p , base_url_ = b , file_url_ = stripTrailingSlash <$> maybestringopt "file-url" rawopts - , capabilities_ = caps - , capabilitiesHeader_ = mk . BC.pack <$> maybestringopt "capabilities-header" rawopts + , allow_ = access , cliopts_ = cliopts , socket_ = sock } @@ -189,29 +181,45 @@ getHledgerWebOpts = do args <- fmap (replaceNumericFlags . ensureDebugHasArg) . expandArgsAt =<< getArgs rawOptsToWebOpts . either usageError id $ process webmode args -data Capability - = CapView - | CapAdd - | CapManage +data Permission + = ViewPermission -- ^ allow viewing things (read only) + | AddPermission -- ^ allow adding transactions, or more generally allow appending text to input files + | EditPermission -- ^ allow editing input files deriving (Eq, Ord, Bounded, Enum, Show) -capabilityFromText :: Text -> Either Text Capability -capabilityFromText "view" = Right CapView -capabilityFromText "add" = Right CapAdd -capabilityFromText "manage" = Right CapManage -capabilityFromText x = Left x +parsePermission :: ByteString -> Either Text Permission +parsePermission "view" = Right ViewPermission +parsePermission "add" = Right AddPermission +parsePermission "edit" = Right EditPermission +parsePermission x = Left $ T.pack $ BC.unpack x -capabilityFromBS :: ByteString -> Either ByteString Capability -capabilityFromBS "view" = Right CapView -capabilityFromBS "add" = Right CapAdd -capabilityFromBS "manage" = Right CapManage -capabilityFromBS x = Left x +-- | For the --allow option: how much access to allow to hledger-web users ? +data AccessLevel = + ViewAccess -- ^ view permission only + | AddAccess -- ^ view and add permissions + | EditAccess -- ^ view, add and edit permissions + | SandstormAccess -- ^ the permissions specified by the X-Sandstorm-Permissions HTTP request header + deriving (Eq, Ord, Bounded, Enum, Show) + +parseAccessLevel :: String -> Either String AccessLevel +parseAccessLevel "view" = Right ViewAccess +parseAccessLevel "add" = Right AddAccess +parseAccessLevel "edit" = Right EditAccess +parseAccessLevel "sandstorm" = Right SandstormAccess +parseAccessLevel s = Left $ s <> ", should be one of: view, add, edit, sandstorm" + +-- | Convert an --allow access level to the permissions used internally. +-- SandstormAccess generates an empty list, to be filled in later. +accessLevelToPermissions :: AccessLevel -> [Permission] +accessLevelToPermissions ViewAccess = [ViewPermission] +accessLevelToPermissions AddAccess = [ViewPermission, AddPermission] +accessLevelToPermissions EditAccess = [ViewPermission, AddPermission, EditPermission] +accessLevelToPermissions SandstormAccess = [] -- detected from request header simplePolicyWithOrigin :: Origin -> CorsResourcePolicy simplePolicyWithOrigin origin = simpleCorsResourcePolicy { corsOrigins = Just ([origin], False) } - corsPolicyFromString :: String -> WAI.Middleware corsPolicyFromString origin = let diff --git a/hledger-web/hledger-web.m4.md b/hledger-web/hledger-web.m4.md index b026848ea..7b3c0eda2 100644 --- a/hledger-web/hledger-web.m4.md +++ b/hledger-web/hledger-web.m4.md @@ -94,11 +94,9 @@ Can be useful if running behind a reverse web proxy that does path rewriting. hledger-web normally serves static files itself, but if you wanted to serve them from another server for efficiency, you would set the url with this. -`--capabilities=CAP[,CAP..]` -: enable the view, add, and/or manage capabilities (default: view,add) - -`--capabilities-header=HTTPHEADER` -: read capabilities to enable from a HTTP header, like X-Sandstorm-Permissions (default: disabled) +`--allow=view|add|edit` +: set the user's access level for changing data (default: `add`). +(There is also `sandstorm`, used when running on the Sandstorm app platform.) `--test` : run hledger-web's tests and exit. hspec test runner args may follow a --, eg: hledger-web --test -- --help diff --git a/hledger-web/package.yaml b/hledger-web/package.yaml index 53e8ba76c..e41f5c517 100644 --- a/hledger-web/package.yaml +++ b/hledger-web/package.yaml @@ -128,6 +128,7 @@ library: - megaparsec >=7.0.0 && <9.6 - mtl >=2.2.1 - network + - safe >=0.3.19 - shakespeare >=2.0.2.2 - template-haskell - text >=1.2 diff --git a/hledger-web/templates/default-layout.hamlet b/hledger-web/templates/default-layout.hamlet index 907623512..82c9db499 100644 --- a/hledger-web/templates/default-layout.hamlet +++ b/hledger-web/templates/default-layout.hamlet @@ -7,7 +7,7 @@

#{takeFileName (journalFilePath j)} -$if elem CapView caps +$if elem ViewPermission perms ^{accounts} @@ -15,7 +15,7 @@ $if elem CapView caps $maybe m <- msg
#{m} - $if elem CapView caps + $if elem ViewPermission perms @@ -25,7 +25,7 @@ $if elem CapView caps
^{mixedAmountAsHtml amt} -$if elem CapAdd caps +$if elem AddPermission perms ^{addModal AddR j today} diff --git a/hledger-web/templates/register.hamlet b/hledger-web/templates/register.hamlet index bb7caa320..2c947ceb7 100644 --- a/hledger-web/templates/register.hamlet +++ b/hledger-web/templates/register.hamlet @@ -35,5 +35,5 @@ ^{mixedAmountAsHtml bal} -$if elem CapAdd caps +$if elem AddPermission perms ^{addModal AddR j today}