Skip to content

Commit

Permalink
Simplify team feature database interface; add validate-saml-emails fe…
Browse files Browse the repository at this point in the history
…ature to stern/backoffice (#1129)

* Simplify team feature database interface.

There was one Data.* module per column in the team_features table.
That may make sense at some point in the distant future, but as long
as all those columns are just booleans, having one haskell module to
access any of them in a uniform way saves a lot of boilerplate.

* Use new feature flag types and make a generic end-point in stern.

* Add FUTUREWORKs.

Co-authored-by: Akshay Mankar <akshay@wire.com>
  • Loading branch information
fisx and akshaymankar authored Jun 8, 2020
1 parent ce3bb4f commit 869c7ac
Show file tree
Hide file tree
Showing 15 changed files with 121 additions and 389 deletions.
3 changes: 2 additions & 1 deletion docs/reference/cassandra-schema.cql
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ CREATE TABLE galley_test.team_features (
team_id uuid PRIMARY KEY,
legalhold_status int,
search_visibility_status int,
sso_status int
sso_status int,
validate_saml_emails int
) WITH bloom_filter_fp_chance = 0.1
AND caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'}
AND comment = ''
Expand Down
5 changes: 2 additions & 3 deletions services/galley/galley.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ cabal-version: 1.12
--
-- see: https://github.com/sol/hpack
--
-- hash: ea8a2731dc5ada81ea4a4bfed991318a463ea1bdc8af1b5ae12836565ce2cb19
-- hash: 92456c44f0413d6ce65ee41f0b151adcc8a84a465cd9b05e3be17b1917c8f551

name: galley
version: 0.83.0
Expand Down Expand Up @@ -48,10 +48,9 @@ library
Galley.Data.Queries
Galley.Data.SearchVisibility
Galley.Data.Services
Galley.Data.SSO
Galley.Data.TeamFeatures
Galley.Data.TeamNotifications
Galley.Data.Types
Galley.Data.ValidateSAMLEmails
Galley.External
Galley.External.LegalHoldService
Galley.Intra.Client
Expand Down
3 changes: 2 additions & 1 deletion services/galley/src/Galley/API/LegalHold.hs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import Galley.API.Util
import Galley.App
import qualified Galley.Data as Data
import qualified Galley.Data.LegalHold as LegalHoldData
import qualified Galley.Data.TeamFeatures as TeamFeatures
import qualified Galley.External.LegalHoldService as LHService
import qualified Galley.Intra.Client as Client
import Galley.Types.Teams as Team
Expand All @@ -60,7 +61,7 @@ assertLegalHoldEnabled tid = unlessM (isLegalHoldEnabled tid) $ throwM legalHold

isLegalHoldEnabled :: TeamId -> Galley Bool
isLegalHoldEnabled tid = do
lhConfig <- LegalHoldData.getLegalHoldTeamConfig tid
lhConfig <- TeamFeatures.getFlag tid Public.TeamFeatureLegalHold
return $ case lhConfig of
Just Public.TeamFeatureEnabled -> True
Just Public.TeamFeatureDisabled -> False
Expand Down
27 changes: 13 additions & 14 deletions services/galley/src/Galley/API/Teams.hs
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,10 @@ import qualified Galley.API.TeamNotifications as APITeamQueue
import Galley.API.Util
import Galley.App
import qualified Galley.Data as Data
import qualified Galley.Data.LegalHold as LegalHoldData
import qualified Galley.Data.SSO as SSOData
import qualified Galley.Data.SearchVisibility as SearchVisibilityData
import Galley.Data.Services (BotMember)
import qualified Galley.Data.TeamFeatures as TeamFeatures
import qualified Galley.Data.Types as Data
import qualified Galley.Data.ValidateSAMLEmails as ValidateSAMLEmailsData
import qualified Galley.External as External
import qualified Galley.Intra.Journal as Journal
import Galley.Intra.Push
Expand Down Expand Up @@ -896,22 +894,22 @@ getSSOStatusInternal tid = do
pure $ case featureSSO of
FeatureSSOEnabledByDefault -> Public.TeamFeatureEnabled
FeatureSSODisabledByDefault -> Public.TeamFeatureDisabled
ssoTeamConfig <- SSOData.getSSOTeamConfig tid
ssoTeamConfig <- TeamFeatures.getFlag tid Public.TeamFeatureSSO
pure . fromMaybe defConfig $ ssoTeamConfig

setSSOStatusInternal :: TeamId -> Public.TeamFeatureStatus -> Galley ()
setSSOStatusInternal tid ssoTeamConfig = do
case ssoTeamConfig of
Public.TeamFeatureDisabled -> throwM disableSsoNotImplemented
Public.TeamFeatureEnabled -> pure () -- this one is easy to implement :)
SSOData.setSSOTeamConfig tid ssoTeamConfig
TeamFeatures.setFlag tid Public.TeamFeatureSSO ssoTeamConfig

