feature flags infrastructure and API
test plan:
- install the test_features plugin (since no real features exist yet)
- render and consult the feature flags documentation
- have a test environment with a root account,
sub-account, course in sub-account, and user
- Use the "list features" endpoint as a root account admin
(with no site admin privileges), on the root account context, and
confirm that hidden features do not show up
- Use the "list features" endpoint as a site admin user,
on the root account context, and confirm that hidden features
show up
- Use the "list features" endpoint on the site admin account
and confirm the hidden features show up
- Use the "set feature flag" endpoint on a hidden feature on site
admin and ensure the feature becomes visible in all root accounts
- Use the "set feature flag endpoint" on a hidden feature on a
single root account, and ensure the feature becomes visible to
that root account and not others
- Confirm that root_opt_in features appear "Off" by default
in root accounts, after being "Allowed" in code or site admin
- Confirm a feature flag that is set to "on" or "off" (vs. "allowed")
cannot be overridden in a lower context (and the API returns
locked=true for them)
- Confirm that setting locking_account_id requires admin rights
in the locking account
- Confirm that a feature flag with locking_account_id cannot be
changed without admin rights in the locking account (e.g.,
set a feature flag on a course, locked with the root account's id,
and make sure a teacher who is not an account admin can't change it)
- Confirm feature flags can be deleted with the "remove feature flag"
endpoint (and they are only deleted where they are defined, not
when called on an object that inherits a flag)
Change-Id: I3e12e23b4454889b6e8b263f1315e82d8f2ada52
Reviewed-on: https://gerrit.instructure.com/25502
Tested-by: Jenkins <jenkins@instructure.com>
QA-Review: Matt Fairbourn <mfairbourn@instructure.com>
Product-Review: Matt Goodwin <mattg@instructure.com>
Reviewed-by: Zach Pendleton <zachp@instructure.com>
2013-10-22 23:28:26 +08:00
|
|
|
#
|
|
|
|
# Copyright (C) 2013 Instructure, Inc.
|
|
|
|
#
|
|
|
|
# This file is part of Canvas.
|
|
|
|
#
|
|
|
|
# Canvas 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, version 3 of the License.
|
|
|
|
#
|
|
|
|
# Canvas 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 <http://www.gnu.org/licenses/>.
|
|
|
|
#
|
|
|
|
|
|
|
|
class FeatureFlag < ActiveRecord::Base
|
|
|
|
attr_accessible :feature, :state, :locking_account
|
|
|
|
belongs_to :context, polymorphic: true
|
|
|
|
belongs_to :locking_account, class_name: 'Account'
|
|
|
|
|
|
|
|
validate :valid_state, :feature_applies, :locking_account_in_chain
|
|
|
|
before_save :check_cache
|
|
|
|
before_destroy :clear_cache
|
|
|
|
|
|
|
|
def default?
|
|
|
|
false
|
|
|
|
end
|
|
|
|
|
|
|
|
def hidden?
|
|
|
|
false
|
|
|
|
end
|
|
|
|
|
2014-12-13 08:27:02 +08:00
|
|
|
def unhides_feature?
|
|
|
|
return false unless Feature.definitions[feature].hidden?
|
|
|
|
return true if context.is_a?(Account) && context.site_admin?
|
|
|
|
Account.find(context.feature_flag_account_ids.last).lookup_feature_flag(feature, true).hidden?
|
|
|
|
end
|
|
|
|
|
feature flags infrastructure and API
test plan:
- install the test_features plugin (since no real features exist yet)
- render and consult the feature flags documentation
- have a test environment with a root account,
sub-account, course in sub-account, and user
- Use the "list features" endpoint as a root account admin
(with no site admin privileges), on the root account context, and
confirm that hidden features do not show up
- Use the "list features" endpoint as a site admin user,
on the root account context, and confirm that hidden features
show up
- Use the "list features" endpoint on the site admin account
and confirm the hidden features show up
- Use the "set feature flag" endpoint on a hidden feature on site
admin and ensure the feature becomes visible in all root accounts
- Use the "set feature flag endpoint" on a hidden feature on a
single root account, and ensure the feature becomes visible to
that root account and not others
- Confirm that root_opt_in features appear "Off" by default
in root accounts, after being "Allowed" in code or site admin
- Confirm a feature flag that is set to "on" or "off" (vs. "allowed")
cannot be overridden in a lower context (and the API returns
locked=true for them)
- Confirm that setting locking_account_id requires admin rights
in the locking account
- Confirm that a feature flag with locking_account_id cannot be
changed without admin rights in the locking account (e.g.,
set a feature flag on a course, locked with the root account's id,
and make sure a teacher who is not an account admin can't change it)
- Confirm feature flags can be deleted with the "remove feature flag"
endpoint (and they are only deleted where they are defined, not
when called on an object that inherits a flag)
Change-Id: I3e12e23b4454889b6e8b263f1315e82d8f2ada52
Reviewed-on: https://gerrit.instructure.com/25502
Tested-by: Jenkins <jenkins@instructure.com>
QA-Review: Matt Fairbourn <mfairbourn@instructure.com>
Product-Review: Matt Goodwin <mattg@instructure.com>
Reviewed-by: Zach Pendleton <zachp@instructure.com>
2013-10-22 23:28:26 +08:00
|
|
|
def enabled?
|
|
|
|
state == 'on'
|
|
|
|
end
|
|
|
|
|
|
|
|
def allowed?
|
|
|
|
state == 'allowed'
|
|
|
|
end
|
|
|
|
|
|
|
|
def locked?(query_context, current_user = nil)
|
|
|
|
locking_account.present? && (current_user.blank? || !locking_account.grants_right?(current_user, :manage_feature_flags)) ||
|
|
|
|
!allowed? && (context_id != query_context.id || context_type != query_context.class.name)
|
|
|
|
end
|
|
|
|
|
|
|
|
def clear_cache
|
2015-12-29 03:23:33 +08:00
|
|
|
self.class.connection.after_transaction_commit { MultiCache.delete(self.context.feature_flag_cache_key(feature)) } if self.context
|
feature flags infrastructure and API
test plan:
- install the test_features plugin (since no real features exist yet)
- render and consult the feature flags documentation
- have a test environment with a root account,
sub-account, course in sub-account, and user
- Use the "list features" endpoint as a root account admin
(with no site admin privileges), on the root account context, and
confirm that hidden features do not show up
- Use the "list features" endpoint as a site admin user,
on the root account context, and confirm that hidden features
show up
- Use the "list features" endpoint on the site admin account
and confirm the hidden features show up
- Use the "set feature flag" endpoint on a hidden feature on site
admin and ensure the feature becomes visible in all root accounts
- Use the "set feature flag endpoint" on a hidden feature on a
single root account, and ensure the feature becomes visible to
that root account and not others
- Confirm that root_opt_in features appear "Off" by default
in root accounts, after being "Allowed" in code or site admin
- Confirm a feature flag that is set to "on" or "off" (vs. "allowed")
cannot be overridden in a lower context (and the API returns
locked=true for them)
- Confirm that setting locking_account_id requires admin rights
in the locking account
- Confirm that a feature flag with locking_account_id cannot be
changed without admin rights in the locking account (e.g.,
set a feature flag on a course, locked with the root account's id,
and make sure a teacher who is not an account admin can't change it)
- Confirm feature flags can be deleted with the "remove feature flag"
endpoint (and they are only deleted where they are defined, not
when called on an object that inherits a flag)
Change-Id: I3e12e23b4454889b6e8b263f1315e82d8f2ada52
Reviewed-on: https://gerrit.instructure.com/25502
Tested-by: Jenkins <jenkins@instructure.com>
QA-Review: Matt Fairbourn <mfairbourn@instructure.com>
Product-Review: Matt Goodwin <mattg@instructure.com>
Reviewed-by: Zach Pendleton <zachp@instructure.com>
2013-10-22 23:28:26 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
def valid_state
|
|
|
|
errors.add(:state, "is not valid in context") unless %w(off on).include?(state) || context.is_a?(Account) && state == 'allowed'
|
|
|
|
end
|
|
|
|
|
|
|
|
def feature_applies
|
|
|
|
errors.add(:feature, "is not valid in context") unless Feature.feature_applies_to_object(feature, context)
|
|
|
|
end
|
|
|
|
|
|
|
|
def locking_account_in_chain
|
|
|
|
if locking_account_id.present?
|
|
|
|
account = if context.respond_to?(:parent_account)
|
|
|
|
context.parent_account || Account.site_admin
|
|
|
|
else
|
|
|
|
context.account
|
|
|
|
end
|
|
|
|
account_chain_ids = account.account_chain(include_site_admin: true).map(&:id)
|
|
|
|
errors.add(:locking_account_id, "not in account chain") unless account_chain_ids.include?(locking_account_id)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def check_cache
|
|
|
|
clear_cache if self.changed?
|
|
|
|
end
|
|
|
|
end
|