From f5f391c66d5c0daae82a5d4b201f9c20e36f0682 Mon Sep 17 00:00:00 2001 From: Stefan Matting Date: Mon, 23 Nov 2020 10:14:38 +0100 Subject: [PATCH 01/12] Fix bug: stern cannot set features (204 expected instead of 200) --- tools/stern/src/Stern/Intra.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/stern/src/Stern/Intra.hs b/tools/stern/src/Stern/Intra.hs index 8ffd65bd5d8..e0226852b5d 100644 --- a/tools/stern/src/Stern/Intra.hs +++ b/tools/stern/src/Stern/Intra.hs @@ -460,7 +460,7 @@ setTeamFeatureFlag tid status = do . contentJson resp <- catchRpcErrors $ rpc' "galley" gly req case statusCode resp of - 204 -> pure () + 200 -> pure () _ -> throwE $ responseJsonUnsafe resp getSearchVisibility :: TeamId -> Handler TeamSearchVisibilityView @@ -494,7 +494,7 @@ setSearchVisibility tid typ = do . contentJson ) case statusCode resp of - 204 -> pure () + 200 -> pure () 403 -> throwE $ Error From 5136d8ac38512fdb1a2c5ff86766cc074a44ab43 Mon Sep 17 00:00:00 2001 From: Stefan Matting Date: Mon, 23 Nov 2020 10:51:50 +0100 Subject: [PATCH 02/12] Fix bug: GET features/app-lock 500 when other feature flags set --- services/galley/src/Galley/Data/TeamFeatures.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/services/galley/src/Galley/Data/TeamFeatures.hs b/services/galley/src/Galley/Data/TeamFeatures.hs index 5f314cf7b68..452171efad2 100644 --- a/services/galley/src/Galley/Data/TeamFeatures.hs +++ b/services/galley/src/Galley/Data/TeamFeatures.hs @@ -87,10 +87,11 @@ getApplockFeatureStatus tid = do let q = query1 (select) (params Quorum (Identity tid)) mTuple <- retry x1 q pure $ - mTuple <&> \(statusValue, enforce, timeout) -> - TeamFeatureStatusWithConfig statusValue (Public.TeamFeatureAppLockConfig enforce timeout) + join $ + mTuple <&> \(mbStatusValue, mbEnforce, mbTimeout) -> + TeamFeatureStatusWithConfig <$> mbStatusValue <*> (Public.TeamFeatureAppLockConfig <$> mbEnforce <*> mbTimeout) where - select :: PrepQuery R (Identity TeamId) (TeamFeatureStatusValue, Public.EnforceAppLock, Int32) + select :: PrepQuery R (Identity TeamId) (Maybe TeamFeatureStatusValue, Maybe Public.EnforceAppLock, Maybe Int32) select = fromString $ "select " <> toCol Public.TeamFeatureAppLock <> ", app_lock_enforce, app_lock_inactivity_timeout_secs " From 1ee793da698a6a4589213339501010536079cc7d Mon Sep 17 00:00:00 2001 From: Stefan Matting Date: Mon, 23 Nov 2020 10:55:43 +0100 Subject: [PATCH 03/12] Fix: TeamFeatureAppLockConfig schema missing from the swagger docs --- libs/wire-api/src/Wire/API/Swagger.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/wire-api/src/Wire/API/Swagger.hs b/libs/wire-api/src/Wire/API/Swagger.hs index 0478b64b3fc..0484c0eb614 100644 --- a/libs/wire-api/src/Wire/API/Swagger.hs +++ b/libs/wire-api/src/Wire/API/Swagger.hs @@ -126,6 +126,7 @@ models = Team.Feature.modelForTeamFeature Team.Feature.TeamFeatureValidateSAMLEmails, Team.Feature.modelForTeamFeature Team.Feature.TeamFeatureDigitalSignatures, Team.Feature.modelForTeamFeature Team.Feature.TeamFeatureAppLock, + Team.Feature.modelTeamFeatureAppLockConfig, Team.Invitation.modelTeamInvitation, Team.Invitation.modelTeamInvitationList, Team.Invitation.modelTeamInvitationRequest, From d40576cc0deefcea41c041529c709963aadeea4b Mon Sep 17 00:00:00 2001 From: Stefan Matting Date: Mon, 23 Nov 2020 11:38:05 +0100 Subject: [PATCH 04/12] Fix: internal feature flag doesnt check existence of team --- services/galley/src/Galley/API/Teams.hs | 4 ++-- services/galley/src/Galley/API/Util.hs | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/services/galley/src/Galley/API/Teams.hs b/services/galley/src/Galley/API/Teams.hs index 021106234f0..5bb00386146 100644 --- a/services/galley/src/Galley/API/Teams.hs +++ b/services/galley/src/Galley/API/Teams.hs @@ -885,7 +885,7 @@ getFeatureStatus getter doauth tid = do zusrMembership <- Data.teamMember tid uid void $ permissionCheck (ViewTeamFeature (Public.knownTeamFeatureName @a)) zusrMembership DontDoAuth -> - pure () + assertTeamExists tid getter tid setFeatureStatus :: @@ -905,7 +905,7 @@ setFeatureStatus setter doauth tid status = do zusrMembership <- Data.teamMember tid uid void $ permissionCheck (ChangeTeamFeature (Public.knownTeamFeatureName @a)) zusrMembership DontDoAuth -> - pure () + assertTeamExists tid setter tid status getSSOStatusInternal :: TeamId -> Galley (Public.TeamFeatureStatus 'Public.TeamFeatureSSO) diff --git a/services/galley/src/Galley/API/Util.hs b/services/galley/src/Galley/API/Util.hs index 59e0fca8083..8eb2e39e903 100644 --- a/services/galley/src/Galley/API/Util.hs +++ b/services/galley/src/Galley/API/Util.hs @@ -145,6 +145,13 @@ permissionCheck p = \case else throwM (operationDenied p) Nothing -> throwM notATeamMember +assertTeamExists :: TeamId -> Galley () +assertTeamExists tid = do + teamExists <- isJust <$> Data.team tid + if teamExists + then pure () + else throwM teamNotFound + assertOnTeam :: UserId -> TeamId -> Galley () assertOnTeam uid tid = do Data.teamMember tid uid >>= \case From a4c4b7ad7a665892bb857bbc464459640d81c918 Mon Sep 17 00:00:00 2001 From: Stefan Matting Date: Mon, 23 Nov 2020 12:39:10 +0100 Subject: [PATCH 05/12] Use camelCase for feature flags , allow deprecated kebab-case --- libs/wire-api/src/Wire/API/Team/Feature.hs | 20 ++++++-- .../brig/test/integration/API/Team/Util.hs | 2 +- services/galley/src/Galley/API/Internal.hs | 24 ++++++--- services/galley/src/Galley/API/Public.hs | 50 +++++++++++-------- services/spar/src/Spar/Intra/Galley.hs | 2 +- .../Test/Spar/Scim/UserSpec.hs | 2 +- 6 files changed, 64 insertions(+), 36 deletions(-) diff --git a/libs/wire-api/src/Wire/API/Team/Feature.hs b/libs/wire-api/src/Wire/API/Team/Feature.hs index e9af1051138..148ba6da952 100644 --- a/libs/wire-api/src/Wire/API/Team/Feature.hs +++ b/libs/wire-api/src/Wire/API/Team/Feature.hs @@ -29,6 +29,7 @@ module Wire.API.Team.Feature KnownTeamFeatureName (..), TeamFeatureStatusNoConfig (..), TeamFeatureStatusWithConfig (..), + deprecatedFeatureName, -- * Swagger typeTeamFeatureName, @@ -89,19 +90,28 @@ instance FromByteString TeamFeatureName where Left e -> fail $ "Invalid TeamFeatureName: " <> show e Right "legalhold" -> pure TeamFeatureLegalHold Right "sso" -> pure TeamFeatureSSO + Right "searchVisibility" -> pure TeamFeatureSearchVisibility Right "search-visibility" -> pure TeamFeatureSearchVisibility + Right "validateSAMLemails" -> pure TeamFeatureValidateSAMLEmails Right "validate-saml-emails" -> pure TeamFeatureValidateSAMLEmails + Right "digitalSignatures" -> pure TeamFeatureDigitalSignatures Right "digital-signatures" -> pure TeamFeatureDigitalSignatures - Right "app-lock" -> pure TeamFeatureAppLock + Right "appLock" -> pure TeamFeatureAppLock Right t -> fail $ "Invalid TeamFeatureName: " <> T.unpack t instance ToByteString TeamFeatureName where builder TeamFeatureLegalHold = "legalhold" builder TeamFeatureSSO = "sso" - builder TeamFeatureSearchVisibility = "search-visibility" - builder TeamFeatureValidateSAMLEmails = "validate-saml-emails" - builder TeamFeatureDigitalSignatures = "digital-signatures" - builder TeamFeatureAppLock = "app-lock" + builder TeamFeatureSearchVisibility = "searchVisibility" + builder TeamFeatureValidateSAMLEmails = "validateSAMLemails" + builder TeamFeatureDigitalSignatures = "digitalSignatures" + builder TeamFeatureAppLock = "appLock" + +deprecatedFeatureName :: TeamFeatureName -> Maybe ByteString +deprecatedFeatureName TeamFeatureSearchVisibility = Just "search-visibility" +deprecatedFeatureName TeamFeatureValidateSAMLEmails = Just "validate-saml-emails" +deprecatedFeatureName TeamFeatureDigitalSignatures = Just "digital-signatures" +deprecatedFeatureName _ = Nothing typeTeamFeatureName :: Doc.DataType typeTeamFeatureName = Doc.string . Doc.enum $ cs . toByteString' <$> [(minBound :: TeamFeatureName) ..] diff --git a/services/brig/test/integration/API/Team/Util.hs b/services/brig/test/integration/API/Team/Util.hs index 1e5d2fda92c..d88a90f8b83 100644 --- a/services/brig/test/integration/API/Team/Util.hs +++ b/services/brig/test/integration/API/Team/Util.hs @@ -448,7 +448,7 @@ setTeamTeamSearchVisibilityAvailable :: HasCallStack => Galley -> TeamId -> Team setTeamTeamSearchVisibilityAvailable galley tid status = put ( galley - . paths ["i/teams", toByteString' tid, "features/search-visibility"] + . paths ["i/teams", toByteString' tid, "features/searchVisibility"] . contentJson . body (RequestBodyLBS . encode $ Public.TeamFeatureStatusNoConfig status) ) diff --git a/services/galley/src/Galley/API/Internal.hs b/services/galley/src/Galley/API/Internal.hs index 7e8fe647370..6550a5238f7 100644 --- a/services/galley/src/Galley/API/Internal.hs +++ b/services/galley/src/Galley/API/Internal.hs @@ -63,7 +63,7 @@ import qualified Network.Wai.Predicate as P import Network.Wai.Routing hiding (route) import Network.Wai.Utilities import Network.Wai.Utilities.ZAuth -import System.Logger.Class hiding (Path) +import System.Logger.Class hiding (Path, name) import qualified Wire.API.Team.Feature as Public sitemap :: Routes a Galley () @@ -336,9 +336,13 @@ mkFeatureGetAndPutRoute getter setter = do getHandler (tid ::: _) = json <$> Teams.getFeatureStatus @a getter DontDoAuth tid - get ("/i/teams/:tid/features/" <> toByteString' featureName) (continue getHandler) $ - capture "tid" - .&. accept "application" "json" + let mkGetRoute name = + get ("/i/teams/:tid/features/" <> name) (continue getHandler) $ + capture "tid" + .&. accept "application" "json" + + mkGetRoute (toByteString' featureName) + mkGetRoute `mapM_` (Public.deprecatedFeatureName featureName) let putHandler :: TeamId ::: JsonRequest (Public.TeamFeatureStatus a) ::: JSON -> Galley Response putHandler (tid ::: req ::: _) = do @@ -346,7 +350,11 @@ mkFeatureGetAndPutRoute getter setter = do res <- Teams.setFeatureStatus @a setter DontDoAuth tid status pure $ (json res) & Network.Wai.Utilities.setStatus status200 - put ("/i/teams/:tid/features/" <> toByteString' featureName) (continue putHandler) $ - capture "tid" - .&. jsonRequest @(Public.TeamFeatureStatus a) - .&. accept "application" "json" + let mkPutRoute name = + put ("/i/teams/:tid/features/" <> name) (continue putHandler) $ + capture "tid" + .&. jsonRequest @(Public.TeamFeatureStatus a) + .&. accept "application" "json" + + mkPutRoute (toByteString' featureName) + mkPutRoute `mapM_` (Public.deprecatedFeatureName featureName) diff --git a/services/galley/src/Galley/API/Public.hs b/services/galley/src/Galley/API/Public.hs index 14f894df461..077b2beb841 100644 --- a/services/galley/src/Galley/API/Public.hs +++ b/services/galley/src/Galley/API/Public.hs @@ -1089,15 +1089,20 @@ mkFeatureGetAndPutRoute getter setter = do getHandler (uid ::: tid ::: _) = json <$> Teams.getFeatureStatus @a getter (DoAuth uid) tid - get ("/teams/:tid/features/" <> toByteString' featureName) (continue getHandler) $ - zauthUserId - .&. capture "tid" - .&. accept "application" "json" - document "GET" "getTeamFeature" $ do - parameter Path "tid" bytes' $ - description "Team ID" - returns (ref (Public.modelForTeamFeature featureName)) - response 200 "Team feature status" end + let mkGetRoute makeDocumentation name = do + get ("/teams/:tid/features/" <> name) (continue getHandler) $ + zauthUserId + .&. capture "tid" + .&. accept "application" "json" + when makeDocumentation $ + document "GET" "getTeamFeature" $ do + parameter Path "tid" bytes' $ + description "Team ID" + returns (ref (Public.modelForTeamFeature featureName)) + response 200 "Team feature status" end + + mkGetRoute True (toByteString' featureName) + mkGetRoute False `mapM_` Public.deprecatedFeatureName featureName let putHandler :: UserId ::: TeamId ::: JsonRequest (Public.TeamFeatureStatus a) ::: JSON -> Galley Response putHandler (uid ::: tid ::: req ::: _) = do @@ -1105,14 +1110,19 @@ mkFeatureGetAndPutRoute getter setter = do res <- Teams.setFeatureStatus @a setter (DoAuth uid) tid status pure $ (json res) & Network.Wai.Utilities.setStatus status200 - put ("/teams/:tid/features/" <> toByteString' featureName) (continue putHandler) $ - zauthUserId - .&. capture "tid" - .&. jsonRequest @(Public.TeamFeatureStatus a) - .&. accept "application" "json" - document "PUT" "putTeamFeature" $ do - parameter Path "tid" bytes' $ - description "Team ID" - body (ref (Public.modelForTeamFeature featureName)) $ - description "JSON body" - response 204 "Team feature status" end + let mkPutRoute makeDocumentation name = do + put ("/teams/:tid/features/" <> name) (continue putHandler) $ + zauthUserId + .&. capture "tid" + .&. jsonRequest @(Public.TeamFeatureStatus a) + .&. accept "application" "json" + when makeDocumentation $ + document "PUT" "putTeamFeature" $ do + parameter Path "tid" bytes' $ + description "Team ID" + body (ref (Public.modelForTeamFeature featureName)) $ + description "JSON body" + response 204 "Team feature status" end + + mkPutRoute True (toByteString' featureName) + mkGetRoute False `mapM_` Public.deprecatedFeatureName featureName diff --git a/services/spar/src/Spar/Intra/Galley.hs b/services/spar/src/Spar/Intra/Galley.hs index 61a355978d9..7fdfa6670e4 100644 --- a/services/spar/src/Spar/Intra/Galley.hs +++ b/services/spar/src/Spar/Intra/Galley.hs @@ -94,7 +94,7 @@ assertSSOEnabled tid = do isEmailValidationEnabledTeam :: (HasCallStack, MonadSparToGalley m) => TeamId -> m Bool isEmailValidationEnabledTeam tid = do - resp <- call $ method GET . paths ["i", "teams", toByteString' tid, "features", "validate-saml-emails"] + resp <- call $ method GET . paths ["i", "teams", toByteString' tid, "features", "validateSAMLemails"] pure ( (statusCode resp == 200) && ( responseJsonMaybe @(TeamFeatureStatus 'TeamFeatureValidateSAMLEmails) resp diff --git a/services/spar/test-integration/Test/Spar/Scim/UserSpec.hs b/services/spar/test-integration/Test/Spar/Scim/UserSpec.hs index 369de4c11b2..28d37362642 100644 --- a/services/spar/test-integration/Test/Spar/Scim/UserSpec.hs +++ b/services/spar/test-integration/Test/Spar/Scim/UserSpec.hs @@ -1525,7 +1525,7 @@ specEmailValidation = do enableSamlEmailValidation tid = do galley <- asks (^. teGalley) let req = put $ galley . paths p . json (Feature.TeamFeatureStatusNoConfig Feature.TeamFeatureEnabled) - p = ["/i/teams", toByteString' tid, "features", "validate-saml-emails"] + p = ["/i/teams", toByteString' tid, "features", "validateSAMLemails"] call req !!! const 200 === statusCode -- (This may be the same as 'Util.Email.checkEmail'.) From a191998041e53d20643e868f0cc9cb51433a1558 Mon Sep 17 00:00:00 2001 From: Stefan Matting Date: Mon, 23 Nov 2020 13:40:15 +0100 Subject: [PATCH 06/12] Add features/ endpoint --- services/galley/src/Galley/API/Public.hs | 10 +++++++ services/galley/src/Galley/API/Teams.hs | 33 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/services/galley/src/Galley/API/Public.hs b/services/galley/src/Galley/API/Public.hs index 077b2beb841..a50a81e81be 100644 --- a/services/galley/src/Galley/API/Public.hs +++ b/services/galley/src/Galley/API/Public.hs @@ -458,6 +458,16 @@ sitemap = do mkFeatureGetAndPutRoute @'Public.TeamFeatureDigitalSignatures Teams.getDigitalSignaturesInternal Teams.setDigitalSignaturesInternal mkFeatureGetAndPutRoute @'Public.TeamFeatureAppLock Teams.getAppLockInternal Teams.setAppLockInternal + get "/teams/:tid/features/" (continue Teams.getAllFeaturesH) $ + zauthUserId + .&. capture "tid" + .&. accept "application" "json" + document "GET" "getAllFeatures" $ do + summary "Shows the configuration status of every team feature" + parameter Path "tid" bytes' $ + description "Team ID" + response 200 "All feature statuses" end + -- Custom Backend API ------------------------------------------------- get "/custom-backend/by-domain/:domain" (continue CustomBackend.getCustomBackendByDomainH) $ diff --git a/services/galley/src/Galley/API/Teams.hs b/services/galley/src/Galley/API/Teams.hs index 5bb00386146..8d2d70b2dec 100644 --- a/services/galley/src/Galley/API/Teams.hs +++ b/services/galley/src/Galley/API/Teams.hs @@ -54,6 +54,7 @@ module Galley.API.Teams internalDeleteBindingTeamWithOneMember, getFeatureStatus, setFeatureStatus, + getAllFeaturesH, getSSOStatusInternal, setSSOStatusInternal, getLegalholdStatusInternal, @@ -73,6 +74,7 @@ where import Brig.Types.Team (TeamSize (..)) import Control.Lens import Control.Monad.Catch +import qualified Data.Aeson as Aeson import Data.ByteString.Conversion hiding (fromList) import Data.Id import qualified Data.Id as Id @@ -82,6 +84,7 @@ import Data.List1 (list1) import Data.Range as Range import Data.Set (fromList) import qualified Data.Set as Set +import Data.String.Conversions (cs) import Data.Time.Clock (UTCTime (..), getCurrentTime) import qualified Data.UUID as UUID import qualified Data.UUID.Util as UUID @@ -908,6 +911,36 @@ setFeatureStatus setter doauth tid status = do assertTeamExists tid setter tid status +getAllFeaturesH :: UserId ::: TeamId ::: JSON -> Galley Response +getAllFeaturesH (uid ::: tid ::: _) = + json <$> getAllFeatures uid tid + +getAllFeatures :: UserId -> TeamId -> Galley Aeson.Value +getAllFeatures uid tid = do + Aeson.object + <$> sequence + [ w @'Public.TeamFeatureSSO getSSOStatusInternal, + w @'Public.TeamFeatureLegalHold getLegalholdStatusInternal, + w @'Public.TeamFeatureSearchVisibility getTeamSearchVisibilityAvailableInternal, + w @'Public.TeamFeatureValidateSAMLEmails getValidateSAMLEmailsInternal, + w @'Public.TeamFeatureDigitalSignatures getDigitalSignaturesInternal, + w @'Public.TeamFeatureAppLock getAppLockInternal + ] + where + w :: + forall (a :: Public.TeamFeatureName). + ( Public.KnownTeamFeatureName a, + Aeson.ToJSON (Public.TeamFeatureStatus a) + ) => + ( TeamId -> + Galley (Public.TeamFeatureStatus a) + ) -> + Galley (Text, Aeson.Value) + w getter = do + status <- getFeatureStatus @a getter (DoAuth uid) tid + let feature = Public.knownTeamFeatureName @a + pure $ (cs . toByteString' $ feature, Aeson.toJSON status) + getSSOStatusInternal :: TeamId -> Galley (Public.TeamFeatureStatus 'Public.TeamFeatureSSO) getSSOStatusInternal tid = do defStatus <- do From 4cc033a86d9b96d1dab1330ccdcc26e09fbf2e9c Mon Sep 17 00:00:00 2001 From: Stefan Matting Date: Mon, 23 Nov 2020 14:56:02 +0100 Subject: [PATCH 07/12] move appLock defaults hardcoded -> config file --- .../conf/galley.demo-docker.yaml | 6 +++++ deploy/services-demo/conf/galley.demo.yaml | 6 +++++ libs/galley-types/src/Galley/Types/Teams.hs | 22 ++++++++++++++++--- services/galley/galley.integration.yaml | 6 +++++ services/galley/src/Galley/API/Teams.hs | 5 +---- 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/deploy/services-demo/conf/galley.demo-docker.yaml b/deploy/services-demo/conf/galley.demo-docker.yaml index 5d548e9f9a1..7c5da37ae6b 100644 --- a/deploy/services-demo/conf/galley.demo-docker.yaml +++ b/deploy/services-demo/conf/galley.demo-docker.yaml @@ -33,6 +33,12 @@ settings: sso: disabled-by-default legalhold: disabled-by-default teamSearchVisibility: disabled-by-default + appLock: + defaults: + status: enabled + config: + enforceAppLock: false + inactivityTimeoutSecs: 60 logLevel: Info logNetStrings: false diff --git a/deploy/services-demo/conf/galley.demo.yaml b/deploy/services-demo/conf/galley.demo.yaml index 9dbce0463c4..55d2382a226 100644 --- a/deploy/services-demo/conf/galley.demo.yaml +++ b/deploy/services-demo/conf/galley.demo.yaml @@ -33,6 +33,12 @@ settings: sso: disabled-by-default legalhold: disabled-by-default teamSearchVisibility: disabled-by-default + appLock: + defaults: + status: enabled + config: + enforceAppLock: false + inactivityTimeoutSecs: 60 logLevel: Info logNetStrings: false diff --git a/libs/galley-types/src/Galley/Types/Teams.hs b/libs/galley-types/src/Galley/Types/Teams.hs index 3ebeb0d14e5..5525d5f6cc2 100644 --- a/libs/galley-types/src/Galley/Types/Teams.hs +++ b/libs/galley-types/src/Galley/Types/Teams.hs @@ -27,6 +27,8 @@ module Galley.Types.Teams flagSSO, flagLegalHold, flagTeamSearchVisibility, + flagAppLockDefaults, + Defaults (..), FeatureSSO (..), FeatureLegalHold (..), FeatureTeamSearchVisibility (..), @@ -196,10 +198,22 @@ newtype TeamCreationTime = TeamCreationTime data FeatureFlags = FeatureFlags { _flagSSO :: !FeatureSSO, _flagLegalHold :: !FeatureLegalHold, - _flagTeamSearchVisibility :: !FeatureTeamSearchVisibility + _flagTeamSearchVisibility :: !FeatureTeamSearchVisibility, + _flagAppLockDefaults :: !(Defaults (TeamFeatureStatus 'TeamFeatureAppLock)) } deriving (Eq, Show, Generic) +newtype Defaults a = Defaults {_unDefaults :: a} + deriving (Eq, Ord, Show, Enum, Bounded, Generic) + +instance FromJSON a => FromJSON (Defaults a) where + parseJSON = withObject "default object" $ \ob -> + Defaults <$> (ob .: "defaults") + +instance ToJSON a => ToJSON (Defaults a) where + toJSON (Defaults x) = + object ["defaults" .= toJSON x] + data FeatureSSO = FeatureSSOEnabledByDefault | FeatureSSODisabledByDefault @@ -225,13 +239,15 @@ instance FromJSON FeatureFlags where <$> obj .: "sso" <*> obj .: "legalhold" <*> obj .: "teamSearchVisibility" + <*> obj .: "appLock" instance ToJSON FeatureFlags where - toJSON (FeatureFlags sso legalhold searchVisibility) = + toJSON (FeatureFlags sso legalhold searchVisibility appLock) = object $ [ "sso" .= sso, "legalhold" .= legalhold, - "teamSearchVisibility" .= searchVisibility + "teamSearchVisibility" .= searchVisibility, + "appLock" .= appLock ] instance FromJSON FeatureSSO where diff --git a/services/galley/galley.integration.yaml b/services/galley/galley.integration.yaml index cff6b2b0c8b..2d5d3fde96a 100644 --- a/services/galley/galley.integration.yaml +++ b/services/galley/galley.integration.yaml @@ -41,6 +41,12 @@ settings: sso: disabled-by-default legalhold: disabled-by-default teamSearchVisibility: disabled-by-default + appLock: + defaults: + status: enabled + config: + enforceAppLock: false + inactivityTimeoutSecs: 60 logLevel: Info logNetStrings: false diff --git a/services/galley/src/Galley/API/Teams.hs b/services/galley/src/Galley/API/Teams.hs index 8d2d70b2dec..33e1fa4a1c1 100644 --- a/services/galley/src/Galley/API/Teams.hs +++ b/services/galley/src/Galley/API/Teams.hs @@ -1041,10 +1041,7 @@ getSearchVisibilityInternalH (tid ::: _) = getAppLockInternal :: TeamId -> Galley (Public.TeamFeatureStatus 'Public.TeamFeatureAppLock) getAppLockInternal tid = do - let defaultStatus = - Public.TeamFeatureStatusWithConfig - Public.TeamFeatureEnabled - (Public.TeamFeatureAppLockConfig (Public.EnforceAppLock False) 60) + Defaults defaultStatus <- view (options . optSettings . setFeatureFlags . flagAppLockDefaults) status <- TeamFeatures.getApplockFeatureStatus tid pure $ fromMaybe defaultStatus status From 04bedcf792e00671da484e63300e1ec18d6126af Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Mon, 23 Nov 2020 16:01:58 +0100 Subject: [PATCH 08/12] Revert "move appLock defaults hardcoded -> config file" This reverts commit 4cc033a86d9b96d1dab1330ccdcc26e09fbf2e9c. --- .../conf/galley.demo-docker.yaml | 6 ----- deploy/services-demo/conf/galley.demo.yaml | 6 ----- libs/galley-types/src/Galley/Types/Teams.hs | 22 +++---------------- services/galley/galley.integration.yaml | 6 ----- services/galley/src/Galley/API/Teams.hs | 5 ++++- 5 files changed, 7 insertions(+), 38 deletions(-) diff --git a/deploy/services-demo/conf/galley.demo-docker.yaml b/deploy/services-demo/conf/galley.demo-docker.yaml index 7c5da37ae6b..5d548e9f9a1 100644 --- a/deploy/services-demo/conf/galley.demo-docker.yaml +++ b/deploy/services-demo/conf/galley.demo-docker.yaml @@ -33,12 +33,6 @@ settings: sso: disabled-by-default legalhold: disabled-by-default teamSearchVisibility: disabled-by-default - appLock: - defaults: - status: enabled - config: - enforceAppLock: false - inactivityTimeoutSecs: 60 logLevel: Info logNetStrings: false diff --git a/deploy/services-demo/conf/galley.demo.yaml b/deploy/services-demo/conf/galley.demo.yaml index 55d2382a226..9dbce0463c4 100644 --- a/deploy/services-demo/conf/galley.demo.yaml +++ b/deploy/services-demo/conf/galley.demo.yaml @@ -33,12 +33,6 @@ settings: sso: disabled-by-default legalhold: disabled-by-default teamSearchVisibility: disabled-by-default - appLock: - defaults: - status: enabled - config: - enforceAppLock: false - inactivityTimeoutSecs: 60 logLevel: Info logNetStrings: false diff --git a/libs/galley-types/src/Galley/Types/Teams.hs b/libs/galley-types/src/Galley/Types/Teams.hs index 5525d5f6cc2..3ebeb0d14e5 100644 --- a/libs/galley-types/src/Galley/Types/Teams.hs +++ b/libs/galley-types/src/Galley/Types/Teams.hs @@ -27,8 +27,6 @@ module Galley.Types.Teams flagSSO, flagLegalHold, flagTeamSearchVisibility, - flagAppLockDefaults, - Defaults (..), FeatureSSO (..), FeatureLegalHold (..), FeatureTeamSearchVisibility (..), @@ -198,22 +196,10 @@ newtype TeamCreationTime = TeamCreationTime data FeatureFlags = FeatureFlags { _flagSSO :: !FeatureSSO, _flagLegalHold :: !FeatureLegalHold, - _flagTeamSearchVisibility :: !FeatureTeamSearchVisibility, - _flagAppLockDefaults :: !(Defaults (TeamFeatureStatus 'TeamFeatureAppLock)) + _flagTeamSearchVisibility :: !FeatureTeamSearchVisibility } deriving (Eq, Show, Generic) -newtype Defaults a = Defaults {_unDefaults :: a} - deriving (Eq, Ord, Show, Enum, Bounded, Generic) - -instance FromJSON a => FromJSON (Defaults a) where - parseJSON = withObject "default object" $ \ob -> - Defaults <$> (ob .: "defaults") - -instance ToJSON a => ToJSON (Defaults a) where - toJSON (Defaults x) = - object ["defaults" .= toJSON x] - data FeatureSSO = FeatureSSOEnabledByDefault | FeatureSSODisabledByDefault @@ -239,15 +225,13 @@ instance FromJSON FeatureFlags where <$> obj .: "sso" <*> obj .: "legalhold" <*> obj .: "teamSearchVisibility" - <*> obj .: "appLock" instance ToJSON FeatureFlags where - toJSON (FeatureFlags sso legalhold searchVisibility appLock) = + toJSON (FeatureFlags sso legalhold searchVisibility) = object $ [ "sso" .= sso, "legalhold" .= legalhold, - "teamSearchVisibility" .= searchVisibility, - "appLock" .= appLock + "teamSearchVisibility" .= searchVisibility ] instance FromJSON FeatureSSO where diff --git a/services/galley/galley.integration.yaml b/services/galley/galley.integration.yaml index 2d5d3fde96a..cff6b2b0c8b 100644 --- a/services/galley/galley.integration.yaml +++ b/services/galley/galley.integration.yaml @@ -41,12 +41,6 @@ settings: sso: disabled-by-default legalhold: disabled-by-default teamSearchVisibility: disabled-by-default - appLock: - defaults: - status: enabled - config: - enforceAppLock: false - inactivityTimeoutSecs: 60 logLevel: Info logNetStrings: false diff --git a/services/galley/src/Galley/API/Teams.hs b/services/galley/src/Galley/API/Teams.hs index 33e1fa4a1c1..8d2d70b2dec 100644 --- a/services/galley/src/Galley/API/Teams.hs +++ b/services/galley/src/Galley/API/Teams.hs @@ -1041,7 +1041,10 @@ getSearchVisibilityInternalH (tid ::: _) = getAppLockInternal :: TeamId -> Galley (Public.TeamFeatureStatus 'Public.TeamFeatureAppLock) getAppLockInternal tid = do - Defaults defaultStatus <- view (options . optSettings . setFeatureFlags . flagAppLockDefaults) + let defaultStatus = + Public.TeamFeatureStatusWithConfig + Public.TeamFeatureEnabled + (Public.TeamFeatureAppLockConfig (Public.EnforceAppLock False) 60) status <- TeamFeatures.getApplockFeatureStatus tid pure $ fromMaybe defaultStatus status From f0f6f3d6dbbae2d763403d180482ddeffffb4142 Mon Sep 17 00:00:00 2001 From: Stefan Matting Date: Mon, 23 Nov 2020 16:47:07 +0100 Subject: [PATCH 09/12] Update services/galley/src/Galley/API/Internal.hs Co-authored-by: fisx --- services/galley/src/Galley/API/Internal.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/galley/src/Galley/API/Internal.hs b/services/galley/src/Galley/API/Internal.hs index 6550a5238f7..36aca0bc50e 100644 --- a/services/galley/src/Galley/API/Internal.hs +++ b/services/galley/src/Galley/API/Internal.hs @@ -342,7 +342,7 @@ mkFeatureGetAndPutRoute getter setter = do .&. accept "application" "json" mkGetRoute (toByteString' featureName) - mkGetRoute `mapM_` (Public.deprecatedFeatureName featureName) + mkGetRoute `mapM_` Public.deprecatedFeatureName featureName let putHandler :: TeamId ::: JsonRequest (Public.TeamFeatureStatus a) ::: JSON -> Galley Response putHandler (tid ::: req ::: _) = do From f6727fb21b712947b2d15ed26a713a4f48edbbb6 Mon Sep 17 00:00:00 2001 From: Stefan Matting Date: Mon, 23 Nov 2020 16:47:15 +0100 Subject: [PATCH 10/12] Update services/galley/src/Galley/API/Internal.hs Co-authored-by: fisx --- services/galley/src/Galley/API/Internal.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/galley/src/Galley/API/Internal.hs b/services/galley/src/Galley/API/Internal.hs index 36aca0bc50e..7d86b0c7fc8 100644 --- a/services/galley/src/Galley/API/Internal.hs +++ b/services/galley/src/Galley/API/Internal.hs @@ -357,4 +357,4 @@ mkFeatureGetAndPutRoute getter setter = do .&. accept "application" "json" mkPutRoute (toByteString' featureName) - mkPutRoute `mapM_` (Public.deprecatedFeatureName featureName) + mkPutRoute `mapM_` Public.deprecatedFeatureName featureName From 8a61f3c5f8e9e2fb587d61c888df6f43e6d466cb Mon Sep 17 00:00:00 2001 From: Stefan Matting Date: Mon, 23 Nov 2020 16:46:20 +0100 Subject: [PATCH 11/12] equivalent by category theory ;) --- services/galley/src/Galley/Data/TeamFeatures.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/services/galley/src/Galley/Data/TeamFeatures.hs b/services/galley/src/Galley/Data/TeamFeatures.hs index 452171efad2..d58df01101f 100644 --- a/services/galley/src/Galley/Data/TeamFeatures.hs +++ b/services/galley/src/Galley/Data/TeamFeatures.hs @@ -87,9 +87,8 @@ getApplockFeatureStatus tid = do let q = query1 (select) (params Quorum (Identity tid)) mTuple <- retry x1 q pure $ - join $ - mTuple <&> \(mbStatusValue, mbEnforce, mbTimeout) -> - TeamFeatureStatusWithConfig <$> mbStatusValue <*> (Public.TeamFeatureAppLockConfig <$> mbEnforce <*> mbTimeout) + mTuple >>= \(mbStatusValue, mbEnforce, mbTimeout) -> + TeamFeatureStatusWithConfig <$> mbStatusValue <*> (Public.TeamFeatureAppLockConfig <$> mbEnforce <*> mbTimeout) where select :: PrepQuery R (Identity TeamId) (Maybe TeamFeatureStatusValue, Maybe Public.EnforceAppLock, Maybe Int32) select = From 41b23d556fd27f1a068bfc9a99f93e51a8be8089 Mon Sep 17 00:00:00 2001 From: Stefan Matting Date: Mon, 23 Nov 2020 16:54:19 +0100 Subject: [PATCH 12/12] apply suggestions from PR review --- services/galley/src/Galley/API/Teams.hs | 26 +++++++++++-------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/services/galley/src/Galley/API/Teams.hs b/services/galley/src/Galley/API/Teams.hs index 8d2d70b2dec..2b34f0797bb 100644 --- a/services/galley/src/Galley/API/Teams.hs +++ b/services/galley/src/Galley/API/Teams.hs @@ -919,27 +919,23 @@ getAllFeatures :: UserId -> TeamId -> Galley Aeson.Value getAllFeatures uid tid = do Aeson.object <$> sequence - [ w @'Public.TeamFeatureSSO getSSOStatusInternal, - w @'Public.TeamFeatureLegalHold getLegalholdStatusInternal, - w @'Public.TeamFeatureSearchVisibility getTeamSearchVisibilityAvailableInternal, - w @'Public.TeamFeatureValidateSAMLEmails getValidateSAMLEmailsInternal, - w @'Public.TeamFeatureDigitalSignatures getDigitalSignaturesInternal, - w @'Public.TeamFeatureAppLock getAppLockInternal + [ getStatus @'Public.TeamFeatureSSO getSSOStatusInternal, + getStatus @'Public.TeamFeatureLegalHold getLegalholdStatusInternal, + getStatus @'Public.TeamFeatureSearchVisibility getTeamSearchVisibilityAvailableInternal, + getStatus @'Public.TeamFeatureValidateSAMLEmails getValidateSAMLEmailsInternal, + getStatus @'Public.TeamFeatureDigitalSignatures getDigitalSignaturesInternal, + getStatus @'Public.TeamFeatureAppLock getAppLockInternal ] where - w :: + getStatus :: forall (a :: Public.TeamFeatureName). - ( Public.KnownTeamFeatureName a, - Aeson.ToJSON (Public.TeamFeatureStatus a) - ) => - ( TeamId -> - Galley (Public.TeamFeatureStatus a) - ) -> + (Public.KnownTeamFeatureName a, Aeson.ToJSON (Public.TeamFeatureStatus a)) => + (TeamId -> Galley (Public.TeamFeatureStatus a)) -> Galley (Text, Aeson.Value) - w getter = do + getStatus getter = do status <- getFeatureStatus @a getter (DoAuth uid) tid let feature = Public.knownTeamFeatureName @a - pure $ (cs . toByteString' $ feature, Aeson.toJSON status) + pure $ (cs (toByteString' feature) Aeson..= status) getSSOStatusInternal :: TeamId -> Galley (Public.TeamFeatureStatus 'Public.TeamFeatureSSO) getSSOStatusInternal tid = do