getLegalholdStatusInternal :: TeamId -> Galley Public.TeamFeatureStatus
getLegalholdStatusInternal tid = do
featureLegalHold <- view (options . optSettings . setFeatureFlags . flagLegalHold)
case featureLegalHold of
FeatureLegalHoldDisabledByDefault -> do
legalHoldTeamConfig <- LegalHoldData.getLegalHoldTeamConfig tid
legalHoldTeamConfig <- TeamFeatures.getFlag tid Public.TeamFeatureLegalHold
pure (fromMaybe Public.TeamFeatureDisabled legalHoldTeamConfig)
FeatureLegalHoldDisabledPermanently -> do
pure Public.TeamFeatureDisabled
Expand All @@ -929,7 +927,7 @@ setLegalholdStatusInternal tid legalHoldTeamConfig = do
Public.TeamFeatureDisabled -> removeSettings' tid
-- FUTUREWORK: We cannot enable legalhold on large teams right now
Public.TeamFeatureEnabled -> checkTeamSize
LegalHoldData.setLegalHoldTeamConfig tid legalHoldTeamConfig
TeamFeatures.setFlag tid Public.TeamFeatureLegalHold legalHoldTeamConfig
where
checkTeamSize = do
(TeamSize size) <- BrigTeam.getSize tid
Expand All @@ -945,23 +943,24 @@ getTeamSearchVisibilityAvailableInternal tid = do
pure $ case featureTeamSearchVisibility of
FeatureTeamSearchVisibilityEnabledByDefault -> Public.TeamFeatureEnabled
FeatureTeamSearchVisibilityDisabledByDefault -> Public.TeamFeatureDisabled
fromMaybe defConfig <$> SearchVisibilityData.getTeamSearchVisibilityAvailable tid
fromMaybe defConfig <$> TeamFeatures.getFlag tid Public.TeamFeatureSearchVisibility

setTeamSearchVisibilityAvailableInternal :: TeamId -> Public.TeamFeatureStatus -> Galley ()
setTeamSearchVisibilityAvailableInternal tid isenabled = do
case isenabled of
Public.TeamFeatureDisabled -> SearchVisibilityData.resetSearchVisibility tid
Public.TeamFeatureEnabled -> pure () -- This allows the option to be set at the team level
SearchVisibilityData.setTeamSearchVisibilityAvailable tid isenabled
TeamFeatures.setFlag tid Public.TeamFeatureSearchVisibility isenabled

getValidateSAMLEmailsInternal :: TeamId -> Galley Public.TeamFeatureStatus
getValidateSAMLEmailsInternal =
ValidateSAMLEmailsData.getValidateSAMLEmails >=> \case
Nothing -> throwM teamNotFound
Just s -> pure s
getValidateSAMLEmailsInternal tid =
-- FUTUREWORK: we may also want to get a default from the server config file here, like for
-- sso, and team search visibility.
fromMaybe Public.TeamFeatureDisabled
<$> TeamFeatures.getFlag tid Public.TeamFeatureValidateSAMLEmails

setValidateSAMLEmailsInternal :: TeamId -> Public.TeamFeatureStatus -> Galley ()
setValidateSAMLEmailsInternal = ValidateSAMLEmailsData.setValidateSAMLEmails
setValidateSAMLEmailsInternal tid = TeamFeatures.setFlag tid Public.TeamFeatureValidateSAMLEmails

-- | Modify and get visibility type for a team (internal, no user permission checks)
getSearchVisibilityInternalH :: TeamId ::: JSON -> Galley Response
Expand Down
19 changes: 1 addition & 18 deletions services/galley/src/Galley/Data/LegalHold.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
-- with this program. If not, see <https://www.gnu.org/licenses/>.

