From de98e403d3efd938b4ad24c965c1a972bc7edd90 Mon Sep 17 00:00:00 2001 From: Profpatsch Date: Wed, 2 Apr 2025 16:39:07 +0200 Subject: [PATCH] Authentication: Remove RPXnow authentication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://rpxnow.com/ now redirects to the “Akamai Identity Cloud Social Login Dashboard”, which displays a large Notice: > As part of the Identity Cloud End of Life, please be aware of these > important dates regarding Social Login and corresponding changes you > must prepare for. And a timeframe of deprecation of Social Apps until end of 2026 and final shutdown 2027. As such, it does not quite make sense to keep supporting RPX in future versions of gitit. --- data/default.conf | 14 +----- gitit.cabal | 2 +- src/Network/Gitit/Authentication.hs | 69 ++------------------------ src/Network/Gitit/Config.hs | 10 +--- src/Network/Gitit/Rpxnow.hs | 75 ----------------------------- src/Network/Gitit/Types.hs | 3 -- 6 files changed, 8 insertions(+), 165 deletions(-) delete mode 100644 src/Network/Gitit/Rpxnow.hs diff --git a/data/default.conf b/data/default.conf index cd528f994..5d74e1a70 100644 --- a/data/default.conf +++ b/data/default.conf @@ -33,10 +33,7 @@ authentication-method: form # suppressed). 'generic' means that gitit will assume that # some form of authentication is in place that directly # sets REMOTE_USER to the name of the authenticated user -# (e.g. mod_auth_cas on apache). 'rpx' means that gitit -# will attempt to log in through https://rpxnow.com. -# This requires that 'rpx-domain', 'rpx-key', and 'base-url' -# be set below, and that 'curl' be in the system path. +# (e.g. mod_auth_cas on apache). # 'github' means that you are redirected to github website and # need to avail gitit to use your credential from there (github name and # email). Your email is used to identify you when you push your wiki data @@ -204,12 +201,6 @@ access-question-answers: # access-question: What is the code given to you by Ms. X? # access-question-answers: RED DOG, red dog -rpx-domain: -rpx-key: -# Specifies the domain and key of your RPX account. The domain is -# just the prefix of the complete RPX domain, so if your full domain -# is 'https://foo.rpxnow.com/', use 'foo' as the value of rpx-domain. - mail-command: sendmail %s # specifies the command to use to send notification emails. # '%s' will be replaced by the destination email address. @@ -250,8 +241,7 @@ use-feed: no base-url: # the base URL of the wiki, to be used in constructing feed IDs -# and RPX token_urls. -# Set this if use-feed is 'yes' or authentication-method is 'rpx'. +# Set this if use-feed is 'yes'. absolute-urls: no # make wikilinks absolute with respect to the base-url. diff --git a/gitit.cabal b/gitit.cabal index 1df84f9ea..75f90636c 100644 --- a/gitit.cabal +++ b/gitit.cabal @@ -115,7 +115,7 @@ Library Network.Gitit.Util, Network.Gitit.Server Network.Gitit.Cache, Network.Gitit.State, Network.Gitit.Handlers, - Network.Gitit.Plugins, Network.Gitit.Rpxnow, + Network.Gitit.Plugins, Network.Gitit.Page, Network.Gitit.Feed, Network.Gitit.Compat.Except, Paths_gitit diff --git a/src/Network/Gitit/Authentication.hs b/src/Network/Gitit/Authentication.hs index 98aaf1846..59a3f4013 100644 --- a/src/Network/Gitit/Authentication.hs +++ b/src/Network/Gitit/Authentication.hs @@ -25,7 +25,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA module Network.Gitit.Authentication ( loginUserForm , formAuthHandlers , httpAuthHandlers - , rpxAuthHandlers , githubAuthHandlers) where import Network.Gitit.State @@ -37,21 +36,19 @@ import Network.Gitit.Util import Network.Gitit.Authentication.Github import Network.Captcha.ReCaptcha (captchaFields, validateCaptcha) import System.Process (readProcessWithExitCode) -import Control.Monad (unless, liftM, mplus) +import Control.Monad (unless, liftM) import Control.Monad.Trans (liftIO) import System.Exit import System.Log.Logger (logM, Priority(..)) import Data.Char (isAlphaNum, isAlpha) import qualified Data.Map as M import Data.List (stripPrefix) -import Data.Maybe (isJust, fromJust, isNothing, fromMaybe) +import Data.Maybe (isJust, fromJust, fromMaybe) import Network.URL (exportURL, add_param, importURL) import Network.BSD (getHostName) import qualified Text.StringTemplate as T -import Network.HTTP (urlEncodeVars, urlDecode, urlEncode) +import Network.HTTP (urlEncodeVars) import Codec.Binary.UTF8.String (encodeString) -import Data.ByteString.UTF8 (toString) -import Network.Gitit.Rpxnow as R import Text.Blaze.Html.Renderer.String as Blaze ( renderHtml ) import Text.Blaze.Html5 hiding (i, search, u, s, contents, source, html, title, map) import qualified Text.Blaze.Html5 as Html5 hiding (search) @@ -59,6 +56,7 @@ import qualified Text.Blaze.Html5.Attributes as Html5.Attr hiding (dir, span) import Text.Blaze.Html5.Attributes import Data.String (IsString(fromString)) import qualified Text.XHtml as XHTML +import Data.ByteString.UTF8 (toString) -- | Replace each occurrence of one sublist in a list with another. -- Vendored in from pandoc 2.11.4 as 2.12 removed this function. @@ -541,65 +539,6 @@ githubLoginFailure = withData $ \params -> pgMessages = msgs } --- Login using RPX (see RPX development docs at https://rpxnow.com/docs) -loginRPXUser :: RPars -- ^ The parameters passed by the RPX callback call (after authentication has taken place - -> Handler -loginRPXUser params = do - cfg <- getConfig - ref <- getReferer - let mtoken = rToken params - if isNothing mtoken - then do - let url = baseUrl cfg ++ "/_login?destination=" ++ - fromMaybe ref (rDestination params) - if null (rpxDomain cfg) - then error "rpx-domain is not set." - else do - let rpx = "https://" ++ rpxDomain cfg ++ - ".rpxnow.com/openid/v2/signin?token_url=" ++ - urlEncode url - see rpx - else do -- We got an answer from RPX, this might also return an exception. - uid' :: Either String R.Identifier <- liftIO $ - R.authenticate (rpxKey cfg) $ fromJust mtoken - uid <- case uid' of - Right u -> return u - Left err -> error err - liftIO $ logM "gitit.loginRPXUser" DEBUG $ "uid:" ++ show uid - -- We need to get an unique Html5.Attr.id for the user - -- The 'Html5.Attr.id' is always present but can be rather cryptic - -- The 'verifiedEmail' is also unique and is a more readable choice - -- so we use it if present. - let userId = R.userIdentifier uid - let email = prop "verifiedEmail" uid - user <- liftIO $ mkUser (fromMaybe userId email) (fromMaybe "" email) "none" - updateGititState $ \s -> s { users = M.insert userId user (users s) } - key <- newSession (sessionData userId) - addCookie (MaxAge $ sessionTimeout cfg) (mkSessionCookie key) - see $ fromJust $ rDestination params - where - prop pname info = lookup pname $ R.userData info - see url = seeOther (encUrl url) $ toResponse (renderHtml mempty) - --- The parameters passed by the RPX callback call. -data RPars = RPars { rToken :: Maybe String - , rDestination :: Maybe String } - deriving Show - -instance FromData RPars where - fromData = do - vtoken <- liftM Just (look "token") `mplus` return Nothing - vDestination <- liftM (Just . urlDecode) (look "destination") `mplus` - return Nothing - return RPars { rToken = vtoken - , rDestination = vDestination } - -rpxAuthHandlers :: [Handler] -rpxAuthHandlers = - [ Network.Gitit.Server.dir "_logout" $ Network.Gitit.Server.method GET >> withData logoutUser - , Network.Gitit.Server.dir "_login" $ withData loginRPXUser - , Network.Gitit.Server.dir "_user" currentUser ] - -- | Returns username of logged in user or null string if nobody logged in. currentUser :: Handler currentUser = do diff --git a/src/Network/Gitit/Config.hs b/src/Network/Gitit/Config.hs index 2fd24e035..d94df848f 100644 --- a/src/Network/Gitit/Config.hs +++ b/src/Network/Gitit/Config.hs @@ -28,7 +28,7 @@ where import Network.Gitit.Types import Network.Gitit.Server (mimeTypes) import Network.Gitit.Framework -import Network.Gitit.Authentication (formAuthHandlers, rpxAuthHandlers, httpAuthHandlers, githubAuthHandlers) +import Network.Gitit.Authentication (formAuthHandlers, httpAuthHandlers, githubAuthHandlers) import Network.Gitit.Util (parsePageType, readFileUTF8) import System.Log.Logger (logM, Priority(..)) import System.IO (hPutStrLn, stderr) @@ -162,8 +162,6 @@ extractConfig cfgmap = do cfUseRecaptcha <- get "DEFAULT" "use-recaptcha" >>= readBool cfRecaptchaPublicKey <- get "DEFAULT" "recaptcha-public-key" cfRecaptchaPrivateKey <- get "DEFAULT" "recaptcha-private-key" - cfRPXDomain <- get "DEFAULT" "rpx-domain" - cfRPXKey <- get "DEFAULT" "rpx-key" cfCompressResponses <- get "DEFAULT" "compress-responses" >>= readBool cfUseCache <- get "DEFAULT" "use-cache" >>= readBool cfCacheDir <- get "DEFAULT" "cache-dir" @@ -195,8 +193,6 @@ extractConfig cfgmap = do "darcs" -> pure Darcs "mercurial" -> pure Mercurial x -> throwError $ "Unknown repository type: " ++ x - when (authMethod == "rpx" && cfRPXDomain == "") $ - liftIO $ logM "gitit" WARNING "rpx-domain is not set" ghConfig <- extractGithubConfig cfgmap @@ -219,7 +215,6 @@ extractConfig cfgmap = do "form" -> withUserFromSession "github" -> withUserFromSession "http" -> withUserFromHTTPAuth - "rpx" -> withUserFromSession _ -> id , requireAuthentication = case map toLower cfRequireAuthentication of "none" -> Never @@ -231,7 +226,6 @@ extractConfig cfgmap = do "form" -> msum $ formAuthHandlers cfDisableRegistration "github" -> msum $ githubAuthHandlers ghConfig "http" -> msum httpAuthHandlers - "rpx" -> msum rpxAuthHandlers _ -> mzero , userFile = cfUserFile , sessionTimeout = cfSessionTimeout * 60 -- convert minutes -> seconds @@ -264,8 +258,6 @@ extractConfig cfgmap = do , useRecaptcha = cfUseRecaptcha , recaptchaPublicKey = cfRecaptchaPublicKey , recaptchaPrivateKey = cfRecaptchaPrivateKey - , rpxDomain = cfRPXDomain - , rpxKey = cfRPXKey , compressResponses = cfCompressResponses , useCache = cfUseCache , cacheDir = cfCacheDir diff --git a/src/Network/Gitit/Rpxnow.hs b/src/Network/Gitit/Rpxnow.hs deleted file mode 100644 index c54103f14..000000000 --- a/src/Network/Gitit/Rpxnow.hs +++ /dev/null @@ -1,75 +0,0 @@ --- Modified from Michael Snoyman's BSD3 authenticate-0.0.1 --- and http-wget-0.0.1. --- Facilitates authentication with "http://rpxnow.com/". - -module Network.Gitit.Rpxnow - ( Identifier (..) - , authenticate - ) where - -import Text.JSON -import Data.Maybe (isJust, fromJust) -import System.Process -import System.Exit -import System.IO -import Network.HTTP (urlEncodeVars) - --- | Make a post request with parameters to the URL and return a response. -curl :: String -- ^ URL - -> [(String, String)] -- ^ Post parameters - -> IO (Either String String) -- ^ Response body -curl url params = do - (Nothing, Just hout, Just herr, phandle) <- createProcess $ (proc "curl" - [url, "-d", urlEncodeVars params] - ) { std_out = CreatePipe, std_err = CreatePipe } - exitCode <- waitForProcess phandle - case exitCode of - ExitSuccess -> hGetContents hout >>= return . Right - _ -> hGetContents herr >>= return . Left - - - --- | Information received from Rpxnow after a valid login. -data Identifier = Identifier - { userIdentifier :: String - , userData :: [(String, String)] - } - deriving Show - --- | Attempt to log a user in. -authenticate :: String -- ^ API key given by RPXNOW. - -> String -- ^ Token passed by client. - -> IO (Either String Identifier) -authenticate apiKey token = do - body <- curl - "https://rpxnow.com/api/v2/auth_info" - [ ("apiKey", apiKey) - , ("token", token) - ] - case body of - Left s -> return $ Left $ "Unable to connect to rpxnow: " ++ s - Right b -> - case decode b >>= getObject of - Error s -> return $ Left $ "Not a valid JSON response: " ++ s - Ok o -> - case valFromObj "stat" o of - Error _ -> return $ Left "Missing 'stat' field" - Ok "ok" -> return $ resultToEither $ parseProfile o - Ok stat -> return $ Left $ "Login not accepted: " ++ stat - -parseProfile :: JSObject JSValue -> Result Identifier -parseProfile v = do - profile <- valFromObj "profile" v >>= getObject - ident <- valFromObj "identifier" profile - let pairs = fromJSObject profile - pairs' = filter (\(k, _) -> k /= "identifier") pairs - pairs'' = map fromJust . filter isJust . map takeString $ pairs' - return $ Identifier ident pairs'' - -takeString :: (String, JSValue) -> Maybe (String, String) -takeString (k, JSString v) = Just (k, fromJSString v) -takeString _ = Nothing - -getObject :: JSValue -> Result (JSObject JSValue) -getObject (JSObject o) = return o -getObject _ = fail "Not an object" diff --git a/src/Network/Gitit/Types.hs b/src/Network/Gitit/Types.hs index 4b95759cb..cd7bde5f2 100644 --- a/src/Network/Gitit/Types.hs +++ b/src/Network/Gitit/Types.hs @@ -175,9 +175,6 @@ data Config = Config { useRecaptcha :: Bool, recaptchaPublicKey :: String, recaptchaPrivateKey :: String, - -- | RPX domain and key - rpxDomain :: String, - rpxKey :: String, -- | Should responses be compressed? compressResponses :: Bool, -- | Should responses be cached?