diff options
author | GitLab Release Tools Bot <delivery-team+release-tools@gitlab.com> | 2019-10-30 13:56:31 +0000 |
---|---|---|
committer | GitLab Release Tools Bot <delivery-team+release-tools@gitlab.com> | 2019-10-30 13:56:31 +0000 |
commit | 96c2c29afc742d129dab22fed8554b17572900d8 (patch) | |
tree | 9d2bdc74e09a949506d6cd57a0a8a4f5bcd13894 /app | |
parent | 635e1578219d95ee683cd2901fa5d0f6965e7033 (diff) | |
parent | 2b354bdcd1f3e49399eb80bdf28f90f1ac158ef7 (diff) | |
download | gitlab-ce-96c2c29afc742d129dab22fed8554b17572900d8.tar.gz |
Merge remote-tracking branch 'dev/12-2-stable' into 12-2-stable
Diffstat (limited to 'app')
34 files changed, 199 insertions, 44 deletions
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js index 60d3d83a4b2..bd5ab4f9ec4 100644 --- a/app/assets/javascripts/project_find_file.js +++ b/app/assets/javascripts/project_find_file.js @@ -5,6 +5,7 @@ import fuzzaldrinPlus from 'fuzzaldrin-plus'; import axios from '~/lib/utils/axios_utils'; import flash from '~/flash'; import { __ } from '~/locale'; +import sanitize from 'sanitize-html'; // highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> ) const highlighter = function(element, text, matches) { @@ -75,7 +76,7 @@ export default class ProjectFindFile { findFile() { var result, searchText; - searchText = this.inputElement.val(); + searchText = sanitize(this.inputElement.val()); result = searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths; return this.renderList(result, searchText); diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index af6644b8fcc..a246ec15535 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -14,7 +14,7 @@ class ApplicationController < ActionController::Base include SessionlessAuthentication include ConfirmEmailWarning - before_action :authenticate_user! + before_action :authenticate_user!, except: [:route_not_found] before_action :enforce_terms!, if: :should_enforce_terms? before_action :validate_user_service_ticket! before_action :check_password_expiration @@ -92,7 +92,9 @@ class ApplicationController < ActionController::Base if current_user not_found else - authenticate_user! + store_location_for(:user, request.fullpath) unless request.xhr? + + redirect_to new_user_session_path, alert: I18n.t('devise.failure.unauthenticated') end end diff --git a/app/controllers/concerns/internal_redirect.rb b/app/controllers/concerns/internal_redirect.rb index fa3716502a0..e314953bb79 100644 --- a/app/controllers/concerns/internal_redirect.rb +++ b/app/controllers/concerns/internal_redirect.rb @@ -6,7 +6,7 @@ module InternalRedirect def safe_redirect_path(path) return unless path # Verify that the string starts with a `/` and a known route character. - return unless path =~ %r{^/[-\w].*$} + return unless path =~ %r{\A/[-\w].*\z} uri = URI(path) # Ignore anything path of the redirect except for the path, querystring and, diff --git a/app/controllers/concerns/lfs_request.rb b/app/controllers/concerns/lfs_request.rb index f7137a04437..d3af15d82f9 100644 --- a/app/controllers/concerns/lfs_request.rb +++ b/app/controllers/concerns/lfs_request.rb @@ -34,6 +34,7 @@ module LfsRequest end def lfs_check_access! + return render_lfs_not_found unless project return if download_request? && lfs_download_access? return if upload_request? && lfs_upload_access? diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb index e523942ea4c..027cdc4fc78 100644 --- a/app/finders/labels_finder.rb +++ b/app/finders/labels_finder.rb @@ -51,7 +51,7 @@ class LabelsFinder < UnionFinder end label_ids << Label.where(group_id: projects.group_ids) - label_ids << Label.where(project_id: projects.select(:id)) unless only_group_labels? + label_ids << Label.where(project_id: ids_user_can_read_labels(projects)) unless only_group_labels? end label_ids @@ -188,4 +188,10 @@ class LabelsFinder < UnionFinder groups.select { |group| authorized_to_read_labels?(group) } end end + + # rubocop: disable CodeReuse/ActiveRecord + def ids_user_can_read_labels(projects) + Project.where(id: projects.select(:id)).ids_with_issuables_available_for(current_user) + end + # rubocop: enable CodeReuse/ActiveRecord end diff --git a/app/graphql/gitlab_schema.rb b/app/graphql/gitlab_schema.rb index 7edd14e48f7..c49c4d937c6 100644 --- a/app/graphql/gitlab_schema.rb +++ b/app/graphql/gitlab_schema.rb @@ -18,15 +18,15 @@ class GitlabSchema < GraphQL::Schema use Gitlab::Graphql::GenericTracing query_analyzer Gitlab::Graphql::QueryAnalyzers::LoggerAnalyzer.new - - query(Types::QueryType) - - default_max_page_size 100 + query_analyzer Gitlab::Graphql::QueryAnalyzers::RecursionAnalyzer.new max_complexity DEFAULT_MAX_COMPLEXITY max_depth DEFAULT_MAX_DEPTH - mutation(Types::MutationType) + query Types::QueryType + mutation Types::MutationType + + default_max_page_size 100 class << self def multiplex(queries, **kwargs) diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb index d76a0f3a3b8..e2524938e10 100644 --- a/app/helpers/markup_helper.rb +++ b/app/helpers/markup_helper.rb @@ -133,15 +133,7 @@ module MarkupHelper issuable_state_filter_enabled: true ) - html = - case wiki_page.format - when :markdown - markdown_unsafe(text, context) - when :asciidoc - asciidoc_unsafe(text) - else - wiki_page.formatted_content.html_safe - end + html = markup_unsafe(wiki_page.path, text, context) prepare_for_rendering(html, context) end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index d6caf092ed0..7b5d67556ea 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -7,6 +7,13 @@ class ApplicationSetting < ApplicationRecord include IgnorableColumn include ChronicDurationAttribute + GRAFANA_URL_RULES = { + allow_localhost: true, + allow_local_network: true, + enforce_sanitization: true, + require_absolute: false + }.freeze + add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption, default_enabled: true) ? :optional : :required } add_authentication_token_field :health_check_access_token @@ -55,6 +62,11 @@ class ApplicationSetting < ApplicationRecord allow_nil: false, qualified_domain_array: true + validates :grafana_url, + allow_blank: true, + allow_nil: true, + addressable_url: GRAFANA_URL_RULES + validates :session_expire_delay, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 } @@ -72,7 +84,6 @@ class ApplicationSetting < ApplicationRecord validates :after_sign_out_path, allow_blank: true, addressable_url: true - validates :admin_notification_email, devise_email: true, allow_blank: true @@ -303,6 +314,14 @@ class ApplicationSetting < ApplicationRecord current_without_cache end + def grafana_url + if Gitlab::UrlBlocker.blocked_url?(self[:grafana_url], GRAFANA_URL_RULES) + ApplicationSetting.column_defaults["grafana_url"] + else + self[:grafana_url] + end + end + # By default, the backend is Rails.cache, which uses # ActiveSupport::Cache::RedisStore. Since loading ApplicationSetting # can cause a significant amount of load on Redis, let's cache it in diff --git a/app/models/concerns/mentionable/reference_regexes.rb b/app/models/concerns/mentionable/reference_regexes.rb index b8fb3f71925..fb10035b92e 100644 --- a/app/models/concerns/mentionable/reference_regexes.rb +++ b/app/models/concerns/mentionable/reference_regexes.rb @@ -13,7 +13,9 @@ module Mentionable def self.other_patterns [ Commit.reference_pattern, - MergeRequest.reference_pattern + MergeRequest.reference_pattern, + Label.reference_pattern, + Milestone.reference_pattern ] end diff --git a/app/models/discussion.rb b/app/models/discussion.rb index 0d066d0d99f..b8525f7b135 100644 --- a/app/models/discussion.rb +++ b/app/models/discussion.rb @@ -16,6 +16,7 @@ class Discussion :commit_id, :for_commit?, :for_merge_request?, + :noteable_ability_name, :to_ability_name, :editable?, :visible_for?, diff --git a/app/models/member.rb b/app/models/member.rb index dbae1076670..2dd3e7b5cec 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -8,6 +8,7 @@ class Member < ApplicationRecord include Gitlab::Access include Presentable include Gitlab::Utils::StrongMemoize + include FromUnion attr_accessor :raw_invite_token diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index bfd636fa62a..8ef26af84e3 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -67,6 +67,14 @@ class MergeRequest < ApplicationRecord has_many :merge_request_assignees has_many :assignees, class_name: "User", through: :merge_request_assignees + KNOWN_MERGE_PARAMS = [ + :auto_merge_strategy, + :should_remove_source_branch, + :force_remove_source_branch, + :commit_message, + :squash_commit_message, + :sha + ].freeze serialize :merge_params, Hash # rubocop:disable Cop/ActiveRecordSerialize after_create :ensure_merge_request_diff diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 2ad2838111e..012e72ece5a 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -254,6 +254,10 @@ class Milestone < ApplicationRecord group || project end + def to_ability_name + model_name.singular + end + def group_milestone? group_id.present? end diff --git a/app/models/note.rb b/app/models/note.rb index 3956ec192b1..307f409de09 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -353,6 +353,10 @@ class Note < ApplicationRecord end def to_ability_name + model_name.singular + end + + def noteable_ability_name for_snippet? ? noteable.class.name.underscore : noteable_type.demodulize.underscore end diff --git a/app/models/project.rb b/app/models/project.rb index a1bd5edaba9..e2c3ec8c82b 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -581,11 +581,11 @@ class Project < ApplicationRecord joins(:namespace).where(namespaces: { type: 'Group' }).select(:namespace_id) end - # Returns ids of projects with milestones available for given user + # Returns ids of projects with issuables available for given user # - # Used on queries to find milestones which user can see - # For example: Milestone.where(project_id: ids_with_milestone_available_for(user)) - def ids_with_milestone_available_for(user) + # Used on queries to find milestones or labels which user can see + # For example: Milestone.where(project_id: ids_with_issuables_available_for(user)) + def ids_with_issuables_available_for(user) with_issues_enabled = with_issues_available_for_user(user).select(:id) with_merge_requests_enabled = with_merge_requests_available_for_user(user).select(:id) @@ -1223,6 +1223,10 @@ class Project < ApplicationRecord end end + def to_ability_name + model_name.singular + end + # rubocop: disable CodeReuse/ServiceClass def execute_hooks(data, hooks_scope = :push_hooks) run_after_commit_or_now do diff --git a/app/models/system_note_metadata.rb b/app/models/system_note_metadata.rb index a19755d286a..bf19fd15290 100644 --- a/app/models/system_note_metadata.rb +++ b/app/models/system_note_metadata.rb @@ -10,6 +10,7 @@ class SystemNoteMetadata < ApplicationRecord commit cross_reference close duplicate moved merge + label milestone ].freeze ICON_TYPES = %w[ diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index cd4c7895587..1b6d8fc47a7 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -138,6 +138,12 @@ class WikiPage @version ||= @page.version end + def path + return unless persisted? + + @path ||= @page.path + end + def versions(options = {}) return [] unless persisted? diff --git a/app/policies/commit_policy.rb b/app/policies/commit_policy.rb index 4d4f0ba9267..4b358c45ec2 100644 --- a/app/policies/commit_policy.rb +++ b/app/policies/commit_policy.rb @@ -4,4 +4,5 @@ class CommitPolicy < BasePolicy delegate { @subject.project } rule { can?(:download_code) }.enable :read_commit + rule { ~can?(:read_commit) }.prevent :create_note end diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index c686e7763bb..987f252546d 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -124,6 +124,8 @@ class GroupPolicy < BasePolicy rule { developer & developer_maintainer_access }.enable :create_projects rule { create_projects_disabled }.prevent :create_projects + rule { maintainer & can?(:create_projects) }.enable :transfer_projects + def access_level return GroupMember::NO_ACCESS if @user.nil? diff --git a/app/policies/namespace_policy.rb b/app/policies/namespace_policy.rb index 2babcb0a2d9..926a8b7264d 100644 --- a/app/policies/namespace_policy.rb +++ b/app/policies/namespace_policy.rb @@ -14,4 +14,6 @@ class NamespacePolicy < BasePolicy end rule { personal_project & ~can_create_personal_project }.prevent :create_projects + + rule { (owner | admin) & can?(:create_projects) }.enable :transfer_projects end diff --git a/app/policies/note_policy.rb b/app/policies/note_policy.rb index b2af6c874c7..dcde8cefa0d 100644 --- a/app/policies/note_policy.rb +++ b/app/policies/note_policy.rb @@ -9,7 +9,7 @@ class NotePolicy < BasePolicy condition(:editable, scope: :subject) { @subject.editable? } - condition(:can_read_noteable) { can?(:"read_#{@subject.to_ability_name}") } + condition(:can_read_noteable) { can?(:"read_#{@subject.noteable_ability_name}") } condition(:is_visible) { @subject.visible_for?(@user) } diff --git a/app/services/auto_merge/base_service.rb b/app/services/auto_merge/base_service.rb index e06659a39cd..e08b4ac2260 100644 --- a/app/services/auto_merge/base_service.rb +++ b/app/services/auto_merge/base_service.rb @@ -3,12 +3,13 @@ module AutoMerge class BaseService < ::BaseService include Gitlab::Utils::StrongMemoize + include MergeRequests::AssignsMergeParams def execute(merge_request) - merge_request.merge_params.merge!(params) + assign_allowed_merge_params(merge_request, params.merge(auto_merge_strategy: strategy)) + merge_request.auto_merge_enabled = true merge_request.merge_user = current_user - merge_request.auto_merge_strategy = strategy return :failed unless merge_request.save @@ -21,7 +22,7 @@ module AutoMerge end def update(merge_request) - merge_request.merge_params.merge!(params) + assign_allowed_merge_params(merge_request, params.merge(auto_merge_strategy: strategy)) return :failed unless merge_request.save diff --git a/app/services/concerns/merge_requests/assigns_merge_params.rb b/app/services/concerns/merge_requests/assigns_merge_params.rb new file mode 100644 index 00000000000..bd870d9a1e7 --- /dev/null +++ b/app/services/concerns/merge_requests/assigns_merge_params.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module MergeRequests + module AssignsMergeParams + def self.included(klass) + raise "#{self} can not be included in #{klass} without implementing #current_user" unless klass.method_defined?(:current_user) + end + + def assign_allowed_merge_params(merge_request, merge_params) + known_merge_params = merge_params.to_h.with_indifferent_access.slice(*MergeRequest::KNOWN_MERGE_PARAMS) + + # Not checking `MergeRequest#can_remove_source_branch` as that includes + # other checks that aren't needed here. + known_merge_params.delete(:force_remove_source_branch) unless current_user.can?(:push_code, merge_request.source_project) + + merge_request.merge_params.merge!(known_merge_params) + + # Delete the known params now that they're assigned, so we don't try to + # assign them through an `#assign_attributes` later. + # They could be coming in as strings or symbols + merge_params.to_h.with_indifferent_access.except!(*MergeRequest::KNOWN_MERGE_PARAMS) + end + end +end diff --git a/app/services/error_tracking/list_projects_service.rb b/app/services/error_tracking/list_projects_service.rb index 8d08f0cda94..92d4ef85ecf 100644 --- a/app/services/error_tracking/list_projects_service.rb +++ b/app/services/error_tracking/list_projects_service.rb @@ -32,7 +32,7 @@ module ErrorTracking project_slug: 'proj' ) - setting.token = params[:token] + setting.token = token(setting) setting.enabled = true end end @@ -40,5 +40,12 @@ module ErrorTracking def can_read? can?(current_user, :read_sentry_issue, project) end + + def token(setting) + # Use param token if not masked, otherwise use database token + return params[:token] unless /\A\*+\z/.match?(params[:token]) + + setting.token + end end end diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index 067510a8a0a..b4590c20080 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -2,6 +2,8 @@ module MergeRequests class BaseService < ::IssuableBaseService + include MergeRequests::AssignsMergeParams + def create_note(merge_request, state = merge_request.state) SystemNoteService.change_status(merge_request, merge_request.target_project, current_user, state, nil) end @@ -31,6 +33,18 @@ module MergeRequests private + def create(merge_request) + self.params = assign_allowed_merge_params(merge_request, params) + + super + end + + def update(merge_request) + self.params = assign_allowed_merge_params(merge_request, params) + + super + end + def handle_wip_event(merge_request) if wip_event = params.delete(:wip_event) # We update the title that is provided in the params or we use the mr title diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb index b28f80939ae..20e60299dd1 100644 --- a/app/services/merge_requests/build_service.rb +++ b/app/services/merge_requests/build_service.rb @@ -10,13 +10,14 @@ module MergeRequests # TODO: this should handle all quick actions that don't have side effects # https://gitlab.com/gitlab-org/gitlab-ce/issues/53658 merge_quick_actions_into_params!(merge_request, only: [:target_branch]) - merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch) if params.has_key?(:force_remove_source_branch) # Assign the projects first so we can use policies for `filter_params` merge_request.author = current_user merge_request.source_project = find_source_project merge_request.target_project = find_target_project + self.params = assign_allowed_merge_params(merge_request, params) + filter_params(merge_request) merge_request.assign_attributes(params.to_h.compact) diff --git a/app/services/merge_requests/create_service.rb b/app/services/merge_requests/create_service.rb index 06e46595b95..bd883a28c73 100644 --- a/app/services/merge_requests/create_service.rb +++ b/app/services/merge_requests/create_service.rb @@ -9,7 +9,6 @@ module MergeRequests merge_request.target_project = @project merge_request.source_project = @source_project merge_request.source_branch = params[:source_branch] - merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch) create(merge_request) end diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb index d361e96babf..e890247ffb0 100644 --- a/app/services/merge_requests/update_service.rb +++ b/app/services/merge_requests/update_service.rb @@ -16,10 +16,6 @@ module MergeRequests params.delete(:force_remove_source_branch) end - if params.has_key?(:force_remove_source_branch) - merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch) - end - handle_wip_event(merge_request) update_task_event(merge_request) || update(merge_request) end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 83710ffce2f..be213d8ceba 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -281,7 +281,7 @@ class NotificationService end def send_new_note_notifications(note) - notify_method = "note_#{note.to_ability_name}_email".to_sym + notify_method = "note_#{note.noteable_ability_name}_email".to_sym recipients = NotificationRecipientService.build_new_note_recipients(note) recipients.each do |recipient| diff --git a/app/services/projects/operations/update_service.rb b/app/services/projects/operations/update_service.rb index 48eddb0e8d0..f343e174a67 100644 --- a/app/services/projects/operations/update_service.rb +++ b/app/services/projects/operations/update_service.rb @@ -34,15 +34,17 @@ module Projects organization_slug: settings.dig(:project, :organization_slug) ) - { + params = { error_tracking_setting_attributes: { api_url: api_url, - token: settings[:token], enabled: settings[:enabled], project_name: settings.dig(:project, :name), organization_name: settings.dig(:project, :organization_name) } } + params[:error_tracking_setting_attributes][:token] = settings[:token] unless /\A\*+\z/.match?(settings[:token]) # Don't update token if we receive masked value + + params end end end diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb index 7080f388e53..1cd81fe37c7 100644 --- a/app/services/projects/participants_service.rb +++ b/app/services/projects/participants_service.rb @@ -7,16 +7,69 @@ module Projects def execute(noteable) @noteable = noteable - participants = noteable_owner + participants_in_noteable + all_members + groups + project_members + participants = + noteable_owner + + participants_in_noteable + + all_members + + groups + + project_members + participants.uniq end def project_members - @project_members ||= sorted(project.team.members) + @project_members ||= sorted(get_project_members) + end + + def get_project_members + members = Member.from_union([project_members_through_ancestral_groups, + project_members_through_invited_groups, + individual_project_members]) + + User.id_in(members.select(:user_id)) end def all_members [{ username: "all", name: "All Project and Group Members", count: project_members.count }] end + + private + + def project_members_through_invited_groups + groups_with_ancestors_ids = Gitlab::ObjectHierarchy + .new(visible_groups) + .base_and_ancestors + .pluck_primary_key + + GroupMember + .active_without_invites_and_requests + .with_source_id(groups_with_ancestors_ids) + end + + def visible_groups + visible_groups = project.invited_groups + + unless project_owner? + visible_groups = visible_groups.public_or_visible_to_user(current_user) + end + + visible_groups + end + + def project_members_through_ancestral_groups + project.group.present? ? project.group.members_with_parents : Member.none + end + + def individual_project_members + project.project_members + end + + def project_owner? + if project.group.present? + project.group.owners.include?(current_user) + else + project.namespace.owner == current_user + end + end end end diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index 233dcf37e35..b94d618dc43 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -95,7 +95,7 @@ module Projects @new_namespace && can?(current_user, :change_namespace, project) && @new_namespace.id != project.namespace_id && - current_user.can?(:create_projects, @new_namespace) + current_user.can?(:transfer_projects, @new_namespace) end def update_namespace_and_visibility(to_namespace) diff --git a/app/validators/addressable_url_validator.rb b/app/validators/addressable_url_validator.rb index bb445499cee..1075d71869f 100644 --- a/app/validators/addressable_url_validator.rb +++ b/app/validators/addressable_url_validator.rb @@ -49,7 +49,8 @@ class AddressableUrlValidator < ActiveModel::EachValidator allow_local_network: true, ascii_only: false, enforce_user: false, - enforce_sanitization: false + enforce_sanitization: false, + require_absolute: true }.freeze DEFAULT_OPTIONS = BLOCKER_VALIDATE_OPTIONS.merge({ diff --git a/app/views/projects/settings/operations/_error_tracking.html.haml b/app/views/projects/settings/operations/_error_tracking.html.haml index 583fc08f375..589d3037eba 100644 --- a/app/views/projects/settings/operations/_error_tracking.html.haml +++ b/app/views/projects/settings/operations/_error_tracking.html.haml @@ -17,4 +17,4 @@ project: error_tracking_setting_project_json, api_host: setting.api_host, enabled: setting.enabled.to_json, - token: setting.token } } + token: setting.token.present? ? '*' * 12 : nil } } |