pageflow 12.0.0 → 12.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of pageflow might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5743e9c16455ddedda06ac7f2f335a5a6361b320
4
- data.tar.gz: c8ba3dc3473a937a1a18fa8d95abd5dc36baec99
3
+ metadata.gz: 38b651a6838c18e932f4aa388ff6056538003958
4
+ data.tar.gz: 8fb568b33219a77b03288fccec3a910f290b8fc3
5
5
  SHA512:
6
- metadata.gz: 55da5bc6a8c3305171d819d154a62eefcdd8341dbc0fe753fde956ece59f5f19709619796d3d1ba4ce2d3e61165260273a11d84a61f1c0f93877307af21c5da0
7
- data.tar.gz: b9e360e1243a39c35299c12e152dadcd2eba5c03704823e445eb0ad666bdb5e528852901edfc37697450e00031451e9c435b134e8d09c6783a9300313bcf3003
6
+ metadata.gz: e7cfec9c5b6573463fbef5f19dae0591b4f0100ff5512aec0fde2eb057e53e2ac274509a90c6d212d13e66ddb8f240788f889bc5ba31818f4fa215266d81f83b
7
+ data.tar.gz: 36f4bc205f70ae0676f590442fbba3bda6cdbdaff9fa24aa676f394c4764be4f7e7196917a023c544f593faf1501957760e85609034cedeab9f0844a36bd4118
data/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # CHANGELOG
2
2
 
