Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions server/channels/api4/access_control.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ func createAccessControlPolicy(c *Context, w http.ResponseWriter, r *http.Reques
hasManageSystemPermission := c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem)

if !hasManageSystemPermission {
// FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules - Remove this check when feature is GA
if !c.App.Config().FeatureFlags.ChannelAdminManageABACRules {
c.SetPermissionError(model.PermissionManageSystem)
return
}
// END FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules

// For non-system admins, check channel-specific permission
if !model.IsValidId(policy.ID) {
c.SetInvalidParam("policy.id")
Expand Down Expand Up @@ -110,6 +117,13 @@ func getAccessControlPolicy(c *Context, w http.ResponseWriter, r *http.Request)
// Check if user has system admin permission OR channel-specific permission
hasManageSystemPermission := c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem)
if !hasManageSystemPermission {
// FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules - Remove this check when feature is GA
if !c.App.Config().FeatureFlags.ChannelAdminManageABACRules {
c.SetPermissionError(model.PermissionManageSystem)
return
}
// END FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules

// For non-system admins, validate policy access permission
if appErr := c.App.ValidateAccessControlPolicyPermission(c.AppContext, c.AppContext.Session().UserId, policyID); appErr != nil {
c.SetPermissionError(model.PermissionManageSystem)
Expand Down Expand Up @@ -148,6 +162,13 @@ func deleteAccessControlPolicy(c *Context, w http.ResponseWriter, r *http.Reques
// Check if user has system admin permission OR channel-specific permission
hasManageSystemPermission := c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem)
if !hasManageSystemPermission {
// FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules - Remove this check when feature is GA
if !c.App.Config().FeatureFlags.ChannelAdminManageABACRules {
c.SetPermissionError(model.PermissionManageSystem)
return
}
// END FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules

// For non-system admins, validate policy access permission
if appErr := c.App.ValidateAccessControlPolicyPermission(c.AppContext, c.AppContext.Session().UserId, policyID); appErr != nil {
c.SetPermissionError(model.PermissionManageSystem)
Expand Down Expand Up @@ -180,6 +201,13 @@ func checkExpression(c *Context, w http.ResponseWriter, r *http.Request) {
return
}

// FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules - Remove this check when feature is GA
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) && !c.App.Config().FeatureFlags.ChannelAdminManageABACRules {
c.SetPermissionError(model.PermissionManageSystem)
return
}
// END FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules

errs, appErr := c.App.CheckExpression(c.AppContext, checkExpressionRequest.Expression)
if appErr != nil {
c.Err = appErr
Expand Down Expand Up @@ -210,6 +238,13 @@ func testExpression(c *Context, w http.ResponseWriter, r *http.Request) {
return
}

// FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules - Remove this check when feature is GA
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) && !c.App.Config().FeatureFlags.ChannelAdminManageABACRules {
c.SetPermissionError(model.PermissionManageSystem)
return
}
// END FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules

users, count, appErr := c.App.TestExpression(c.AppContext, checkExpressionRequest.Expression, model.SubjectSearchOptions{
Term: checkExpressionRequest.Term,
Limit: checkExpressionRequest.Limit,
Expand Down Expand Up @@ -490,6 +525,13 @@ func getFieldsAutocomplete(c *Context, w http.ResponseWriter, r *http.Request) {
return
}

// FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules - Remove this check when feature is GA
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) && !c.App.Config().FeatureFlags.ChannelAdminManageABACRules {
c.SetPermissionError(model.PermissionManageSystem)
return
}
// END FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules

after := r.URL.Query().Get("after")
if after != "" && !model.IsValidId(after) {
c.SetInvalidParam("after")
Expand Down Expand Up @@ -533,6 +575,13 @@ func convertToVisualAST(c *Context, w http.ResponseWriter, r *http.Request) {
return
}

// FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules - Remove this check when feature is GA
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) && !c.App.Config().FeatureFlags.ChannelAdminManageABACRules {
c.SetPermissionError(model.PermissionManageSystem)
return
}
// END FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules

var cel struct {
Expression string `json:"expression"`
}
Expand Down
4 changes: 4 additions & 0 deletions server/channels/api4/access_control_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@ import (

func TestCreateAccessControlPolicy(t *testing.T) {
os.Setenv("MM_FEATUREFLAGS_ATTRIBUTEBASEDACCESSCONTROL", "true")
// FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules - Remove this env var when feature is GA
os.Setenv("MM_FEATUREFLAGS_CHANNELADMINMANAGEABACRULES", "true")
th := Setup(t).InitBasic()
t.Cleanup(func() {
th.TearDown()
os.Unsetenv("MM_FEATUREFLAGS_ATTRIBUTEBASEDACCESSCONTROL")
// FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules - Remove this unsetenv when feature is GA
os.Unsetenv("MM_FEATUREFLAGS_CHANNELADMINMANAGEABACRULES")
})

samplePolicy := &model.AccessControlPolicy{
Expand Down
6 changes: 6 additions & 0 deletions server/public/model/feature_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ type FeatureFlags struct {

// Enable AppsForm for Interactive Dialogs instead of legacy dialog implementation
InteractiveDialogAppsForm bool

// FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules - Remove this field when feature is GA
// Enable channel admins to manage ABAC rules for their channels
ChannelAdminManageABACRules bool
}

func (f *FeatureFlags) SetDefaults() {
Expand Down Expand Up @@ -103,6 +107,8 @@ func (f *FeatureFlags) SetDefaults() {
f.AttributeBasedAccessControl = true
f.ContentFlagging = false
f.InteractiveDialogAppsForm = true
// FEATURE_FLAG_REMOVAL: ChannelAdminManageABACRules - Remove this default when feature is GA
f.ChannelAdminManageABACRules = false // Default to false for safety
}

// ToMap returns the feature flags as a map[string]string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@
}

.PrivateCloudCard__actionButton {
display: block;
display: flex;
align-items: center;
justify-content: center;
margin-top: 16px;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {getCurrentUser} from 'mattermost-redux/selectors/entities/common';
import {get as getPreference} from 'mattermost-redux/selectors/entities/preferences';

import AlertBanner from 'components/alert_banner';
import useOpenSalesLink from 'components/common/hooks/useOpenSalesLink';
import UpgradeLink from 'components/widgets/links/upgrade_link';

import {CloudBanners, Preferences} from 'utils/constants';
Expand All @@ -30,7 +29,6 @@ const CloudTrialBanner = ({trialEndDate}: Props): JSX.Element | null => {
const endDate = new Date(trialEndDate);
const DISMISSED_DAYS = 10;
const {formatMessage} = useIntl();
const [openSalesLink] = useOpenSalesLink();
const dispatch = useDispatch();
const user = useSelector(getCurrentUser);
const storedDismissedEndDate = useSelector((state: GlobalState) => getPreference(state, Preferences.CLOUD_TRIAL_BANNER, CloudBanners.UPGRADE_FROM_TRIAL));
Expand Down Expand Up @@ -72,7 +70,7 @@ const CloudTrialBanner = ({trialEndDate}: Props): JSX.Element | null => {
title={(
<FormattedMessage
id='admin.subscription.cloudTrialCard.upgradeTitle'
defaultMessage='Upgrade to one of our paid plans to avoid Free plan data limits'
defaultMessage='Upgrade to one of our paid plans to keep your workspace '
/>
)}
message={(
Expand All @@ -94,17 +92,7 @@ const CloudTrialBanner = ({trialEndDate}: Props): JSX.Element | null => {
/>
)}
actionButtonRight={(
<button
onClick={openSalesLink}
className='AlertBanner__buttonRight'
>
<FormattedMessage
id='admin.billing.subscription.privateCloudCard.contactSalesy'
defaultMessage={
'Contact sales'
}
/>
</button>
null
)}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,14 @@ export const FreeTrial = ({daysLeftOnTrial}: FreeTrialProps) => {
{daysLeftOnTrial > TrialPeriodDays.TRIAL_WARNING_THRESHOLD &&
<FormattedMessage
id='admin.billing.subscription.freeTrial.description'
defaultMessage='Your free trial will expire in {daysLeftOnTrial} days. Add your payment information to continue after the trial ends.'
defaultMessage='Your free trial will expire in {daysLeftOnTrial} days. Contact sales to continue after the trial ends.'
values={{daysLeftOnTrial}}
/>
}
{(daysLeftOnTrial > TrialPeriodDays.TRIAL_1_DAY && daysLeftOnTrial <= TrialPeriodDays.TRIAL_WARNING_THRESHOLD) &&
<FormattedMessage
id='admin.billing.subscription.freeTrial.lessThan3Days.description'
defaultMessage='Your free trial will end in {daysLeftOnTrial, number} {daysLeftOnTrial, plural, one {day} other {days}}. Add payment information to continue enjoying the benefits of Cloud Professional.'
defaultMessage='Your free trial will end in {daysLeftOnTrial, number} {daysLeftOnTrial, plural, one {day} other {days}}. Contact sales to continue enjoying the benefits of Cloud Professional.'
values={{daysLeftOnTrial}}
/>
}
Expand All @@ -119,9 +119,8 @@ export const FreeTrial = ({daysLeftOnTrial}: FreeTrialProps) => {
<button
type='button'
onClick={() => openSalesLink()}
className='UpgradeMattermostCloud__upgradeButton'
className='UpgradeMattermostCloud__upgradeButton btn btn-primary'
>

<FormattedMessage
id='admin.billing.subscription.privateCloudCard.contactSales'
defaultMessage='Contact Sales'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,14 @@ const FeatureList = (props: FeatureListProps) => {
}),
];

const featuresCloudAdvanced = [
intl.formatMessage({
id: 'admin.billing.subscription.planDetails.features.abac',
defaultMessage: 'Attribute-based access control (ABAC)',
}),
...featuresCloudEnterprise,
];

let features: string[] = [];

switch (props.subscriptionPlan) {
Expand All @@ -162,6 +170,9 @@ const FeatureList = (props: FeatureListProps) => {
case CloudProducts.ENTERPRISE:
features = featuresCloudEnterprise;
break;
case CloudProducts.ADVANCED:
features = featuresCloudAdvanced;
break;
default:
// unknown product
features = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
PlanDetailsTopElements,
currentPlanText,
} from './plan_details';
import PlanPricing from './plan_pricing';

import './plan_details.scss';

Expand Down Expand Up @@ -47,9 +46,6 @@ const PlanDetails = ({isFreeTrial, subscriptionPlan}: Props) => {
daysLeftOnTrial={daysLeftOnTrial}
isYearly={product.recurring_interval === 'year'}
/>
<PlanPricing
product={product}
/>
<div className='PlanDetails__teamAndChannelCount'>
<FormattedMessage
id='admin.billing.subscription.planDetails.subheader'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ export const PlanDetailsTopElements = ({
/>
);
break;
case CloudProducts.ADVANCED:
productName = (
<FormattedMessage
id='admin.billing.subscription.planDetails.productName.cloudAdvanced'
defaultMessage='Cloud Enterprise Advanced'
/>
);
break;
default:
productName = (
<FormattedMessage
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`components/channel_settings_modal/ChannelSettingsAccessRulesTab should match snapshot 1`] = `
<div>
<div
class="ChannelSettingsModal__accessRulesTab"
>
<div
class="ChannelSettingsModal__accessRulesHeader"
>
<h3
class="ChannelSettingsModal__accessRulesTitle"
>
Access Rules
</h3>
<p
class="ChannelSettingsModal__accessRulesSubtitle"
>
Select user attributes and values as rules to restrict channel membership
</p>
</div>
<p
class="ChannelSettingsModal__accessRulesDescription"
>
Select attributes and values that users must match in addition to access this channel. All selected attributes are required.
</p>
</div>
</div>
`;
Loading
Loading