aboutsummaryrefslogtreecommitdiff
path: root/lib/Server/Frontend/Routes.hs
blob: 8dceda5dd3e842d13254a53fc9a5b44458d24322 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
{-# LANGUAGE LambdaCase      #-}
{-# LANGUAGE QuasiQuotes     #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies    #-}

module Server.Frontend.Routes where

import           Config                    (ServerConfig (..), UffdConfig (..))
import           Control.Monad             (forM_)
import qualified Data.Aeson                as A
import qualified Data.ByteString.Char8     as C8
import qualified Data.ByteString.Lazy      as LB
import           Data.Functor              ((<&>))
import           Data.Pool                 (Pool)
import qualified Data.Text                 as T
import           Data.Time                 (UTCTime)
import           Data.Time.Calendar        (Day)
import           Data.UUID                 (UUID)
import           Database.Persist.Sql      (SqlBackend, runSqlPool)
import qualified GTFS
import           Persist                   (Token)
import           Text.Blaze.Internal       (MarkupM (Empty))
import           Yesod
import           Yesod.Auth
import           Yesod.Auth.OAuth2.Prelude
import           Yesod.Auth.Uffd           (UffdUser (..), uffdClient)
import           Yesod.Orphans             ()

data Frontend = Frontend
  { getGtfs     :: GTFS.GTFS
  , getPool     :: Pool SqlBackend
  , getSettings :: ServerConfig
  }

mkMessage "Frontend" "messages" "en"

mkYesodData "Frontend" [parseRoutes|
/ RootR GET
/auth AuthR Auth getAuth

/tickets TicketsR GET
/ticket/#UUID TicketViewR GET
/ticket/map/#UUID TicketMapViewR GET
/ticket/announce/#UUID AnnounceR POST
/ticket/del-announce/#UUID DelAnnounceR GET

/spacetime SpaceTimeDiagramR GET

/token/block/#Token TokenBlock GET

/gtfs/trips GtfsTripsViewR GET
/gtfs/trip/#GTFS.TripId GtfsTripViewR GET
/gtfs/import/#Day GtfsTicketImportR POST

/tracker OnboardTrackerR GET
|]

emptyMarkup :: MarkupM a -> Bool
emptyMarkup (Empty _) = True
emptyMarkup _         = False


instance Yesod Frontend where
  authRoute _ = Just $ AuthR LoginR
  isAuthorized OnboardTrackerR _ = pure Authorized
  isAuthorized (AuthR _) _ = pure Authorized
  isAuthorized _ _ = do
    UffdConfig{..} <- getYesod <&> serverConfigLogin . getSettings
    if uffdConfigEnable then maybeAuthId >>= \case
      Just _  -> pure Authorized
      Nothing -> pure AuthenticationRequired
    else pure Authorized


  defaultLayout w = do
      PageContent{..} <- widgetToPageContent w
      msgs <- getMessages

      withUrlRenderer [hamlet|
        $newline never
        $doctype 5
        <html>
          <head>
            <title>
              $if emptyMarkup pageTitle
                Tracktrain
              $else
                #{pageTitle}
            $maybe description <- pageDescription
              <meta name="description" content="#{description}">
            ^{pageHead}
            <link rel="stylesheet" href="/assets/style.css">
            <meta name="viewport" content="width=device-width, initial-scale=1">
          <body>
            $forall (status, msg) <- msgs
              <!--  <p class="message #{status}">#{msg} -->
            ^{pageBody}
        |]


instance RenderMessage Frontend FormMessage where
  renderMessage _ _ = defaultFormMessage

instance YesodPersist Frontend where
  type YesodPersistBackend Frontend = SqlBackend
  runDB action = do
    pool <- getYesod <&> getPool
    runSqlPool action pool


-- this instance is only slightly cursed (it keeps login information
-- as json in a session cookie and hopes nothing will ever go wrong)
instance YesodAuth Frontend where
  type AuthId Frontend = UffdUser

  authPlugins cr = case config of
    UffdConfig {..} -> if uffdConfigEnable
      then [ uffdClient uffdConfigUrl uffdConfigClientName uffdConfigClientSecret ]
      else []
    where config = serverConfigLogin (getSettings cr)

  maybeAuthId = do
    e <- lookupSession "json"
    pure $ case e of
      Nothing    -> Nothing
      Just extra -> A.decode (LB.fromStrict $ C8.pack $ T.unpack extra)

  authenticate creds = do
    forM_ (credsExtra creds) (uncurry setSession)
    -- extra <- lookupSession "extra"
    -- pure (Authenticated ( undefined))
    e <- lookupSession "json"
    case e of
      Nothing -> error "no session information"
      Just extra -> case A.decode (LB.fromStrict $ C8.pack $ T.unpack extra) of
        Nothing   -> error "malformed session information"
        Just user -> pure $ Authenticated user

  loginDest _ = RootR
  logoutDest _ = RootR
  -- hardcode redirecting to uffd directly; showing the normal login
  -- screen is kinda pointless when there's only one option
  loginHandler = do
    redirect ("/auth/page/uffd/forward" :: Text)
  onLogout = do
    clearSession