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 +4 -4
- data/CHANGELOG.md +25 -0
- data/admins/pageflow/user.rb +34 -43
- data/app/models/pageflow/account_member_query.rb +62 -0
- data/app/models/pageflow/invitation_form.rb +58 -0
- data/app/models/pageflow/invited_user.rb +0 -2
- data/app/views/admin/users/_form.html.erb +2 -24
- data/app/views/admin/users/invitation.html.erb +42 -0
- data/config/locales/de.yml +2 -0
- data/config/locales/en.yml +2 -0
- data/lib/pageflow/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 38b651a6838c18e932f4aa388ff6056538003958
|
4
|
+
data.tar.gz: 8fb568b33219a77b03288fccec3a910f290b8fc3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/admins/pageflow/user.rb
CHANGED
@@ -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
|
-
|
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
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
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
|
-
|
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
|
189
|
-
|
190
|
-
|
191
|
-
|
179
|
+
def permitted_user_attributes
|
180
|
+
attributes = [
|
181
|
+
:first_name,
|
182
|
+
:last_name,
|
183
|
+
:email,
|
184
|
+
:locale
|
185
|
+
]
|
192
186
|
|
193
|
-
if
|
194
|
-
|
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
|
@@ -1,34 +1,12 @@
|
|
1
|
-
<%= admin_form_for([:admin,
|
1
|
+
<%= admin_form_for([:admin, resource]) do |f| %>
|
2
2
|
<%= f.inputs "Details" do %>
|
3
|
-
|
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 %>
|
data/config/locales/de.yml
CHANGED
@@ -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.
|
data/config/locales/en.yml
CHANGED
@@ -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.
|
data/lib/pageflow/version.rb
CHANGED
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.
|
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-
|
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
|