Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Jérome Perrin
gitlab-ce
Commits
fd05e266
Commit
fd05e266
authored
Oct 11, 2016
by
Ahmad Sherif
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Precalculate user's authorized projects in database
Closes #23150
parent
aea8baed
Changes
27
Show whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
336 additions
and
51 deletions
+336
-51
app/models/concerns/select_for_project_authorization.rb
app/models/concerns/select_for_project_authorization.rb
+9
-0
app/models/group.rb
app/models/group.rb
+13
-0
app/models/member.rb
app/models/member.rb
+13
-0
app/models/project.rb
app/models/project.rb
+2
-31
app/models/project_authorization.rb
app/models/project_authorization.rb
+8
-0
app/models/project_group_link.rb
app/models/project_group_link.rb
+7
-0
app/models/user.rb
app/models/user.rb
+50
-16
app/services/projects/create_service.rb
app/services/projects/create_service.rb
+2
-0
app/services/user_project_access_changed_service.rb
app/services/user_project_access_changed_service.rb
+9
-0
app/workers/authorized_projects_worker.rb
app/workers/authorized_projects_worker.rb
+15
-0
changelogs/unreleased/feature-precalculate-authorized-projects.yml
...s/unreleased/feature-precalculate-authorized-projects.yml
+4
-0
config/sidekiq_queues.yml
config/sidekiq_queues.yml
+1
-0
db/fixtures/development/04_project.rb
db/fixtures/development/04_project.rb
+1
-0
db/fixtures/development/17_cycle_analytics.rb
db/fixtures/development/17_cycle_analytics.rb
+1
-0
db/fixtures/support/serialized_transaction.rb
db/fixtures/support/serialized_transaction.rb
+9
-0
db/migrate/20161010142410_create_project_authorizations.rb
db/migrate/20161010142410_create_project_authorizations.rb
+15
-0
db/migrate/20161017091941_add_authorized_projects_populated_to_users.rb
...61017091941_add_authorized_projects_populated_to_users.rb
+9
-0
db/schema.rb
db/schema.rb
+11
-0
lib/gitlab/database.rb
lib/gitlab/database.rb
+7
-0
spec/finders/group_projects_finder_spec.rb
spec/finders/group_projects_finder_spec.rb
+4
-1
spec/models/member_spec.rb
spec/models/member_spec.rb
+22
-0
spec/models/project_group_link_spec.rb
spec/models/project_group_link_spec.rb
+16
-0
spec/models/project_spec.rb
spec/models/project_spec.rb
+4
-2
spec/models/user_spec.rb
spec/models/user_spec.rb
+75
-1
spec/services/projects/create_service_spec.rb
spec/services/projects/create_service_spec.rb
+3
-0
spec/support/db_cleaner.rb
spec/support/db_cleaner.rb
+4
-0
spec/workers/authorized_projects_worker_spec.rb
spec/workers/authorized_projects_worker_spec.rb
+22
-0
No files found.
app/models/concerns/select_for_project_authorization.rb
0 → 100644
View file @
fd05e266
module
SelectForProjectAuthorization
extend
ActiveSupport
::
Concern
module
ClassMethods
def
select_for_project_authorization
select
(
"members.user_id, projects.id AS project_id, members.access_level"
)
end
end
end
app/models/group.rb
View file @
fd05e266
...
@@ -5,6 +5,7 @@ class Group < Namespace
...
@@ -5,6 +5,7 @@ class Group < Namespace
include
Gitlab
::
VisibilityLevel
include
Gitlab
::
VisibilityLevel
include
AccessRequestable
include
AccessRequestable
include
Referable
include
Referable
include
SelectForProjectAuthorization
has_many
:group_members
,
->
{
where
(
requested_at:
nil
)
},
dependent: :destroy
,
as: :source
has_many
:group_members
,
->
{
where
(
requested_at:
nil
)
},
dependent: :destroy
,
as: :source
alias_method
:members
,
:group_members
alias_method
:members
,
:group_members
...
@@ -61,6 +62,14 @@ class Group < Namespace
...
@@ -61,6 +62,14 @@ class Group < Namespace
def
visible_to_user
(
user
)
def
visible_to_user
(
user
)
where
(
id:
user
.
authorized_groups
.
select
(
:id
).
reorder
(
nil
))
where
(
id:
user
.
authorized_groups
.
select
(
:id
).
reorder
(
nil
))
end
end
def
select_for_project_authorization
if
current_scope
.
joins_values
.
include?
(
:shared_projects
)
select
(
"members.user_id, projects.id AS project_id, project_group_links.group_access"
)
else
super
end
end
end
end
def
to_reference
(
_from_project
=
nil
)
def
to_reference
(
_from_project
=
nil
)
...
@@ -176,4 +185,8 @@ class Group < Namespace
...
@@ -176,4 +185,8 @@ class Group < Namespace
def
system_hook_service
def
system_hook_service
SystemHooksService
.
new
SystemHooksService
.
new
end
end
def
refresh_members_authorized_projects
UserProjectAccessChangedService
.
new
(
users
.
pluck
(
:id
)).
execute
end
end
end
app/models/member.rb
View file @
fd05e266
...
@@ -113,6 +113,8 @@ class Member < ActiveRecord::Base
...
@@ -113,6 +113,8 @@ class Member < ActiveRecord::Base
member
.
save
member
.
save
end
end
UserProjectAccessChangedService
.
new
(
user
.
id
).
execute
if
user
.
is_a?
(
User
)
member
member
end
end
...
@@ -239,6 +241,7 @@ class Member < ActiveRecord::Base
...
@@ -239,6 +241,7 @@ class Member < ActiveRecord::Base
end
end
def
post_create_hook
def
post_create_hook
UserProjectAccessChangedService
.
new
(
user
.
id
).
execute
system_hook_service
.
execute_hooks_for
(
self
,
:create
)
system_hook_service
.
execute_hooks_for
(
self
,
:create
)
end
end
...
@@ -247,9 +250,19 @@ class Member < ActiveRecord::Base
...
@@ -247,9 +250,19 @@ class Member < ActiveRecord::Base
end
end
def
post_destroy_hook
def
post_destroy_hook
refresh_member_authorized_projects
system_hook_service
.
execute_hooks_for
(
self
,
:destroy
)
system_hook_service
.
execute_hooks_for
(
self
,
:destroy
)
end
end
def
refresh_member_authorized_projects
# If user/source is being destroyed, project access are gonna be destroyed eventually
# because of DB foreign keys, so we shouldn't bother with refreshing after each
# member is destroyed through association
return
if
destroyed_by_association
.
present?
UserProjectAccessChangedService
.
new
(
user_id
).
execute
end
def
after_accept_invite
def
after_accept_invite
post_create_hook
post_create_hook
end
end
...
...
app/models/project.rb
View file @
fd05e266
...
@@ -13,6 +13,7 @@ class Project < ActiveRecord::Base
...
@@ -13,6 +13,7 @@ class Project < ActiveRecord::Base
include
CaseSensitivity
include
CaseSensitivity
include
TokenAuthenticatable
include
TokenAuthenticatable
include
ProjectFeaturesCompatibility
include
ProjectFeaturesCompatibility
include
SelectForProjectAuthorization
extend
Gitlab
::
ConfigHelper
extend
Gitlab
::
ConfigHelper
...
@@ -1289,16 +1290,10 @@ class Project < ActiveRecord::Base
...
@@ -1289,16 +1290,10 @@ class Project < ActiveRecord::Base
# Checks if `user` is authorized for this project, with at least the
# Checks if `user` is authorized for this project, with at least the
# `min_access_level` (if given).
# `min_access_level` (if given).
#
# If you change the logic of this method, please also update `User#authorized_projects`
def
authorized_for_user?
(
user
,
min_access_level
=
nil
)
def
authorized_for_user?
(
user
,
min_access_level
=
nil
)
return
false
unless
user
return
false
unless
user
return
true
if
personal?
&&
namespace_id
==
user
.
namespace_id
user
.
authorized_project?
(
self
,
min_access_level
)
authorized_for_user_by_group?
(
user
,
min_access_level
)
||
authorized_for_user_by_members?
(
user
,
min_access_level
)
||
authorized_for_user_by_shared_projects?
(
user
,
min_access_level
)
end
end
def
append_or_update_attribute
(
name
,
value
)
def
append_or_update_attribute
(
name
,
value
)
...
@@ -1358,30 +1353,6 @@ class Project < ActiveRecord::Base
...
@@ -1358,30 +1353,6 @@ class Project < ActiveRecord::Base
current_application_settings
.
default_branch_protection
==
Gitlab
::
Access
::
PROTECTION_DEV_CAN_MERGE
current_application_settings
.
default_branch_protection
==
Gitlab
::
Access
::
PROTECTION_DEV_CAN_MERGE
end
end
def
authorized_for_user_by_group?
(
user
,
min_access_level
)
member
=
user
.
group_members
.
find_by
(
source_id:
group
)
member
&&
(
!
min_access_level
||
member
.
access_level
>=
min_access_level
)
end
def
authorized_for_user_by_members?
(
user
,
min_access_level
)
member
=
members
.
find_by
(
user_id:
user
)
member
&&
(
!
min_access_level
||
member
.
access_level
>=
min_access_level
)
end
def
authorized_for_user_by_shared_projects?
(
user
,
min_access_level
)
shared_projects
=
user
.
group_members
.
joins
(
group: :shared_projects
).
where
(
project_group_links:
{
project_id:
self
})
if
min_access_level
members_scope
=
{
access_level:
Gitlab
::
Access
.
values
.
select
{
|
access
|
access
>=
min_access_level
}
}
shared_projects
=
shared_projects
.
where
(
members:
members_scope
)
end
shared_projects
.
any?
end
# Similar to the normal callbacks that hook into the life cycle of an
# Similar to the normal callbacks that hook into the life cycle of an
# Active Record object, you can also define callbacks that get triggered
# Active Record object, you can also define callbacks that get triggered
# when you add an object to an association collection. If any of these
# when you add an object to an association collection. If any of these
...
...
app/models/project_authorization.rb
0 → 100644
View file @
fd05e266
class
ProjectAuthorization
<
ActiveRecord
::
Base
belongs_to
:user
belongs_to
:project
validates
:project
,
presence:
true
validates
:access_level
,
inclusion:
{
in:
Gitlab
::
Access
.
all_values
},
presence:
true
validates
:user
,
uniqueness:
{
scope:
[
:project
,
:access_level
]
},
presence:
true
end
app/models/project_group_link.rb
View file @
fd05e266
...
@@ -16,6 +16,9 @@ class ProjectGroupLink < ActiveRecord::Base
...
@@ -16,6 +16,9 @@ class ProjectGroupLink < ActiveRecord::Base
validates
:group_access
,
inclusion:
{
in:
Gitlab
::
Access
.
values
},
presence:
true
validates
:group_access
,
inclusion:
{
in:
Gitlab
::
Access
.
values
},
presence:
true
validate
:different_group
validate
:different_group
after_create
:refresh_group_members_authorized_projects
after_destroy
:refresh_group_members_authorized_projects
def
self
.
access_options
def
self
.
access_options
Gitlab
::
Access
.
options
Gitlab
::
Access
.
options
end
end
...
@@ -35,4 +38,8 @@ class ProjectGroupLink < ActiveRecord::Base
...
@@ -35,4 +38,8 @@ class ProjectGroupLink < ActiveRecord::Base
errors
.
add
(
:base
,
"Project cannot be shared with the project it is in."
)
errors
.
add
(
:base
,
"Project cannot be shared with the project it is in."
)
end
end
end
end
def
refresh_group_members_authorized_projects
group
.
refresh_members_authorized_projects
end
end
end
app/models/user.rb
View file @
fd05e266
...
@@ -72,6 +72,8 @@ class User < ActiveRecord::Base
...
@@ -72,6 +72,8 @@ class User < ActiveRecord::Base
has_many
:created_projects
,
foreign_key: :creator_id
,
class_name:
'Project'
has_many
:created_projects
,
foreign_key: :creator_id
,
class_name:
'Project'
has_many
:users_star_projects
,
dependent: :destroy
has_many
:users_star_projects
,
dependent: :destroy
has_many
:starred_projects
,
through: :users_star_projects
,
source: :project
has_many
:starred_projects
,
through: :users_star_projects
,
source: :project
has_many
:project_authorizations
,
dependent: :destroy
has_many
:authorized_projects
,
through: :project_authorizations
,
source: :project
has_many
:snippets
,
dependent: :destroy
,
foreign_key: :author_id
has_many
:snippets
,
dependent: :destroy
,
foreign_key: :author_id
has_many
:issues
,
dependent: :destroy
,
foreign_key: :author_id
has_many
:issues
,
dependent: :destroy
,
foreign_key: :author_id
...
@@ -438,11 +440,44 @@ class User < ActiveRecord::Base
...
@@ -438,11 +440,44 @@ class User < ActiveRecord::Base
Group
.
where
(
"namespaces.id IN (
#{
union
.
to_sql
}
)"
)
Group
.
where
(
"namespaces.id IN (
#{
union
.
to_sql
}
)"
)
end
end
# Returns projects user is authorized to access.
def
refresh_authorized_projects
#
loop
do
# If you change the logic of this method, please also update `Project#authorized_for_user`
begin
Gitlab
::
Database
.
serialized_transaction
do
project_authorizations
.
delete_all
# project_authorizations_union can return multiple records for the same project/user with
# different access_level so we take row with the maximum access_level
project_authorizations
.
connection
.
execute
<<-
SQL
INSERT INTO project_authorizations (user_id, project_id, access_level)
SELECT user_id, project_id, MAX(access_level) AS access_level
FROM (
#{
project_authorizations_union
.
to_sql
}
) sub
GROUP BY user_id, project_id
SQL
update_column
(
:authorized_projects_populated
,
true
)
unless
authorized_projects_populated
end
break
# In the event of a concurrent modification Rails raises StatementInvalid.
# In this case we want to keep retrying until the transaction succeeds
rescue
ActiveRecord
::
StatementInvalid
end
end
end
def
authorized_projects
(
min_access_level
=
nil
)
def
authorized_projects
(
min_access_level
=
nil
)
Project
.
where
(
"projects.id IN (
#{
projects_union
(
min_access_level
).
to_sql
}
)"
)
refresh_authorized_projects
unless
authorized_projects_populated
# We're overriding an association, so explicitly call super with no arguments or it would be passed as `force_reload` to the association
projects
=
super
()
projects
=
projects
.
where
(
'project_authorizations.access_level >= ?'
,
min_access_level
)
if
min_access_level
projects
end
def
authorized_project?
(
project
,
min_access_level
=
nil
)
authorized_projects
(
min_access_level
).
exists?
({
id:
project
.
id
})
end
end
# Returns the projects this user has reporter (or greater) access to, limited
# Returns the projects this user has reporter (or greater) access to, limited
...
@@ -456,8 +491,9 @@ class User < ActiveRecord::Base
...
@@ -456,8 +491,9 @@ class User < ActiveRecord::Base
end
end
def
viewable_starred_projects
def
viewable_starred_projects
starred_projects
.
where
(
"projects.visibility_level IN (?) OR projects.id IN (
#{
projects_union
.
to_sql
}
)"
,
starred_projects
.
where
(
"projects.visibility_level IN (?) OR projects.id IN (?)"
,
[
Project
::
PUBLIC
,
Project
::
INTERNAL
])
[
Project
::
PUBLIC
,
Project
::
INTERNAL
],
authorized_projects
.
select
(
:project_id
))
end
end
def
owned_projects
def
owned_projects
...
@@ -887,16 +923,14 @@ class User < ActiveRecord::Base
...
@@ -887,16 +923,14 @@ class User < ActiveRecord::Base
private
private
def
projects_union
(
min_access_level
=
nil
)
# Returns a union query of projects that the user is authorized to access
relations
=
[
personal_projects
.
select
(
:id
),
def
project_authorizations_union
groups_projects
.
select
(
:id
),
relations
=
[
projects
.
select
(
:id
),
personal_projects
.
select
(
"
#{
id
}
AS user_id, projects.id AS project_id,
#{
Gitlab
::
Access
::
OWNER
}
AS access_level"
),
groups
.
joins
(
:shared_projects
).
select
(
:project_id
)]
groups_projects
.
select_for_project_authorization
,
projects
.
select_for_project_authorization
,
if
min_access_level
groups
.
joins
(
:shared_projects
).
select_for_project_authorization
scope
=
{
access_level:
Gitlab
::
Access
.
all_values
.
select
{
|
access
|
access
>=
min_access_level
}
}
]
relations
=
[
relations
.
shift
]
+
relations
.
map
{
|
relation
|
relation
.
where
(
members:
scope
)
}
end
Gitlab
::
SQL
::
Union
.
new
(
relations
)
Gitlab
::
SQL
::
Union
.
new
(
relations
)
end
end
...
...
app/services/projects/create_service.rb
View file @
fd05e266
...
@@ -106,6 +106,8 @@ module Projects
...
@@ -106,6 +106,8 @@ module Projects
unless
@project
.
group
||
@project
.
gitlab_project_import?
unless
@project
.
group
||
@project
.
gitlab_project_import?
@project
.
team
<<
[
current_user
,
:master
,
current_user
]
@project
.
team
<<
[
current_user
,
:master
,
current_user
]
end
end
@project
.
group
.
refresh_members_authorized_projects
if
@project
.
group
end
end
def
skip_wiki?
def
skip_wiki?
...
...
app/services/user_project_access_changed_service.rb
0 → 100644
View file @
fd05e266
class
UserProjectAccessChangedService
def
initialize
(
user_ids
)
@user_ids
=
Array
.
wrap
(
user_ids
)
end
def
execute
AuthorizedProjectsWorker
.
bulk_perform_async
(
@user_ids
.
map
{
|
id
|
[
id
]
})
end
end
app/workers/authorized_projects_worker.rb
0 → 100644
View file @
fd05e266
class
AuthorizedProjectsWorker
include
Sidekiq
::
Worker
include
DedicatedSidekiqQueue
def
self
.
bulk_perform_async
(
args_list
)
Sidekiq
::
Client
.
push_bulk
(
'class'
=>
self
,
'args'
=>
args_list
)
end
def
perform
(
user_id
)
user
=
User
.
find_by
(
id:
user_id
)
return
unless
user
user
.
refresh_authorized_projects
end
end
changelogs/unreleased/feature-precalculate-authorized-projects.yml
0 → 100644
View file @
fd05e266
---
title
:
Precalculate user's authorized projects in database
merge_request
:
6839
author
:
config/sidekiq_queues.yml
View file @
fd05e266
...
@@ -35,6 +35,7 @@
...
@@ -35,6 +35,7 @@
- [clear_database_cache, 1]
- [clear_database_cache, 1]
- [delete_user, 1]
- [delete_user, 1]
- [delete_merged_branches, 1]
- [delete_merged_branches, 1]
- [authorized_projects, 1]
- [expire_build_instance_artifacts, 1]
- [expire_build_instance_artifacts, 1]
- [group_destroy, 1]
- [group_destroy, 1]
- [irker, 1]
- [irker, 1]
...
...
db/fixtures/development/04_project.rb
View file @
fd05e266
require
'sidekiq/testing'
require
'sidekiq/testing'
require
'./db/fixtures/support/serialized_transaction'
Sidekiq
::
Testing
.
inline!
do
Sidekiq
::
Testing
.
inline!
do
Gitlab
::
Seeder
.
quiet
do
Gitlab
::
Seeder
.
quiet
do
...
...
db/fixtures/development/17_cycle_analytics.rb
View file @
fd05e266
require
'sidekiq/testing'
require
'sidekiq/testing'
require
'./spec/support/test_env'
require
'./spec/support/test_env'
require
'./db/fixtures/support/serialized_transaction'
class
Gitlab::Seeder::CycleAnalytics
class
Gitlab::Seeder::CycleAnalytics
def
initialize
(
project
,
perf:
false
)
def
initialize
(
project
,
perf:
false
)
...
...
db/fixtures/support/serialized_transaction.rb
0 → 100644
View file @
fd05e266
require
'gitlab/database'
module
Gitlab
module
Database
def
self
.
serialized_transaction
connection
.
transaction
{
yield
}
end
end
end
db/migrate/20161010142410_create_project_authorizations.rb
0 → 100644
View file @
fd05e266
class
CreateProjectAuthorizations
<
ActiveRecord
::
Migration
include
Gitlab
::
Database
::
MigrationHelpers
DOWNTIME
=
false
def
change
create_table
:project_authorizations
do
|
t
|
t
.
references
:user
,
foreign_key:
{
on_delete: :cascade
}
t
.
references
:project
,
foreign_key:
{
on_delete: :cascade
}
t
.
integer
:access_level
t
.
index
[
:user_id
,
:project_id
,
:access_level
],
unique:
true
,
name:
'index_project_authorizations_on_user_id_project_id_access_level'
end
end
end
db/migrate/20161017091941_add_authorized_projects_populated_to_users.rb
0 → 100644
View file @
fd05e266
class
AddAuthorizedProjectsPopulatedToUsers
<
ActiveRecord
::
Migration
include
Gitlab
::
Database
::
MigrationHelpers
DOWNTIME
=
false
def
change
add_column
:users
,
:authorized_projects_populated
,
:boolean
end
end
db/schema.rb
View file @
fd05e266
...
@@ -844,6 +844,14 @@ ActiveRecord::Schema.define(version: 20161109150329) do
...
@@ -844,6 +844,14 @@ ActiveRecord::Schema.define(version: 20161109150329) do
add_index
"personal_access_tokens"
,
[
"token"
],
name:
"index_personal_access_tokens_on_token"
,
unique:
true
,
using: :btree
add_index
"personal_access_tokens"
,
[
"token"
],
name:
"index_personal_access_tokens_on_token"
,
unique:
true
,
using: :btree
add_index
"personal_access_tokens"
,
[
"user_id"
],
name:
"index_personal_access_tokens_on_user_id"
,
using: :btree
add_index
"personal_access_tokens"
,
[
"user_id"
],
name:
"index_personal_access_tokens_on_user_id"
,
using: :btree
create_table
"project_authorizations"
,
force: :cascade
do
|
t
|
t
.
integer
"user_id"
t
.
integer
"project_id"
t
.
integer
"access_level"
end
add_index
"project_authorizations"
,
[
"user_id"
,
"project_id"
,
"access_level"
],
name:
"index_project_authorizations_on_user_id_project_id_access_level"
,
unique:
true
,
using: :btree
create_table
"project_features"
,
force: :cascade
do
|
t
|
create_table
"project_features"
,
force: :cascade
do
|
t
|
t
.
integer
"project_id"
t
.
integer
"project_id"
t
.
integer
"merge_requests_access_level"
t
.
integer
"merge_requests_access_level"
...
@@ -1187,6 +1195,7 @@ ActiveRecord::Schema.define(version: 20161109150329) do
...
@@ -1187,6 +1195,7 @@ ActiveRecord::Schema.define(version: 20161109150329) do
t
.
boolean
"external"
,
default:
false
t
.
boolean
"external"
,
default:
false
t
.
string
"organization"
t
.
string
"organization"
t
.
string
"incoming_email_token"
t
.
string
"incoming_email_token"
t
.
boolean
"authorized_projects_populated"
end
end
add_index
"users"
,
[
"admin"
],
name:
"index_users_on_admin"
,
using: :btree
add_index
"users"
,
[
"admin"
],
name:
"index_users_on_admin"
,
using: :btree
...
@@ -1248,6 +1257,8 @@ ActiveRecord::Schema.define(version: 20161109150329) do
...
@@ -1248,6 +1257,8 @@ ActiveRecord::Schema.define(version: 20161109150329) do
add_foreign_key
"merge_requests_closing_issues"
,
"issues"
,
on_delete: :cascade
add_foreign_key
"merge_requests_closing_issues"
,
"issues"
,
on_delete: :cascade
add_foreign_key
"merge_requests_closing_issues"
,
"merge_requests"
,
on_delete: :cascade
add_foreign_key
"merge_requests_closing_issues"
,
"merge_requests"
,
on_delete: :cascade
add_foreign_key
"personal_access_tokens"
,
"users"
add_foreign_key
"personal_access_tokens"
,
"users"
add_foreign_key
"project_authorizations"
,
"projects"
,
on_delete: :cascade
add_foreign_key
"project_authorizations"
,
"users"
,
on_delete: :cascade
add_foreign_key
"protected_branch_merge_access_levels"
,
"protected_branches"
add_foreign_key
"protected_branch_merge_access_levels"
,
"protected_branches"
add_foreign_key
"protected_branch_push_access_levels"
,
"protected_branches"
add_foreign_key
"protected_branch_push_access_levels"
,
"protected_branches"
add_foreign_key
"trending_projects"
,
"projects"
,
on_delete: :cascade
add_foreign_key
"trending_projects"
,
"projects"
,
on_delete: :cascade
...
...
lib/gitlab/database.rb
View file @
fd05e266
...
@@ -35,6 +35,13 @@ module Gitlab
...
@@ -35,6 +35,13 @@ module Gitlab
order
order
end
end
def
self
.
serialized_transaction
opts
=
{}
opts
[
:isolation
]
=
:serializable
unless
Rails
.
env
.
test?
&&
connection
.
transaction_open?
connection
.
transaction
(
opts
)
{
yield
}
end
def
self
.
random
def
self
.
random
Gitlab
::
Database
.
postgresql?
?
"RANDOM()"
:
"RAND()"
Gitlab
::
Database
.
postgresql?
?
"RANDOM()"
:
"RAND()"
end
end
...
...
spec/finders/group_projects_finder_spec.rb
View file @
fd05e266
...
@@ -38,7 +38,10 @@ describe GroupProjectsFinder do
...
@@ -38,7 +38,10 @@ describe GroupProjectsFinder do
end
end
describe
'without group member current_user'
do
describe
'without group member current_user'
do
before
{
shared_project_2
.
team
<<
[
current_user
,
Gitlab
::
Access
::
MASTER
]
}
before
do
shared_project_2
.
team
<<
[
current_user
,
Gitlab
::
Access
::
MASTER
]
current_user
.
reload
end
context
"only shared"
do
context
"only shared"
do
context
"without external user"
do
context
"without external user"
do
...
...
spec/models/member_spec.rb
View file @
fd05e266
...
@@ -443,6 +443,16 @@ describe Member, models: true do
...
@@ -443,6 +443,16 @@ describe Member, models: true do
member
.
accept_invite!
(
user
)
member
.
accept_invite!
(
user
)
end
end
it
"refreshes user's authorized projects"
,
truncate:
true
do
project
=
member
.
source
expect
(
user
.
authorized_projects
).
not_to
include
(
project
)
member
.
accept_invite!
(
user
)
expect
(
user
.
authorized_projects
.
reload
).
to
include
(
project
)
end
end
end
describe
"#decline_invite!"
do
describe
"#decline_invite!"
do
...
@@ -468,4 +478,16 @@ describe Member, models: true do
...
@@ -468,4 +478,16 @@ describe Member, models: true do
expect
{
member
.
generate_invite_token
}.
to
change
{
member
.
invite_token
}
expect
{
member
.
generate_invite_token
}.
to
change
{
member
.
invite_token
}
end
end
end
end
describe
"destroying a record"
,
truncate:
true
do
it
"refreshes user's authorized projects"
do
project
=
create
(
:project
,
:private
)
user
=
create
(
:user
)
member
=
project
.
team
<<
[
user
,
:reporter
]
member
.
destroy
expect
(
user
.
authorized_projects
).
not_to
include
(
project
)
end
end
end
end
spec/models/project_group_link_spec.rb
View file @
fd05e266
...
@@ -14,4 +14,20 @@ describe ProjectGroupLink do
...
@@ -14,4 +14,20 @@ describe ProjectGroupLink do
it
{
should
validate_presence_of
(
:group
)
}
it
{
should
validate_presence_of
(
:group
)
}
it
{
should
validate_presence_of
(
:group_access
)
}
it
{
should
validate_presence_of
(
:group_access
)
}
end
end
describe
"destroying a record"
,
truncate:
true
do
it
"refreshes group users' authorized projects"
do
project
=
create
(
:project
,
:private
)
group
=
create
(
:group
)
reporter
=
create
(
:user
)
group_users
=
group
.
users
group
.
add_reporter
(
reporter
)
project
.
project_group_links
.
create
(
group:
group
)
group_users
.
each
{
|
user
|
expect
(
user
.
authorized_projects
).
to
include
(
project
)
}
project
.
project_group_links
.
destroy_all
group_users
.
each
{
|
user
|
expect
(
user
.
authorized_projects
).
not_to
include
(
project
)
}
end
end
end
end
spec/models/project_spec.rb
View file @
fd05e266
...
@@ -1514,7 +1514,7 @@ describe Project, models: true do
...
@@ -1514,7 +1514,7 @@ describe Project, models: true do
members_project
.
team
<<
[
developer
,
:developer
]
members_project
.
team
<<
[
developer
,
:developer
]
members_project
.
team
<<
[
master
,
:master
]
members_project
.
team
<<
[
master
,
:master
]
create
(
:project_group_link
,
project:
shared_project
,
group:
group
)
create
(
:project_group_link
,
project:
shared_project
,
group:
group
,
group_access:
Gitlab
::
Access
::
DEVELOPER
)
end
end
it
'returns false for no user'
do
it
'returns false for no user'
do
...
@@ -1543,7 +1543,9 @@ describe Project, models: true do
...
@@ -1543,7 +1543,9 @@ describe Project, models: true do
expect
(
members_project
.
authorized_for_user?
(
developer
,
Gitlab
::
Access
::
MASTER
)).
to
be
(
false
)
expect
(
members_project
.
authorized_for_user?
(
developer
,
Gitlab
::
Access
::
MASTER
)).
to
be
(
false
)
expect
(
members_project
.
authorized_for_user?
(
master
,
Gitlab
::
Access
::
MASTER
)).
to
be
(
true
)
expect
(
members_project
.
authorized_for_user?
(
master
,
Gitlab
::
Access
::
MASTER
)).
to
be
(
true
)
expect
(
shared_project
.
authorized_for_user?
(
developer
,
Gitlab
::
Access
::
MASTER
)).
to
be
(
false
)
expect
(
shared_project
.
authorized_for_user?
(
developer
,
Gitlab
::
Access
::
MASTER
)).
to
be
(
false
)
expect
(
shared_project
.
authorized_for_user?
(
master
,
Gitlab
::
Access
::
MASTER
)).
to
be
(
true
)
expect
(
shared_project
.
authorized_for_user?
(
master
,
Gitlab
::
Access
::
MASTER
)).
to
be
(
false
)
expect
(
shared_project
.
authorized_for_user?
(
developer
,
Gitlab
::
Access
::
DEVELOPER
)).
to
be
(
true
)
expect
(
shared_project
.
authorized_for_user?
(
master
,
Gitlab
::
Access
::
DEVELOPER
)).
to
be
(
true
)
end
end
end
end
...
...
spec/models/user_spec.rb
View file @
fd05e266
...
@@ -1072,7 +1072,7 @@ describe User, models: true do
...
@@ -1072,7 +1072,7 @@ describe User, models: true do
it
{
is_expected
.
to
eq
([
private_group
])
}
it
{
is_expected
.
to
eq
([
private_group
])
}
end
end
describe
'#authorized_projects'
do
describe
'#authorized_projects'
,
truncate:
true
do
context
'with a minimum access level'
do
context
'with a minimum access level'
do
it
'includes projects for which the user is an owner'
do
it
'includes projects for which the user is an owner'
do
user
=
create
(
:user
)
user
=
create
(
:user
)
...
@@ -1092,6 +1092,80 @@ describe User, models: true do
...
@@ -1092,6 +1092,80 @@ describe User, models: true do
.
to
contain_exactly
(
project
)
.
to
contain_exactly
(
project
)
end
end
end
end
it
"includes user's personal projects"
do
user
=
create
(
:user
)
project
=
create
(
:project
,
:private
,
namespace:
user
.
namespace
)
expect
(
user
.
authorized_projects
).
to
include
(
project
)
end
it
"includes personal projects user has been given access to"
do
user1
=
create
(
:user
)
user2
=
create
(
:user
)
project
=
create
(
:project
,
:private
,
namespace:
user1
.
namespace
)
project
.
team
<<
[
user2
,
Gitlab
::
Access
::
DEVELOPER
]
expect
(
user2
.
authorized_projects
).
to
include
(
project
)
end
it
"includes projects of groups user has been added to"
do
group
=
create
(
:group
)
project
=
create
(
:project
,
group:
group
)
user
=
create
(
:user
)
group
.
add_developer
(
user
)
expect
(
user
.
authorized_projects
).
to
include
(
project
)
end
it
"does not include projects of groups user has been removed from"
do
group
=
create
(
:group
)
project
=
create
(
:project
,
group:
group
)
user
=
create
(
:user
)
member
=
group
.
add_developer
(
user
)
expect
(
user
.
authorized_projects
).
to
include
(
project
)
member
.
destroy
expect
(
user
.
authorized_projects
).
not_to
include
(
project
)
end
it
"includes projects shared with user's group"
do
user
=
create
(
:user
)
project
=
create
(
:project
,
:private
)
group
=
create
(
:group
)
group
.
add_reporter
(
user
)
project
.
project_group_links
.
create
(
group:
group
)
expect
(
user
.
authorized_projects
).
to
include
(
project
)
end
it
"does not include destroyed projects user had access to"
do
user1
=
create
(
:user
)
user2
=
create
(
:user
)
project
=
create
(
:project
,
:private
,
namespace:
user1
.
namespace
)
project
.
team
<<
[
user2
,
Gitlab
::
Access
::
DEVELOPER
]
expect
(
user2
.
authorized_projects
).
to
include
(
project
)
project
.
destroy
expect
(
user2
.
authorized_projects
).
not_to
include
(
project
)
end
it
"does not include projects of destroyed groups user had access to"
do
group
=
create
(
:group
)
project
=
create
(
:project
,
namespace:
group
)
user
=
create
(
:user
)
group
.
add_developer
(
user
)
expect
(
user
.
authorized_projects
).
to
include
(
project
)
group
.
destroy
expect
(
user
.
authorized_projects
).
not_to
include
(
project
)
end
end
end
describe
'#projects_where_can_admin_issues'
do
describe
'#projects_where_can_admin_issues'
do
...
...
spec/services/projects/create_service_spec.rb
View file @
fd05e266
...
@@ -34,6 +34,8 @@ describe Projects::CreateService, services: true do
...
@@ -34,6 +34,8 @@ describe Projects::CreateService, services: true do
@group
=
create
:group
@group
=
create
:group
@group
.
add_owner
(
@user
)
@group
.
add_owner
(
@user
)
@user
.
refresh_authorized_projects
# Ensure cache is warm
@opts
.
merge!
(
namespace_id:
@group
.
id
)
@opts
.
merge!
(
namespace_id:
@group
.
id
)
@project
=
create_project
(
@user
,
@opts
)
@project
=
create_project
(
@user
,
@opts
)
end
end
...
@@ -41,6 +43,7 @@ describe Projects::CreateService, services: true do
...
@@ -41,6 +43,7 @@ describe Projects::CreateService, services: true do
it
{
expect
(
@project
).
to
be_valid
}
it
{
expect
(
@project
).
to
be_valid
}
it
{
expect
(
@project
.
owner
).
to
eq
(
@group
)
}
it
{
expect
(
@project
.
owner
).
to
eq
(
@group
)
}
it
{
expect
(
@project
.
namespace
).
to
eq
(
@group
)
}
it
{
expect
(
@project
.
namespace
).
to
eq
(
@group
)
}
it
{
expect
(
@user
.
authorized_projects
).
to
include
(
@project
)
}
end
end
context
'error handling'
do
context
'error handling'
do
...
...
spec/support/db_cleaner.rb
View file @
fd05e266
...
@@ -11,6 +11,10 @@ RSpec.configure do |config|
...
@@ -11,6 +11,10 @@ RSpec.configure do |config|
DatabaseCleaner
.
strategy
=
:truncation
DatabaseCleaner
.
strategy
=
:truncation
end
end
config
.
before
(
:each
,
truncate:
true
)
do
DatabaseCleaner
.
strategy
=
:truncation
end
config
.
before
(
:each
)
do
config
.
before
(
:each
)
do
DatabaseCleaner
.
start
DatabaseCleaner
.
start
end
end
...
...
spec/workers/authorized_projects_worker_spec.rb
0 → 100644
View file @
fd05e266
require
'spec_helper'
describe
AuthorizedProjectsWorker
do
describe
'#perform'
do
it
"refreshes user's authorized projects"
do
user
=
create
(
:user
)
expect
(
User
).
to
receive
(
:find_by
).
with
(
id:
user
.
id
).
and_return
(
user
)
expect
(
user
).
to
receive
(
:refresh_authorized_projects
)
described_class
.
new
.
perform
(
user
.
id
)
end
context
"when user is not found"
do
it
"does nothing"
do
expect_any_instance_of
(
User
).
not_to
receive
(
:refresh_authorized_projects
)
described_class
.
new
.
perform
(
999_999
)
end
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment