packages feed

toodles-1.1.0: src/License.hs

{-# LANGUAGE DeriveAnyClass      #-}
{-# LANGUAGE DeriveGeneric       #-}
{-# LANGUAGE GADTs               #-}
{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE ScopedTypeVariables #-}


module License
  (
    UserTier(..),
    ToodlesLicense(..),
    readLicense
  ) where

import           Paths_toodles

import           Data.Aeson
import qualified Data.ByteString.Base64.Lazy as B64
import qualified Data.ByteString.Lazy.Char8  as LB
import           Data.Text                   (Text)
import qualified Data.Text                   as T
import           Data.Time.Clock.POSIX
import           GHC.Generics
import           System.Directory
import           System.Exit
import           System.Process

data UserTier
  = BadLicense String
  | NoLiscense
  | Individual
  | Commercial
  deriving (Show, Eq, Ord, Generic, ToJSON, FromJSON)

data License = License {
  payload          :: ToodlesLicense,
  encoded          :: Text,
  payloadSignature :: Text
                       } deriving (Generic, FromJSON, ToJSON, Show)

data ToodlesLicense = ToodlesLicense
  { validStart :: Integer
  , validEnd   :: Integer
  , email      :: Text
  , reference  :: Text
  , product    :: Text
  } deriving (Generic, FromJSON, ToJSON, Show)

readLicense :: FilePath -> FilePath  -> IO (Either String UserTier)
readLicense publicKeyPath licensePath  = do
  licenseExists <- doesFileExist licensePath
  if not licenseExists then
    return $ Right NoLiscense
  else do
    parsedContents <- eitherDecodeFileStrict licensePath
    either (return . Left) (isLicenseValid publicKeyPath) parsedContents

isLicenseValid :: FilePath -> License -> IO (Either String UserTier)
isLicenseValid publicKeyPath (License _ encodedPayload sig) = do
  dataDir <- getDataDir
  now <- ((* 1000) . round) `fmap` getPOSIXTime
  -- dependencies for license verification. for now, python
  -- TODO(#techdebt) - license verification should be done in haskell, this removed
  let args =
        [ dataDir ++ "/verify.py"
        , publicKeyPath
        , T.unpack sig
        , T.unpack encodedPayload
        ]
      decodedPayload =
        decode (B64.decodeLenient . LB.pack $ T.unpack encodedPayload)
  (exitcode, stdout, stderr) <- readProcessWithExitCode "python" args ""
  putStrLn stderr
  return $
    let validated = ("True" == T.strip (T.pack stdout))
    in if (exitcode == ExitSuccess) &&
          validated && (maybe 0 validEnd decodedPayload >= now)
         then Right Commercial
         else Left "Invalid license file"