module Galley.Data.LegalHold
( setLegalHoldTeamConfig,
getLegalHoldTeamConfig,
createSettings,
( createSettings,
getSettings,
removeSettings,
Galley.Data.LegalHold.insertPendingPrekeys,
Expand All @@ -40,21 +38,6 @@ import Data.LegalHold
import Galley.Data.Instances ()
import Galley.Data.Queries as Q
import Imports
import Wire.API.Team.Feature (TeamFeatureStatus (..))

-- | Return whether a given team is allowed to enable/disable legalhold
-- Defaults to 'TeamFeatureDisabled'.
getLegalHoldTeamConfig :: MonadClient m => TeamId -> m (Maybe TeamFeatureStatus)
getLegalHoldTeamConfig tid = fmap toLegalHoldTeamConfig <$> do
retry x1 $ query1 selectLegalHoldTeamConfig (params Quorum (Identity tid))
where
toLegalHoldTeamConfig (Identity Nothing) = TeamFeatureDisabled
toLegalHoldTeamConfig (Identity (Just status)) = status

-- | Determines whether a given team is allowed to enable/disable legalhold
setLegalHoldTeamConfig :: MonadClient m => TeamId -> TeamFeatureStatus -> m ()
setLegalHoldTeamConfig tid legalHoldTeamConfigStatus = do
retry x5 $ write updateLegalHoldTeamConfig (params Quorum (legalHoldTeamConfigStatus, tid))

-- | Returns 'False' if legal hold is not enabled for this team
-- The Caller is responsible for checking whether legal hold is enabled for this team
Expand Down
31 changes: 0 additions & 31 deletions services/galley/src/Galley/Data/Queries.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import Galley.Types.Teams.Intra
import Galley.Types.Teams.SearchVisibility
import Imports
import Text.RawString.QQ
import Wire.API.Team.Feature (TeamFeatureStatus)

-- Teams --------------------------------------------------------------------

Expand Down Expand Up @@ -321,12 +320,6 @@ insertBot = "insert into member (conv, user, service, provider, status) values (

-- LegalHold ----------------------------------------------------------------

selectLegalHoldTeamConfig :: PrepQuery R (Identity TeamId) (Identity (Maybe TeamFeatureStatus))
selectLegalHoldTeamConfig = "select legalhold_status from team_features where team_id = ?"

updateLegalHoldTeamConfig :: PrepQuery W (TeamFeatureStatus, TeamId) ()
updateLegalHoldTeamConfig = "update team_features set legalhold_status = ? where team_id = ?"

insertLegalHoldSettings :: PrepQuery W (HttpsUrl, Fingerprint Rsa, ServiceToken, ServiceKey, TeamId) ()
insertLegalHoldSettings =
[r|
Expand Down Expand Up @@ -379,22 +372,6 @@ updateUserLegalHoldStatus =
where team = ? and user = ?
|]

selectSSOTeamConfig :: PrepQuery R (Identity TeamId) (Identity (Maybe TeamFeatureStatus))
selectSSOTeamConfig =
"select sso_status from team_features where team_id = ?"

updateSSOTeamConfig :: PrepQuery W (TeamFeatureStatus, TeamId) ()
updateSSOTeamConfig =
"update team_features set sso_status = ? where team_id = ?"

selectTeamSearchVisibilityAvailable :: PrepQuery R (Identity TeamId) (Identity (Maybe TeamFeatureStatus))
selectTeamSearchVisibilityAvailable =
"select search_visibility_status from team_features where team_id = ?"

updateTeamSearchVisibilityAvailable :: PrepQuery W (TeamFeatureStatus, TeamId) ()
updateTeamSearchVisibilityAvailable =
"update team_features set search_visibility_status = ? where team_id = ?"

selectSearchVisibility :: PrepQuery R (Identity TeamId) (Identity (Maybe TeamSearchVisibility))
selectSearchVisibility =
"select search_visibility from team where team = ?"
Expand All @@ -403,14 +380,6 @@ updateSearchVisibility :: PrepQuery W (TeamSearchVisibility, TeamId) ()
updateSearchVisibility =
"update team set search_visibility = ? where team = ?"

selectValidateSAMLEmails :: PrepQuery R (Identity TeamId) (Identity (Maybe TeamFeatureStatus))
selectValidateSAMLEmails =
"select validate_saml_emails from team_features where team_id = ?"

updateValidateSAMLEmails :: PrepQuery W (TeamFeatureStatus, TeamId) ()
updateValidateSAMLEmails =
"update team_features set validate_saml_emails = ? where team_id = ?"

selectCustomBackend :: PrepQuery R (Identity Domain) (HttpsUrl, HttpsUrl)
selectCustomBackend =
"select config_json_url, webapp_welcome_url from custom_backend where domain = ?"
Expand Down
45 changes: 0 additions & 45 deletions services/galley/src/Galley/Data/SSO.hs

This file was deleted.

15 changes: 1 addition & 14 deletions services/galley/src/Galley/Data/SearchVisibility.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
-- with this program. If not, see <https://www.gnu.org/licenses/>.

module Galley.Data.SearchVisibility
( setTeamSearchVisibilityAvailable,
getTeamSearchVisibilityAvailable,
setSearchVisibility,
( setSearchVisibility,
getSearchVisibility,
resetSearchVisibility,
)
Expand All @@ -32,17 +30,6 @@ import Galley.Data.Instances ()
import Galley.Data.Queries
import Galley.Types.Teams.SearchVisibility
import Imports
import Wire.API.Team.Feature (TeamFeatureStatus)

-- | Return whether a given team is allowed to enable/disable sso
getTeamSearchVisibilityAvailable :: MonadClient m => TeamId -> m (Maybe TeamFeatureStatus)
getTeamSearchVisibilityAvailable tid = join . fmap runIdentity <$> do
retry x1 $ query1 selectTeamSearchVisibilityAvailable (params Quorum (Identity tid))

-- | Determines whether a given team is allowed to enable/disable sso
setTeamSearchVisibilityAvailable :: MonadClient m => TeamId -> TeamFeatureStatus -> m ()
setTeamSearchVisibilityAvailable tid isenabled = do
retry x5 $ write updateTeamSearchVisibilityAvailable (params Quorum (isenabled, tid))

-- | Return whether a given team is allowed to enable/disable sso
getSearchVisibility :: MonadClient m => TeamId -> m TeamSearchVisibility
Expand Down
51 changes: 51 additions & 0 deletions services/galley/src/Galley/Data/TeamFeatures.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{-# LANGUAGE ViewPatterns #-}

-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2020 Wire Swiss GmbH <opensource@wire.com>
--
-- This program is free software: you can redistribute it and/or modify it under
-- the terms of the GNU Affero General Public License as published by the Free
-- Software Foundation, either version 3 of the License, or (at your option) any
-- later version.
--
-- This program is distributed in the hope that it will be useful, but WITHOUT
-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
-- details.
--
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see <https://www.gnu.org/licenses/>.

module Galley.Data.TeamFeatures
( setFlag,
getFlag,
)
where

import Cassandra
import Data.Id
import Galley.Data.Instances ()
import Imports
import Wire.API.Team.Feature (TeamFeatureName (..), TeamFeatureStatus (..))

-- | Is a given feature enabled or disabled? Returns 'Nothing' if team does not exist or the
-- feature flag in Cassandra is null.
getFlag :: MonadClient m => TeamId -> TeamFeatureName -> m (Maybe TeamFeatureStatus)
getFlag tid feature = (>>= runIdentity) <$> retry x1 (query1 (select feature) (params Quorum (Identity tid)))

-- | Enable or disable feature flag.
setFlag :: MonadClient m => TeamId -> TeamFeatureName -> TeamFeatureStatus -> m ()
setFlag tid feature flag = do retry x5 $ write (update feature) (params Quorum (flag, tid))

select :: TeamFeatureName -> PrepQuery R (Identity TeamId) (Identity (Maybe TeamFeatureStatus))
select feature = fromString $ "select " <> toCol feature <> " from team_features where team_id = ?"

update :: TeamFeatureName -> PrepQuery W (TeamFeatureStatus, TeamId) ()
update feature = fromString $ "update team_features set " <> toCol feature <> " = ? where team_id = ?"

toCol :: TeamFeatureName -> String
toCol TeamFeatureLegalHold = "legalhold_status"
toCol TeamFeatureSSO = "sso_status"
toCol TeamFeatureSearchVisibility = "search_visibility_status"
toCol TeamFeatureValidateSAMLEmails = "validate_saml_emails"
42 changes: 0 additions & 42 deletions services/galley/src/Galley/Data/ValidateSAMLEmails.hs

This file was deleted.

Loading

0 comments on commit 869c7ac

Please sign in to comment.