openstax_accounts 2.0.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +1 -0
- data/Rakefile +6 -6
- data/app/models/openstax/accounts/account.rb +34 -24
- data/app/models/openstax/accounts/application_group.rb +7 -0
- data/app/models/openstax/accounts/group.rb +132 -0
- data/app/models/openstax/accounts/group_member.rb +40 -0
- data/app/models/openstax/accounts/group_nesting.rb +62 -0
- data/app/models/openstax/accounts/group_owner.rb +40 -0
- data/app/representers/openstax/accounts/api/v1/application_group_representer.rb +25 -0
- data/app/representers/openstax/accounts/api/v1/application_groups_representer.rb +16 -0
- data/app/representers/openstax/accounts/api/v1/group_nesting_representer.rb +18 -0
- data/app/representers/openstax/accounts/api/v1/group_representer.rb +50 -0
- data/app/representers/openstax/accounts/api/v1/group_user_representer.rb +21 -0
- data/app/routines/openstax/accounts/search_accounts.rb +7 -14
- data/app/routines/openstax/accounts/sync_accounts.rb +32 -18
- data/app/routines/openstax/accounts/sync_groups.rb +61 -0
- data/app/routines/openstax/accounts/update_group_caches.rb +27 -0
- data/app/views/openstax/accounts/shared/accounts/_index.html.erb +0 -3
- data/config/initializers/action_interceptor.rb +1 -1
- data/db/migrate/20140811182433_create_openstax_accounts_groups.rb +16 -0
- data/db/migrate/20140811182505_create_openstax_accounts_group_members.rb +13 -0
- data/db/migrate/20140811182527_create_openstax_accounts_group_owners.rb +13 -0
- data/db/migrate/20140811182553_create_openstax_accounts_group_nestings.rb +13 -0
- data/lib/generators/openstax/accounts/schedule/templates/schedule.rb +1 -0
- data/lib/openstax/accounts/current_user_manager.rb +2 -2
- data/lib/openstax/accounts/has_many_through_groups.rb +45 -0
- data/lib/openstax/accounts/version.rb +1 -1
- data/lib/openstax_accounts.rb +165 -11
- data/spec/controllers/openstax/accounts/dev/accounts_controller_spec.rb +1 -1
- data/spec/controllers/openstax/accounts/sessions_controller_spec.rb +1 -1
- data/spec/dummy/app/controllers/api/application_groups_controller.rb +11 -0
- data/spec/dummy/app/controllers/api/dummy_controller.rb +2 -1
- data/spec/dummy/app/controllers/api/group_members_controller.rb +11 -0
- data/spec/dummy/app/controllers/api/group_nestings_controller.rb +11 -0
- data/spec/dummy/app/controllers/api/group_owners_controller.rb +11 -0
- data/spec/dummy/app/controllers/api/groups_controller.rb +15 -0
- data/spec/dummy/app/controllers/api/users_controller.rb +4 -0
- data/spec/dummy/app/models/ownership.rb +7 -0
- data/spec/dummy/app/models/user.rb +11 -8
- data/spec/dummy/config/application.rb +0 -33
- data/spec/dummy/config/boot.rb +4 -9
- data/spec/dummy/config/database.yml +8 -8
- data/spec/dummy/config/environment.rb +3 -3
- data/spec/dummy/config/environments/development.rb +20 -12
- data/spec/dummy/config/environments/production.rb +42 -29
- data/spec/dummy/config/environments/test.rb +16 -12
- data/spec/dummy/config/initializers/assets.rb +8 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +6 -5
- data/spec/dummy/config/initializers/mime_types.rb +0 -1
- data/spec/dummy/config/initializers/session_store.rb +1 -6
- data/spec/dummy/config/initializers/wrap_parameters.rb +6 -6
- data/spec/dummy/config/routes.rb +23 -0
- data/spec/dummy/config/secrets.yml +8 -0
- data/spec/dummy/db/migrate/1_create_users.rb +2 -2
- data/spec/dummy/db/migrate/2_create_ownerships.rb +11 -0
- data/spec/dummy/db/schema.rb +72 -20
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +186 -0
- data/spec/dummy/log/test.log +2078 -0
- data/spec/factories/openstax_accounts_account.rb +3 -2
- data/spec/factories/openstax_accounts_group.rb +7 -0
- data/spec/factories/openstax_accounts_group_member.rb +6 -0
- data/spec/factories/openstax_accounts_group_nesting.rb +6 -0
- data/spec/factories/openstax_accounts_group_owner.rb +6 -0
- data/spec/lib/openstax/accounts/current_user_manager_spec.rb +9 -3
- data/spec/lib/openstax/accounts/has_many_through_groups_spec.rb +53 -0
- data/spec/lib/openstax_accounts_spec.rb +189 -25
- data/spec/models/openstax/accounts/account_spec.rb +16 -1
- data/spec/models/openstax/accounts/anonymous_account_spec.rb +1 -1
- data/spec/models/openstax/accounts/group_spec.rb +20 -0
- data/spec/routines/openstax/accounts/sync_accounts_spec.rb +70 -0
- data/spec/routines/openstax/accounts/sync_groups_spec.rb +125 -0
- metadata +73 -56
- data/spec/dummy/config/initializers/secret_token.rb +0 -7
@@ -0,0 +1,18 @@
|
|
1
|
+
module OpenStax
|
2
|
+
module Accounts
|
3
|
+
module Api
|
4
|
+
module V1
|
5
|
+
class GroupNestingRepresenter < Roar::Decorator
|
6
|
+
include Roar::Representer::JSON
|
7
|
+
|
8
|
+
property :container_group_id,
|
9
|
+
type: Integer
|
10
|
+
|
11
|
+
property :member_group_id,
|
12
|
+
type: Integer
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module OpenStax
|
2
|
+
module Accounts
|
3
|
+
module Api
|
4
|
+
module V1
|
5
|
+
class GroupRepresenter < Roar::Decorator
|
6
|
+
include Roar::Representer::JSON
|
7
|
+
|
8
|
+
property :openstax_uid,
|
9
|
+
as: :id,
|
10
|
+
type: Integer
|
11
|
+
|
12
|
+
property :name,
|
13
|
+
type: String
|
14
|
+
|
15
|
+
property :is_public
|
16
|
+
|
17
|
+
collection :group_owners,
|
18
|
+
as: :owners,
|
19
|
+
class: GroupOwner,
|
20
|
+
decorator: GroupUserRepresenter
|
21
|
+
|
22
|
+
collection :group_members,
|
23
|
+
as: :members,
|
24
|
+
class: GroupMember,
|
25
|
+
decorator: GroupUserRepresenter
|
26
|
+
|
27
|
+
collection :member_group_nestings,
|
28
|
+
as: :nestings,
|
29
|
+
class: GroupNesting,
|
30
|
+
decorator: GroupNestingRepresenter
|
31
|
+
|
32
|
+
property :cached_supertree_group_ids,
|
33
|
+
as: :supertree_group_ids,
|
34
|
+
type: Array,
|
35
|
+
schema_info: {
|
36
|
+
items: "integer"
|
37
|
+
}
|
38
|
+
|
39
|
+
property :cached_subtree_group_ids,
|
40
|
+
as: :subtree_group_ids,
|
41
|
+
type: Array,
|
42
|
+
schema_info: {
|
43
|
+
items: "integer"
|
44
|
+
}
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module OpenStax
|
2
|
+
module Accounts
|
3
|
+
module Api
|
4
|
+
module V1
|
5
|
+
class GroupUserRepresenter < Roar::Decorator
|
6
|
+
include Roar::Representer::JSON
|
7
|
+
|
8
|
+
property :group_id,
|
9
|
+
type: Integer
|
10
|
+
|
11
|
+
nested :user do
|
12
|
+
property :user_id,
|
13
|
+
as: :id,
|
14
|
+
type: Integer
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -65,7 +65,7 @@ module OpenStax
|
|
65
65
|
else
|
66
66
|
|
67
67
|
# Local search
|
68
|
-
accounts = OpenStax::Accounts::Account.
|
68
|
+
accounts = OpenStax::Accounts::Account.all
|
69
69
|
|
70
70
|
KeywordSearch.search(query) do |with|
|
71
71
|
|
@@ -73,7 +73,7 @@ module OpenStax
|
|
73
73
|
|
74
74
|
with.keyword :username do |usernames|
|
75
75
|
accounts = accounts.where{username
|
76
|
-
.like_any my{
|
76
|
+
.like_any my{prep_names(usernames)}}
|
77
77
|
end
|
78
78
|
|
79
79
|
with.keyword :first_name do |first_names|
|
@@ -103,7 +103,7 @@ module OpenStax
|
|
103
103
|
end
|
104
104
|
|
105
105
|
with.keyword :email do |emails|
|
106
|
-
accounts = OpenStax::Accounts::Account.
|
106
|
+
accounts = OpenStax::Accounts::Account.none
|
107
107
|
end
|
108
108
|
|
109
109
|
# Rerun the queries above for 'any' terms (which are ones without a
|
@@ -113,7 +113,7 @@ module OpenStax
|
|
113
113
|
names = prep_names(terms)
|
114
114
|
|
115
115
|
accounts = accounts.where{
|
116
|
-
(
|
116
|
+
(lower(username).like_any names) |
|
117
117
|
(lower(first_name).like_any names) |
|
118
118
|
(lower(last_name).like_any names) |
|
119
119
|
(lower(full_name).like_any names) |
|
@@ -175,21 +175,14 @@ module OpenStax
|
|
175
175
|
# Return no results if query exceeds maximum allowed number of matches
|
176
176
|
max_accounts = options[:max_matching_accounts] || \
|
177
177
|
OpenStax::Accounts.configuration.max_matching_accounts
|
178
|
-
outputs[:accounts] = OpenStax::Accounts::Account.
|
178
|
+
outputs[:accounts] = OpenStax::Accounts::Account.none \
|
179
179
|
if outputs[:num_matching_accounts] > max_accounts
|
180
180
|
|
181
181
|
end
|
182
182
|
|
183
|
-
# Downcase, and put a wildcard at the end.
|
184
|
-
# For the moment don't exclude characters.
|
183
|
+
# Downcase, remove all wildcards and put a wildcard at the end.
|
185
184
|
def prep_names(names)
|
186
|
-
names.collect{|name| name.downcase
|
187
|
-
end
|
188
|
-
|
189
|
-
def prep_usernames(usernames)
|
190
|
-
usernames.collect{|username| username.gsub(
|
191
|
-
OpenStax::Accounts::Account::USERNAME_DISCARDED_CHAR_REGEX, '')
|
192
|
-
.downcase + '%'}
|
185
|
+
names.collect{|name| "#{name.downcase.gsub('%', '')}%"}
|
193
186
|
end
|
194
187
|
|
195
188
|
end
|
@@ -16,28 +16,42 @@ module OpenStax
|
|
16
16
|
|
17
17
|
def exec(options={})
|
18
18
|
|
19
|
-
|
19
|
+
begin
|
20
|
+
OpenStax::Accounts.syncing = true
|
20
21
|
|
21
|
-
|
22
|
+
return if OpenStax::Accounts.configuration.enable_stubbing?
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
response = OpenStax::Accounts.get_application_account_updates
|
25
|
+
|
26
|
+
app_accounts = []
|
27
|
+
app_accounts_rep = OpenStax::Accounts::Api::V1::ApplicationAccountsRepresenter
|
25
28
|
.new(app_accounts)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
next unless account.update_attributes(
|
36
|
-
app_account.account.attributes.slice(*SYNC_ATTRIBUTES))
|
37
|
-
app_accounts_hash[app_account.id] = app_account.unread_updates
|
38
|
-
end
|
29
|
+
app_accounts_rep.from_json(response.body)
|
30
|
+
|
31
|
+
return if app_accounts.empty?
|
32
|
+
|
33
|
+
updated_app_accounts = []
|
34
|
+
app_accounts.each do |app_account|
|
35
|
+
account = OpenStax::Accounts::Account.where(
|
36
|
+
:openstax_uid => app_account.account.openstax_uid).first ||\
|
37
|
+
app_account.account
|
39
38
|
|
40
|
-
|
39
|
+
if account != app_account.account
|
40
|
+
SYNC_ATTRIBUTES.each do |attribute|
|
41
|
+
account.send("#{attribute}=", app_account.account.send(attribute))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
next unless account.save
|
46
|
+
|
47
|
+
updated_app_accounts << {user_id: account.openstax_uid,
|
48
|
+
read_updates: app_account.unread_updates}
|
49
|
+
end
|
50
|
+
|
51
|
+
OpenStax::Accounts.mark_account_updates_as_read(updated_app_accounts)
|
52
|
+
ensure
|
53
|
+
OpenStax::Accounts.syncing = false
|
54
|
+
end
|
41
55
|
|
42
56
|
end
|
43
57
|
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Routine for getting group updates from the Accounts server
|
2
|
+
#
|
3
|
+
# Should be scheduled to run regularly
|
4
|
+
|
5
|
+
module OpenStax
|
6
|
+
module Accounts
|
7
|
+
|
8
|
+
class SyncGroups
|
9
|
+
|
10
|
+
SYNC_ATTRIBUTES = ['name', 'is_public', 'group_members',
|
11
|
+
'group_owners', 'member_group_nestings',
|
12
|
+
'cached_supertree_group_ids', 'cached_subtree_group_ids']
|
13
|
+
|
14
|
+
lev_routine transaction: :no_transaction
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def exec(options={})
|
19
|
+
|
20
|
+
begin
|
21
|
+
OpenStax::Accounts.syncing = true
|
22
|
+
|
23
|
+
return if OpenStax::Accounts.configuration.enable_stubbing?
|
24
|
+
|
25
|
+
response = OpenStax::Accounts.get_application_group_updates
|
26
|
+
|
27
|
+
app_groups = []
|
28
|
+
app_groups_rep = OpenStax::Accounts::Api::V1::ApplicationGroupsRepresenter
|
29
|
+
.new(app_groups)
|
30
|
+
app_groups_rep.from_json(response.body)
|
31
|
+
|
32
|
+
return if app_groups.empty?
|
33
|
+
|
34
|
+
updated_app_groups = []
|
35
|
+
app_groups.each do |app_group|
|
36
|
+
group = OpenStax::Accounts::Group.where(
|
37
|
+
:openstax_uid => app_group.group.openstax_uid).first || app_group.group
|
38
|
+
|
39
|
+
if group != app_group.group
|
40
|
+
SYNC_ATTRIBUTES.each do |attribute|
|
41
|
+
group.send("#{attribute}=", app_group.group.send(attribute))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
next unless group.save
|
46
|
+
|
47
|
+
updated_app_groups << {group_id: group.openstax_uid,
|
48
|
+
read_updates: app_group.unread_updates}
|
49
|
+
end
|
50
|
+
|
51
|
+
OpenStax::Accounts.mark_group_updates_as_read(updated_app_groups)
|
52
|
+
ensure
|
53
|
+
OpenStax::Accounts.syncing = false
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Routine for updating group caches when a group_nesting is created or destroyed
|
2
|
+
#
|
3
|
+
# Caller provides the group_nesting object
|
4
|
+
|
5
|
+
module OpenStax
|
6
|
+
module Accounts
|
7
|
+
|
8
|
+
class UpdateGroupCaches
|
9
|
+
|
10
|
+
# This transaction needs :repeatable_read to prevent missed updates
|
11
|
+
lev_routine transaction: :repeatable_read
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def exec(group_nesting)
|
16
|
+
subtree_group_ids = group_nesting.member_group.subtree_group_ids
|
17
|
+
supertree_group_ids = group_nesting.container_group.supertree_group_ids
|
18
|
+
tree_group_ids = (subtree_group_ids + supertree_group_ids).uniq
|
19
|
+
|
20
|
+
Group.where(id: subtree_group_ids).update_all(cached_supertree_group_ids: nil)
|
21
|
+
Group.where(id: supertree_group_ids).update_all(cached_subtree_group_ids: nil)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
<%
|
2
2
|
# Clients of this partial can override the following variables:
|
3
3
|
search_action_path ||= nil
|
4
|
-
results_list_id ||= 'search-results-list'
|
5
4
|
method ||= :post
|
6
5
|
remote ||= false
|
7
6
|
form_html ||= {id: 'search-form',
|
@@ -23,5 +22,3 @@
|
|
23
22
|
<%= f.submit 'Search', class: 'btn btn-primary' %>
|
24
23
|
|
25
24
|
<% end %>
|
26
|
-
|
27
|
-
<div id=<%= results_list_id %>></div>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreateOpenStaxAccountsGroups < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :openstax_accounts_groups do |t|
|
4
|
+
t.integer :openstax_uid, :null => false
|
5
|
+
t.boolean :is_public, null: false, default: false
|
6
|
+
t.string :name
|
7
|
+
t.text :cached_subtree_group_ids
|
8
|
+
t.text :cached_supertree_group_ids
|
9
|
+
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
|
13
|
+
add_index :openstax_accounts_groups, :openstax_uid, :unique => true
|
14
|
+
add_index :openstax_accounts_groups, :is_public
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateOpenStaxAccountsGroupMembers < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :openstax_accounts_group_members do |t|
|
4
|
+
t.references :group, null: false
|
5
|
+
t.references :user, null: false
|
6
|
+
|
7
|
+
t.timestamps
|
8
|
+
end
|
9
|
+
|
10
|
+
add_index :openstax_accounts_group_members, [:group_id, :user_id], unique: true
|
11
|
+
add_index :openstax_accounts_group_members, :user_id
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateOpenStaxAccountsGroupOwners < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :openstax_accounts_group_owners do |t|
|
4
|
+
t.references :group, null: false
|
5
|
+
t.references :user, null: false
|
6
|
+
|
7
|
+
t.timestamps
|
8
|
+
end
|
9
|
+
|
10
|
+
add_index :openstax_accounts_group_owners, [:group_id, :user_id], unique: true
|
11
|
+
add_index :openstax_accounts_group_owners, :user_id
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateOpenStaxAccountsGroupNestings < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :openstax_accounts_group_nestings do |t|
|
4
|
+
t.references :member_group, null: false
|
5
|
+
t.references :container_group, null: false
|
6
|
+
|
7
|
+
t.timestamps
|
8
|
+
end
|
9
|
+
|
10
|
+
add_index :openstax_accounts_group_nestings, :member_group_id, unique: true
|
11
|
+
add_index :openstax_accounts_group_nestings, :container_group_id
|
12
|
+
end
|
13
|
+
end
|
@@ -50,7 +50,7 @@ module OpenStax
|
|
50
50
|
@cookies.delete(:secure_account_id)
|
51
51
|
else
|
52
52
|
@session[:account_id] = account.id
|
53
|
-
@cookies.
|
53
|
+
@cookies.encrypted[:secure_account_id] = { secure: true,
|
54
54
|
httponly: true, value: "secure#{account.id}" }
|
55
55
|
end
|
56
56
|
end
|
@@ -64,7 +64,7 @@ module OpenStax
|
|
64
64
|
# and the signed secure ID doesn't match the unsecure ID
|
65
65
|
# http://railscasts.com/episodes/356-dangers-of-session-hijacking
|
66
66
|
if @request.ssl? && \
|
67
|
-
@cookies.
|
67
|
+
@cookies.encrypted[:secure_account_id] != "secure#{@session[:account_id]}"
|
68
68
|
sign_out!
|
69
69
|
return
|
70
70
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module OpenStax
|
2
|
+
module Accounts
|
3
|
+
module HasManyThroughGroups
|
4
|
+
module ActiveRecord
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def has_many_through_groups(groups_name, name, options = {})
|
11
|
+
options = {class_name: name.to_s.classify}.merge(options)
|
12
|
+
association_name = "direct_#{name.to_s}".to_sym
|
13
|
+
|
14
|
+
OpenStax::Accounts::Group.class_exec do
|
15
|
+
has_many association_name, options
|
16
|
+
|
17
|
+
define_method(name) do
|
18
|
+
OpenStax::Accounts::Group.includes(association_name)
|
19
|
+
.where(id: supertree_group_ids)
|
20
|
+
.collect{|g| g.send(association_name).to_a}.flatten.uniq
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class_exec do
|
25
|
+
has_many association_name, options if options[:as]
|
26
|
+
|
27
|
+
define_method(name) do
|
28
|
+
direct_records = respond_to?(association_name) ? \
|
29
|
+
send(association_name).to_a : []
|
30
|
+
indirect_records = OpenStax::Accounts::Group
|
31
|
+
.includes(association_name).where(
|
32
|
+
id: send(groups_name).collect{|g| g.supertree_group_ids}.flatten.uniq
|
33
|
+
)
|
34
|
+
.collect{|g| g.send(association_name).to_a}
|
35
|
+
(direct_records + indirect_records).flatten.uniq
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
ActiveRecord::Base.send :include, OpenStax::Accounts::HasManyThroughGroups::ActiveRecord
|