Save Xero OAuth tokens to DB

This commit is contained in:
Rene Vergara 2022-08-18 14:21:32 -05:00
parent 1aa4adba65
commit c965951d0e
Signed by: pitmutt
GPG key ID: 65122AD495A7F5B2
2 changed files with 111 additions and 17 deletions

View file

@ -2,13 +2,19 @@
module Xero where module Xero where
import Control.Monad.IO.Class
import Data.Aeson import Data.Aeson
import qualified Data.Bson as B import qualified Data.Bson as B
import qualified Data.ByteString.Lazy as BL
import Data.Maybe import Data.Maybe
import qualified Data.Text as T import qualified Data.Text as T
import Data.Text.Encoding
import Data.Time.Calendar
import Data.Time.Clock import Data.Time.Clock
import Database.MongoDB import Database.MongoDB
import GHC.Generics import GHC.Generics
import Network.HTTP.Simple
import Network.HTTP.Types.Header
-- | Type to represent a Xero app configuration -- | Type to represent a Xero app configuration
data Xero = data Xero =
@ -38,13 +44,15 @@ data XeroToken =
{ t_id :: Maybe ObjectId { t_id :: Maybe ObjectId
, t_address :: T.Text , t_address :: T.Text
, t_access :: T.Text , t_access :: T.Text
, t_expires :: UTCTime , t_expires :: Integer
, t_refresh :: T.Text , t_refresh :: T.Text
, t_accdte :: UTCTime
, t_refdte :: UTCTime
} }
deriving (Eq, Show) deriving (Eq, Show)
instance ToJSON XeroToken where instance ToJSON XeroToken where
toJSON (XeroToken i a t e r) = toJSON (XeroToken i a t e r aD d) =
case i of case i of
Just oid -> Just oid ->
object object
@ -53,6 +61,8 @@ instance ToJSON XeroToken where
, "accessToken" .= t , "accessToken" .= t
, "expires" .= e , "expires" .= e
, "refreshToken" .= r , "refreshToken" .= r
, "accExpires" .= aD
, "refExpires" .= d
] ]
Nothing -> Nothing ->
object object
@ -61,28 +71,28 @@ instance ToJSON XeroToken where
, "accessToken" .= t , "accessToken" .= t
, "expires" .= e , "expires" .= e
, "refreshToken" .= r , "refreshToken" .= r
, "accExpires" .= aD
, "refExpires" .= d
] ]
instance FromJSON XeroToken where instance FromJSON XeroToken where
parseJSON = parseJSON =
withObject "XeroToken" $ \obj -> do withObject "XeroToken" $ \obj -> do
i <- obj .: "_id" t <- obj .: "access_token"
a <- obj .: "address" e <- obj .: "expires_in"
t <- obj .: "accessToken" r <- obj .: "refresh_token"
e <- obj .: "expires"
r <- obj .: "refreshToken"
pure $ pure $
XeroToken XeroToken
(if not (null i) Nothing
then Just (read i) ""
else Nothing)
a
t t
e e
r r
(UTCTime (fromGregorian 2000 1 1) (secondsToDiffTime 0))
(UTCTime (fromGregorian 2000 1 1) (secondsToDiffTime 0))
instance Val XeroToken where instance Val XeroToken where
val (XeroToken i a t e r) = val (XeroToken i a t e r aD d) =
if isJust i if isJust i
then Doc then Doc
[ "_id" =: i [ "_id" =: i
@ -90,12 +100,16 @@ instance Val XeroToken where
, "accessToken" =: t , "accessToken" =: t
, "expires" =: e , "expires" =: e
, "refreshToken" =: r , "refreshToken" =: r
, "accExpires" =: aD
, "refExpires" =: d
] ]
else Doc else Doc
[ "address" =: a [ "address" =: a
, "accessToken" =: t , "accessToken" =: t
, "expires" =: e , "expires" =: e
, "refreshToken" =: r , "refreshToken" =: r
, "accExpires" =: aD
, "refExpires" =: d
] ]
cast' (Doc d) = do cast' (Doc d) = do
i <- B.lookup "_id" d i <- B.lookup "_id" d
@ -103,19 +117,79 @@ instance Val XeroToken where
t <- B.lookup "accessToken" d t <- B.lookup "accessToken" d
e <- B.lookup "expires" d e <- B.lookup "expires" d
r <- B.lookup "refreshToken" d r <- B.lookup "refreshToken" d
Just (XeroToken i a t e r) aD <- B.lookup "accExpires" d
d <- B.lookup "refExpires" d
Just (XeroToken i a t e r aD d)
cast' _ = Nothing cast' _ = Nothing
processToken :: XeroToken -> T.Text -> IO XeroToken
processToken t a = do
now <- getCurrentTime
return $
XeroToken
(t_id t)
a
(t_access t)
(t_expires t)
(t_refresh t)
(addUTCTime (fromIntegral $ t_expires t) now)
(addUTCTime 5184000 now)
-- Database actions -- Database actions
findXero :: Action IO (Maybe Document) findXero :: Action IO (Maybe Document)
findXero = findOne (select [] "xero") findXero = findOne (select [] "xero")
upsertToken :: XeroToken -> Action IO () upsertToken :: XeroToken -> Action IO (Maybe Document)
upsertToken t = do upsertToken t = do
let token = val t let token = val t
case token of case token of
Doc d -> upsert (select ["address" =: t_address t] "xerotokens") d Doc d -> do
_ -> return () upsert (select ["address" =: t_address t] "xerotokens") d
findOne (select ["address" =: t_address t] "xerotokens")
_ -> return Nothing
findToken :: T.Text -> Action IO (Maybe Document) findToken :: T.Text -> Action IO (Maybe Document)
findToken a = findOne (select ["address" =: a] "xerotokens") findToken a = findOne (select ["address" =: a] "xerotokens")
-- | Function to request accesstoken
requestXeroToken ::
(Action IO (Maybe Document) -> IO (Maybe Document))
-> Xero
-> T.Text
-> T.Text
-> IO Bool
requestXeroToken f cred code address = do
token <- f $ findToken address
case token of
Just xT -> do
let xToken = cast' (Doc xT) :: Maybe XeroToken
case xToken of
Nothing -> return False
Just x -> return True
Nothing -> do
let pars =
"grant_type=authorization_code&code=" <>
code <> "&redirect_uri=http://localhost:4200/test"
let req =
setRequestBodyLBS (BL.fromStrict (encodeUtf8 pars)) $
addRequestHeader hContentType "application/x-www-form-urlencoded" $
setRequestSecure True $
setRequestBasicAuth
(encodeUtf8 $ x_clientId cred)
(encodeUtf8 $ x_clientSecret cred) $
setRequestHost "identity.xero.com" $
setRequestPort 443 $
setRequestMethod "POST" $
setRequestPath "/connect/token" defaultRequest
res <- httpJSON req
let rCode = getResponseStatusCode (res :: Response XeroToken)
case rCode of
200 -> do
let newToken = getResponseBody (res :: Response XeroToken)
pToken <- processToken newToken address
print pToken
_ <- f $ upsertToken pToken
return True
_ -> do
print res
return False