3
+ ### Version 12.0.1
4
+
5
+ 2017-08-25
6
+
7
+ [Compare changes](https://github.com/codevise/pageflow/compare/v12.0.0...v12.0.1)
8
+
9
+ #### Security Patch
10
+
11
+ Prevent escalation of privileges
12
+ ([#845](https://github.com/codevise/pageflow/pull/845))
13
+
14
+ There are two bugs in Pageflow 12.0.0 that allow signed in users
15
+ to escalate their privileges.
16
+
17
+ * With a manually crafted request, any signed in user can set the
18
+ admin flag on their own user account.
19
+
20
+ * With a manually crafted request, a user with account manager role in
21
+ at least one account, can add users to arbitrary accounts. This can
22
+ be used to gain account manager privileges in any account.
23
+
24
+ Affected versions: 12.0.0 (including all release candidates)
25
+ Unaffected version: 0.11.x and older
26
+ Versions fixes: 12.0.1
27
+
3
28
  ### Version 12.0.0
4
29
 
5
30
  2017-08-10
@@ -2,6 +2,8 @@ module Pageflow
2
2
  ActiveAdmin.register User do
3
3
  menu priority: 2, if: proc { authorized?(:index, current_user) }
4
4
 
5
+ actions :all, except: [:new, :create]
6
+
5
7
  config.batch_actions = false
6
8
  config.clear_action_items!
7
9
 
@@ -24,7 +26,7 @@ module Pageflow
24
26
 
25
27
  action_item(:invite, only: :index) do
26
28
  link_to I18n.t('pageflow.admin.users.invite_user'),
27
- new_admin_user_path,
29
+ invitation_admin_users_path,
28
30
  data: {rel: 'invite_user'}
29
31
  end
30
32
 
@@ -91,6 +93,21 @@ module Pageflow
91
93
 
92
94
  form(partial: 'form')
93
95
 
96
+ collection_action :invitation, method: [:get, :post] do
97
+ @page_title = I18n.t('pageflow.admin.users.invite_user')
98
+ @invitation_form = InvitationForm.new(invitation_form_params.fetch(:invitation_form, {}),
99
+ AccountPolicy::Scope.new(current_user, Account)
100
+ .member_addable)
101
+
102
+ if request.post?
103
+ if @invitation_form.save
104
+ redirect_to(admin_user_path(@invitation_form.target_user))
105
+ else
106
+ render status: 422
107
+ end
108
+ end
109
+ end
110
+
94
111
  collection_action 'me', title: I18n.t('pageflow.admin.users.account'), method: [:get, :patch] do
95
112
  if request.patch?
96
113
  if current_user.update_with_password(user_profile_params)
@@ -137,26 +154,11 @@ module Pageflow
137
154
  super.includes(account_memberships: :entity)
138
155
  end
139
156
 
140
- def build_new_resource
141
- InvitedUser.new(permitted_params[:user])
142
- end
143
-
144
- def create_resource(user)
145
- verify_quota!(:users, params[:user][:account])
146
- known_user = User.find_by(email: resource.email)
147
- membership_user = known_user ? known_user : resource
148
- membership_params = {user: membership_user,
149
- entity_id: resource.initial_account,
150
- entity_type: 'Pageflow::Account'}
151
- if resource.initial_role.present?
152
- membership_params.merge!(role: resource.initial_role.to_sym)
153
- end
154
- Membership.create(membership_params)
155
- if known_user
156
- known_user
157
- else
158
- super
159
- end
157
+ def invitation_form_params
158
+ params.permit(invitation_form: {
159
+ user: permitted_user_attributes,
160
+ membership: [:entity_id, :role]
161
+ })
160
162
  end
161
163
 
162
164
  def user_profile_params
@@ -165,36 +167,25 @@ module Pageflow
165
167
  :current_password,
166
168
  :password,
167
169
  :password_confirmation,
168
- :locale,
169
- :admin)
170
+ :locale)
170
171
  end
171
172
 
172
173
  def permitted_params
173
- result = params.permit(user: [:first_name,
174
- :last_name,
175
- :email,
176
- :password,
177
- :password_confirmation,
178
- :locale,
179
- :admin,
180
- :initial_role,
181
- :initial_account])
182
- restrict_attributes(params[:id], result[:user]) if result[:user]
183
- result
174
+ params.permit(user: permitted_user_attributes)
184
175
  end
185
176
 
186
177
  private
187
178
 
188
- def restrict_attributes(_id, attributes)
189
- unless authorized?(:set_admin, current_user)
190
- attributes.delete(:admin)
191
- end
179
+ def permitted_user_attributes
180
+ attributes = [
181
+ :first_name,
182
+ :last_name,
183
+ :email,
184
+ :locale
185
+ ]
192
186
 
193
- if AccountPolicy::Scope.new(current_user, Pageflow::Account).member_addable.empty? ||
194
- action_name.to_sym != :create
195
- attributes.delete(:initial_role)
196
- attributes.delete(:initial_account)
197
- end
187
+ attributes << :admin if authorized?(:set_admin, current_user)
188
+ attributes
198
189
  end
199
190
  end
200
191
  end
@@ -0,0 +1,62 @@
1
+ module Pageflow
2
+ class AccountMemberQuery
3
+ class Scope
4
+ # Account whose members we scope
5
+ # @return {Pageflow::Account}
6
+ attr_reader :account
7
+
8
+ # Base scope which is further scoped according to account role
9
+ # @return [ActiveRecord::Relation<User>]
10
+ attr_reader :scope
11
+
12
+ # Create scope that can limit base scope to account members at
13
+ # or above a given role
14
+ #
15
+ # @param {Pageflow::Account} account
16
+ # Required. Membership account to check.
17
+ # @param [ActiveRecord::Relation<User>] scope
18
+ # Optional. Membership entity to check.
19
+ def initialize(account, scope = User.all)
20
+ @account = account
21
+ @scope = scope
22
+ end
23
+
24
+ # Scope to those members from scope on account who have at least
25
+ # role
26
+ #
27
+ # @param {String} role
28
+ # Required. Minimum role that we compare against.
29
+ # @return [ActiveRecord::Relation<User>]
30
+ def with_role_at_least(role)
31
+ scope.joins(memberships_for_account_with_at_least_role(role))
32
+ .where(membership_is_present)
33
+ end
34
+
35
+ private
36
+
37
+ # @api private
38
+ def memberships_for_account_with_at_least_role(role)
39
+ options = {roles: Roles.at_least(role), account_id: account.id}
40
+
41
+ sanitize_sql(<<-SQL, options)
42
+ LEFT OUTER JOIN pageflow_memberships as
43
+ pageflow_account_memberships ON
44
+ pageflow_account_memberships.user_id = users.id AND
45
+ pageflow_account_memberships.role IN (:roles) AND
46
+ pageflow_account_memberships.entity_id = :account_id AND
47
+ pageflow_account_memberships.entity_type = 'Pageflow::Account'
48
+ SQL
49
+ end
50
+
51
+ # @api private
52
+ def membership_is_present
53
+ 'pageflow_account_memberships.entity_id IS NOT NULL'
54
+ end
55
+
56
+ # @api private
57
+ def sanitize_sql(sql, interpolations)
58
+ ActiveRecord::Base.send(:sanitize_sql_array, [sql, interpolations])
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,58 @@
1
+ module Pageflow
2
+ # @api private
3
+ class InvitationForm
4
+ include ActiveModel::Model
5
+
6
+ attr_reader :membership, :target_user, :available_accounts
7
+
8
+ def initialize(attributes, available_accounts)
9
+ @attributes = attributes
10
+ @available_accounts = available_accounts
11
+
12
+ @invited_user = InvitedUser.new(attributes[:user])
13
+ @target_user = existing_user || @invited_user
14
+
15
+ @membership = @target_user.memberships.build(entity: initial_account,
16
+ role: initial_role)
17
+ end
18
+
19
+ def user
20
+ @invited_user.becomes(User)
21
+ end
22
+
23
+ def save
24
+ return false unless valid?
25
+ Pageflow.config.quotas.get(:users, initial_account).verify_available!
26
+
27
+ membership.save!
28
+ end
29
+
30
+ def valid?
31
+ (existing_user || @invited_user.valid?) && membership.valid?
32
+ end
33
+
34
+ def existing_member
35
+ @existing_member ||=
36
+ initial_account && initial_account.users.find_by_email(user.email)
37
+ end
38
+
39
+ private
40
+
41
+ def existing_user
42
+ @existing_user ||=
43
+ User.find_by_email(user.email)
44
+ end
45
+
46
+ def initial_account
47
+ @initial_account ||= available_accounts.find_by_id(initial_account_id)
48
+ end
49
+
50
+ def initial_account_id
51
+ @attributes.fetch(:membership, {})[:entity_id]
52
+ end
53
+
54
+ def initial_role
55
+ @attributes.fetch(:membership, {})[:role] || 'member'
56
+ end
57
+ end
58
+ end
@@ -2,8 +2,6 @@ module Pageflow
2
2
  # Specialized User class containing invitation logic used by in the
3
3
  # users admin.
4
4
  class InvitedUser < User
5
- attr_accessor :initial_account, :initial_role
6
-
7
5
  before_create :prepare_invitation
8
6
  after_create :send_invitation
9
7
 
@@ -1,34 +1,12 @@
1
- <%= admin_form_for([:admin, @user.becomes(User)]) do |f| %>
1
+ <%= admin_form_for([:admin, resource]) do |f| %>
2
2
  <%= f.inputs "Details" do %>
3
- <% if @user.new_record? %>
4
- <% Pageflow::AccountPolicy::Scope
5
- .new(current_user, Pageflow::Account).member_addable.each do |account| %>
6
- <%= quota_state_description(:users, account) %>
7
- <% end %>
8
- <% end %>
9
-
10
- <%= f.input :email, hint: f.object.new_record? &&
11
- I18n.t('pageflow.admin.users.email_invitation_hint') %>
3
+ <%= f.input :email %>
12
4
  <%= f.input :first_name %>
13
5
  <%= f.input :last_name %>
14
6
  <%= f.input :locale,
15
7
  as: :select,
16
8
  include_blank: false,
17
9
  collection: available_locales_collection %>
18
-
19
- <% if Pageflow::AccountPolicy::Scope
20
- .new(current_user, Pageflow::Account).member_addable.any? && @user.new_record? %>
21
- <%= f.input :initial_account,
22
- collection: membership_accounts_collection(@user,
23
- Pageflow::Membership.new(user: @user)),
24
- include_blank: false,
25
- selected: (params[:user][:initial_account] if params[:user].present?) %>
26
- <%= f.input :initial_role,
27
- collection: membership_roles_collection('Pageflow::Account'),
28
- include_blank: false,
29
- hint: t('pageflow.admin.memberships.on_account.role.hint_html'),
30
- selected: (params[:user][:initial_role] if params[:user].present?) %>
31
- <% end %>
32
10
  <%= f.input :admin if authorized?(:set_admin, current_user) %>
33
11
  <% end %>
34
12
  <%= f.actions %>
@@ -0,0 +1,42 @@
1
+ <%= admin_form_for(@invitation_form, url: invitation_admin_users_path) do |f| %>
2
+ <%= f.semantic_fields_for(@invitation_form.membership) do |m| %>
3
+ <%= f.semantic_fields_for(@invitation_form.user) do |u| %>
4
+ <% if @invitation_form.existing_member %>
5
+ <ul class="errors">
6
+ <li>
7
+ <%= t('pageflow.admin.users.member_exists') %>
8
+ <%= link_to(t('pageflow.admin.users.member_exists_link'),
9
+ admin_user_path(@invitation_form.existing_member)) %>
10
+ </li>
11
+ </ul>
12
+ <% end %>
13
+
14
+ <%= f.inputs "Details" do %>
15
+ <% @invitation_form.available_accounts.each do |account| %>
16
+ <%= quota_state_description(:users, account) %>
17
+ <% end %>
18
+
19
+ <%= u.input :email, hint: I18n.t('pageflow.admin.users.email_invitation_hint') %>
20
+ <%= u.input :first_name %>
21
+ <%= u.input :last_name %>
22
+ <%= u.input :locale,
23
+ as: :select,
24
+ include_blank: false,
25
+ collection: available_locales_collection %>
26
+ <%= m.input :entity_id,
27
+ as: :select,
28
+ collection: @invitation_form.available_accounts,
29
+ include_blank: false,
30
+ label: Pageflow::Membership.human_attribute_name(:account) %>
31
+ <%= m.input :role,
32
+ collection: membership_roles_collection('Pageflow::Account'),
33
+ include_blank: false,
34
+ hint: t('pageflow.admin.memberships.on_account.role.hint_html') %>
35
+ <%= u.input :admin if authorized?(:set_admin, current_user) %>
36
+ <% end %>
37
+ <% end %>
38
+ <% end %>
39
+ <%= f.actions do %>
40
+ <%= f.action :submit, label: t('pageflow.admin.users.invite_user') %>
41
+ <% end %>
42
+ <% end %>
@@ -869,6 +869,8 @@ de:
869
869
  delete_account_hint: Du möchtest die Anwendung nicht mehr nutzen?
870
870
  deleted: Dein Benutzerkonto wurde gelöscht.
871
871
  updated: Dein Profil wurde aktualisiert.
872
+ member_exists: Ein Benutzer mit der angegebenen E-Mail-Adresse ist bereits Mitglied des Kontos.
873
+ member_exists_link: Benutzer anzeigen
872
874
  no_accounts: Keine Konten
873
875
  no_entries: Keine Beiträge
874
876
  none: Sie haben keine Beiträge.
@@ -869,6 +869,8 @@ en:
869
869
  delete_account_hint: You don't want to continue using Pageflow?
870
870
  deleted: Your account has been deleted. You may sign-up for a new one at any time!
871
871
  updated: Your profile has been updated.
872
+ member_exists: A user with the given email address is already member of the account.
873
+ member_exists_link: View user
872
874
  no_accounts: No members
873
875
  no_entries: No stories
874
876
  none: There are no stories.
@@ -1,3 +1,3 @@
1
1
  module Pageflow
2
- VERSION = '12.0.0'.freeze
2
+ VERSION = '12.0.1'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pageflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 12.0.0
4
+ version: 12.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Codevise Solutions Ltd
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-10 00:00:00.000000000 Z
11
+ date: 2017-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -1612,6 +1612,7 @@ files:
1612
1612
  - app/models/concerns/pageflow/suspendable.rb
1613
1613
  - app/models/concerns/pageflow/uploaded_file.rb
1614
1614
  - app/models/pageflow/account.rb
1615
+ - app/models/pageflow/account_member_query.rb
1615
1616
  - app/models/pageflow/audio_file.rb
1616
1617
  - app/models/pageflow/audio_file_url_templates.rb
1617
1618
  - app/models/pageflow/chapter.rb
@@ -1630,6 +1631,7 @@ files:
1630
1631
  - app/models/pageflow/home_button.rb
1631
1632
  - app/models/pageflow/image_file.rb
1632
1633
  - app/models/pageflow/image_file_url_templates.rb
1634
+ - app/models/pageflow/invitation_form.rb
1633
1635
  - app/models/pageflow/invited_user.rb
1634
1636
  - app/models/pageflow/membership.rb
1635
1637
  - app/models/pageflow/null_user.rb
@@ -1679,6 +1681,7 @@ files:
1679
1681
  - app/views/admin/users/_form.html.erb
1680
1682
  - app/views/admin/users/_quota_exhausted.html.erb
1681
1683
  - app/views/admin/users/delete_me.html.erb
1684
+ - app/views/admin/users/invitation.html.erb
1682
1685
  - app/views/admin/users/me.html.erb
1683
1686
  - app/views/components/pageflow/admin/add_membership_button_if_needed.rb
1684
1687
  - app/views/components/pageflow/admin/custom_scopes_renderer.rb