atlassian-connect-core-0.1.0.0: src/Snap/AtlassianConnect/Connect.hs
{-# LANGUAGE OverloadedStrings #-}
module Snap.AtlassianConnect.Connect
( initConnectSnaplet
) where
-- Data file imports
import qualified Paths_atlassian_connect_core as PACC
-- Helper Imports
import Data.ConfigurationHelpers
-- Atlassian Connect Imports
import qualified Data.Connect.Descriptor as D
import Snap.AtlassianConnect.Data
import qualified Snap.AtlassianConnect.PageToken as PT
import qualified Snap.AtlassianConnect.Routes as CR
import qualified Snap.AtlassianConnect.TimeUnits as TU
-- Standard imports
import qualified Control.Monad as CM
import qualified Control.Monad.IO.Class as MI
import qualified Crypto.Cipher.AES as CCA
import qualified Data.ByteString.Char8 as BSC
import qualified Data.Configurator as DC
import qualified Data.Configurator.Types as DCT
import qualified Data.EnvironmentHelpers as DE
import Data.Maybe (fromMaybe)
import Data.Text
import Data.Time.Units
import qualified Network.HostName as HN
import qualified Network.URI as NU
import qualified Snap.Snaplet as SS
-- | Initialise the Atlassian Connect part of this application. This gives you an Atlassian Connect configuration that
-- comes with:
--
-- * An Atlassian Connect configuration file.
-- * The definition of what it means to be an Atlassian Connect Tenant.
-- * Default routes for your Atlassian Connect Descriptor. (With automatic configuration injection of Plugin Keys / Names)
-- * Page Token support
-- * Host Whitelist Support
--
-- In short, it is the easy way to get up and running with Atlassian Connect in your Snap applications.
initConnectSnaplet :: D.Plugin -> SS.SnapletInit b Connect
initConnectSnaplet plugin = SS.makeSnaplet "connect" "Atlassian Connect" (Just dataDir) $ do
SS.addRoutes CR.connectRoutes
configurationDirectory <- SS.getSnapletFilePath
MI.liftIO . fmap (toConnect plugin) $ SS.loadAppConfig "connect.cfg" configurationDirectory >>= loadConnectConfig
dataDir :: IO FilePath
dataDir = CM.liftM (++"/resources") PACC.getDataDir
data ConnectConfig = ConnectConfig
{ ccSecretKey :: BSC.ByteString
, ccPluginName :: Text
, ccPluginKey :: Text
, ccBaseUrl :: NU.URI
, ccPageTokenTimeout :: TU.ConnectSecond
, ccHostWhiteList :: [Text] -- TODO This host whitelist should be loaded from configuration
}
toConnect :: D.Plugin -> ConnectConfig -> Connect
toConnect plugin conf = Connect
{ connectPlugin = plugin
, connectAES = CCA.initAES $ ccSecretKey conf
, connectPluginName = D.Name $ ccPluginName conf
, connectPluginKey = D.PluginKey $ ccPluginKey conf
, connectBaseUrl = ccBaseUrl conf
, connectPageTokenTimeout = D.Timeout . TU.getSecond . ccPageTokenTimeout $ conf
, connectHostWhitelist = ccHostWhiteList conf
}
loadConnectConfig :: DCT.Config -> IO ConnectConfig
loadConnectConfig connectConf = do
name <- require connectConf "plugin_name" "Missing plugin name in connect configuration file."
key <- require connectConf "plugin_key" "Missing plugin key in connect configuration file."
rawBaseUrl <- require connectConf "base_url" "Missing base url in connect configuration file."
secret <- require connectConf "secret_key" "Missing secret key in connect configuration file."
hostWhiteList <- validHosts
let keyLength = BSC.length secret
CM.when (keyLength /= 32) $ fail ("Expected Atlassian Connect secret_key to be 32 Hex Digits long but was actually: " ++ show keyLength)
envBaseUrl <- DE.getEnv "CONNECT_BASE_URL"
envSecretKey <- DE.getEnv "CONNECT_SECRET_KEY"
let baseUrlToParse = fromMaybe rawBaseUrl envBaseUrl
case NU.parseURI baseUrlToParse of
Nothing -> fail("Could not parse the baseUrl in the configuration file: " ++ rawBaseUrl)
Just baseUrl -> do
pageTokenTimeoutInSeconds <- DC.lookupDefault PT.defaultTimeoutSeconds connectConf "page_token_timeout_seconds"
return ConnectConfig
{ ccPluginName = name
, ccPluginKey = key
, ccBaseUrl = baseUrl
, ccSecretKey = maybe secret BSC.pack envSecretKey
, ccPageTokenTimeout = pageTokenTimeoutInSeconds
, ccHostWhiteList = hostWhiteList
}
validHosts :: IO[Text]
validHosts = fmap hosts HN.getHostName
where hosts localhost = fmap pack (localhost : ["localhost", "jira-dev.com", "jira.com", "atlassian.net"])