View file

@ -497,6 +497,27 @@ routes pipe config = do
[ "message" .= ("Xero config found!" :: String) [ "message" .= ("Xero config found!" :: String)
, "xeroConfig" .= toJSON (c :: Xero) , "xeroConfig" .= toJSON (c :: Xero)
]) ])
get "/api/xerotoken" $ do
code <- param "code"
address <- param "address"
xeroConfig <- liftIO $ run findXero
case xeroConfig of
Nothing -> status noContent204
Just x -> do
let xConfig = cast' (Doc x)
case xConfig of
Nothing -> status noContent204
Just c -> do
res <-
liftIO $
requestXeroToken
(run :: Action IO (Maybe Document) -> IO (Maybe Document))
c
code
address
if res
then status ok200
else status noContent204
post "/api/xerotoken" $ do post "/api/xerotoken" $ do
o <- jsonData o <- jsonData
let q = payload (o :: Payload XeroToken) let q = payload (o :: Payload XeroToken)
@ -881,7 +902,6 @@ updateLogins pipe config = do
mapM_ mapM_
(access pipe master db . ZGoBackend.addUser nodeUser nodePwd pipe db addr) (access pipe master db . ZGoBackend.addUser nodeUser nodePwd pipe db addr)
parsed parsed
putStrLn "Updated logins!"
-- | Function to mark owners as paid -- | Function to mark owners as paid
checkPayments :: Pipe -> T.Text -> IO () checkPayments :: Pipe -> T.Text -> IO ()