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
Léo-Paul Géneau
gitlab-ce
Commits
17c22156
Commit
17c22156
authored
8 years ago
by
David Alexander
Committed by
Rémy Coutable
8 years ago
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Initial implementation of user access request to projects
parent
0c0ef7df
Changes
19
Show whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
248 additions
and
14 deletions
+248
-14
app/controllers/projects/project_members_controller.rb
app/controllers/projects/project_members_controller.rb
+29
-2
app/helpers/projects_helper.rb
app/helpers/projects_helper.rb
+9
-3
app/mailers/emails/projects.rb
app/mailers/emails/projects.rb
+42
-0
app/models/ability.rb
app/models/ability.rb
+1
-1
app/models/member.rb
app/models/member.rb
+55
-5
app/models/members/project_member.rb
app/models/members/project_member.rb
+18
-0
app/models/project_team.rb
app/models/project_team.rb
+6
-0
app/services/notification_service.rb
app/services/notification_service.rb
+12
-0
app/views/layouts/nav/_project.html.haml
app/views/layouts/nav/_project.html.haml
+18
-0
app/views/notify/project_request_access_accepted_email.html.haml
...ws/notify/project_request_access_accepted_email.html.haml
+4
-0
app/views/notify/project_request_access_accepted_email.text.erb
...ews/notify/project_request_access_accepted_email.text.erb
+3
-0
app/views/notify/project_request_access_denied_email.html.haml
...iews/notify/project_request_access_denied_email.html.haml
+4
-0
app/views/notify/project_request_access_denied_email.text.erb
...views/notify/project_request_access_denied_email.text.erb
+3
-0
app/views/projects/project_members/_pending.html.haml
app/views/projects/project_members/_pending.html.haml
+21
-0
app/views/projects/project_members/_project_member.html.haml
app/views/projects/project_members/_project_member.html.haml
+13
-2
app/views/projects/project_members/index.html.haml
app/views/projects/project_members/index.html.haml
+2
-1
config/routes.rb
config/routes.rb
+2
-0
db/migrate/20160314114439_add_membership_request.rb
db/migrate/20160314114439_add_membership_request.rb
+5
-0
db/schema.rb
db/schema.rb
+1
-0
No files found.
app/controllers/projects/project_members_controller.rb
View file @
17c22156
class
Projects::ProjectMembersController
<
Projects
::
ApplicationController
# Authorize
before_action
:authorize_admin_project_member!
,
except:
[
:
leave
,
:index
]
before_action
:authorize_admin_project_member!
,
except:
[
:
index
,
:leave
,
:request_access
]
def
index
@project_members
=
@project
.
project_members
@project_members
=
@project_members
.
non_
invite
unless
can?
(
current_user
,
:admin_project
,
@project
)
@project_members
=
@project_members
.
non_
pending
unless
can?
(
current_user
,
:admin_project
,
@project
)
if
params
[
:search
].
present?
users
=
@project
.
users
.
search
(
params
[
:search
]).
to_a
...
...
@@ -93,6 +93,33 @@ class Projects::ProjectMembersController < Projects::ApplicationController
end
end
def
request_access
redirect_path
=
namespace_project_path
(
@project
.
namespace
,
@project
)
# current_user
# @project
@project_member
=
ProjectMember
.
new
(
source:
@project
,
access_level:
ProjectMember
::
DEVELOPER
,
user_id:
current_user
.
id
,
created_by_id:
current_user
.
id
,
requested:
true
)
@project_member
.
save!
redirect_to
redirect_path
,
notice:
'Your request for access has been queued for review.'
end
def
approval
@project_member
=
@project
.
project_members
.
find
(
params
[
:id
])
return
render_403
unless
can?
(
current_user
,
:update_project_member
,
@project_member
)
@project_member
.
requested
=
nil
@project_member
.
save!
respond_to
do
|
format
|
format
.
html
do
redirect_to
namespace_project_project_members_path
(
@project
.
namespace
,
@project
)
end
format
.
js
{
render
nothing:
true
}
end
end
def
apply_import
source_project
=
Project
.
find
(
params
[
:source_project_id
])
...
...
This diff is collapsed.
Click to expand it.
app/helpers/projects_helper.rb
View file @
17c22156
module
ProjectsHelper
def
remove_from_project_team_message
(
project
,
member
)
if
member
.
user
"You are going to remove
#{
member
.
user
.
name
}
from
#{
project
.
name
}
project team. Are you sure?"
else
if
!
member
.
user
"You are going to revoke the invitation for
#{
member
.
invite_email
}
to join
#{
project
.
name
}
project team. Are you sure?"
elsif
member
.
request?
"You are going to deny
#{
member
.
user
.
name
}
's request to join
#{
project
.
name
}
project team. Are you sure?"
else
"You are going to remove
#{
member
.
user
.
name
}
from
#{
project
.
name
}
project team. Are you sure?"
end
end
def
approve_for_project_team_message
(
project
,
member
)
"You are going to approve
#{
member
.
user
.
name
}
's request for
#{
member
.
human_access
}
access to the
#{
project
.
name
}
project team. Are you sure?"
end
def
link_to_project
(
project
)
...
...
This diff is collapsed.
Click to expand it.
app/mailers/emails/projects.rb
View file @
17c22156
...
...
@@ -11,6 +11,48 @@ module Emails
subject:
subject
(
"Access to project was granted"
))
end
def
project_member_requested_access
(
project_member_id
)
@project_member
=
ProjectMember
.
find
project_member_id
@project
=
@project_member
.
project
@target_url
=
namespace_project_url
(
@project
.
namespace
,
@project
)
project_admins
=
ProjectMember
.
in_project
(
@project
)
.
where
(
access_level:
[
Gitlab
::
Access
::
OWNER
,
Gitlab
::
Access
::
MASTER
])
.
pluck
(
:notification_email
)
project_admins
.
each
do
|
address
|
mail
(
to:
address
,
subject:
subject
(
"Request to join project:
#{
@project
.
name_with_namespace
}
"
))
end
end
def
project_request_access_accepted_email
(
project_member_id
)
@project_member
=
ProjectMember
.
find
project_member_id
return
if
@project_member
.
created_by
.
nil?
@project
=
@project_member
.
project
@target_url
=
namespace_project_url
(
@project
.
namespace
,
@project
)
@current_user
=
@project_member
.
created_by
mail
(
to:
@project_member
.
created_by
.
notification_email
,
subject:
subject
(
'Request for access granted'
))
end
def
project_request_access_declined_email
(
project_member_id
)
@project_member
=
ProjectMember
.
find
project_member_id
return
if
@project_member
.
created_by
.
nil?
@project
=
@project_member
.
project
@target_url
=
namespace_project_url
(
@project
.
namespace
,
@project
)
@current_user
=
@project_member
.
created_by
mail
(
to:
@project_member
.
created_by
.
notification_email
,
subject:
subject
(
'Request for access declined'
))
end
def
project_member_invited_email
(
project_member_id
,
token
)
@project_member
=
ProjectMember
.
find
project_member_id
@project
=
@project_member
.
project
...
...
This diff is collapsed.
Click to expand it.
app/models/ability.rb
View file @
17c22156
...
...
@@ -153,7 +153,7 @@ class Ability
RequestStore
.
store
[
key
]
||=
begin
# Push abilities on the users team role
rules
.
push
(
*
project_team_rules
(
project
.
team
,
user
))
rules
.
push
(
*
project_team_rules
(
project
.
team
,
user
))
unless
project
.
team
.
pending?
(
user
)
if
project
.
owner
==
user
||
(
project
.
group
&&
project
.
group
.
has_owner?
(
user
))
||
...
...
This diff is collapsed.
Click to expand it.
app/models/member.rb
View file @
17c22156
...
...
@@ -27,7 +27,12 @@ class Member < ActiveRecord::Base
}
scope
:invite
,
->
{
where
(
user_id:
nil
)
}
scope
:non_invite
,
->
{
where
(
"user_id IS NOT NULL"
)
}
scope
:non_invite
,
->
{
where
(
'user_id IS NOT NULL'
)
}
scope
:request
,
->
{
where
(
requested:
true
)
}
scope
:non_request
,
->
{
where
(
requested:
nil
)
}
scope
:pending
,
->
{
where
(
"user_id IS NULL OR requested"
)
}
scope
:non_pending
,
->
{
self
.
non_invite
.
non_request
}
scope
:guests
,
->
{
where
(
access_level:
GUEST
)
}
scope
:reporters
,
->
{
where
(
access_level:
REPORTER
)
}
scope
:developers
,
->
{
where
(
access_level:
DEVELOPER
)
}
...
...
@@ -35,11 +40,16 @@ class Member < ActiveRecord::Base
scope
:owners
,
->
{
where
(
access_level:
OWNER
)
}
before_validation
:generate_invite_token
,
on: :create
,
if:
->
(
member
)
{
member
.
invite_email
.
present?
}
after_create
:send_invite
,
if: :invite?
after_create
:create_notification_setting
,
unless: :invite?
after_create
:post_create_hook
,
unless: :invite?
after_update
:post_update_hook
,
unless: :invite?
after_destroy
:post_destroy_hook
,
unless: :invite?
after_create
:send_request_access
,
if: :request?
after_create
:create_notification_setting
,
unless: :pending?
after_create
:post_create_hook
,
unless: :pending?
after_update
:post_update_hook
,
unless: :pending?
after_destroy
:post_destroy_hook
,
unless: :pending?
delegate
:name
,
:username
,
:email
,
to: :user
,
prefix:
true
...
...
@@ -96,10 +106,38 @@ class Member < ActiveRecord::Base
end
end
def
pending?
request?
||
invite?
end
def
request?
self
.
requested
end
def
invite?
self
.
invite_token
.
present?
end
def
accept_request_access!
return
false
unless
request?
self
.
request
=
false
saved
=
self
.
save
after_accept_request_access
if
saved
saved
end
def
decline_request_access!
return
false
unless
request?
destroyed
=
self
.
destroy
after_decline_request_access
if
destroyed
destroyed
end
def
accept_invite!
(
new_user
)
return
false
unless
invite?
...
...
@@ -153,6 +191,10 @@ class Member < ActiveRecord::Base
private
def
send_request_access
# override in subclass
end
def
send_invite
# override in subclass
end
...
...
@@ -169,6 +211,14 @@ class Member < ActiveRecord::Base
system_hook_service
.
execute_hooks_for
(
self
,
:destroy
)
end
def
after_accept_request_access
post_create_hook
end
def
after_decline_request_access
# override in subclass
end
def
after_accept_invite
post_create_hook
end
...
...
This diff is collapsed.
Click to expand it.
app/models/members/project_member.rb
View file @
17c22156
...
...
@@ -107,6 +107,12 @@ class ProjectMember < Member
user
.
todos
.
where
(
project_id:
source_id
).
destroy_all
if
user
end
def
send_request_access
notification_service
.
request_access_project_member
(
self
)
super
end
def
send_invite
notification_service
.
invite_project_member
(
self
,
@raw_invite_token
)
...
...
@@ -136,6 +142,18 @@ class ProjectMember < Member
super
end
def
after_accept_request_access
notification_service
.
accept_project_request_access
(
self
)
super
end
def
after_decline_request_access
notification_service
.
decline_project_request_access
(
self
)
super
end
def
after_accept_invite
notification_service
.
accept_project_invite
(
self
)
...
...
This diff is collapsed.
Click to expand it.
app/models/project_team.rb
View file @
17c22156
...
...
@@ -115,6 +115,12 @@ class ProjectTeam
false
end
def
pending?
(
user
)
project
.
project_members
.
each
do
|
member
|
return
member
.
pending?
if
member
.
user_id
==
user
.
id
end
end
def
guest?
(
user
)
max_member_access
(
user
.
id
)
==
Gitlab
::
Access
::
GUEST
end
...
...
This diff is collapsed.
Click to expand it.
app/services/notification_service.rb
View file @
17c22156
...
...
@@ -173,6 +173,18 @@ class NotificationService
end
end
def
request_access_project_member
(
project_member
)
mailer
.
project_member_requested_access
(
project_member
.
id
).
deliver_later
end
def
accept_project_request_access
(
project_member
)
mailer
.
project_request_access_accepted_email
(
project_member
.
id
).
deliver_later
end
def
decline_project_request_access
(
project_member
)
mailer
.
project_request_access_declined_email
(
project_member
.
id
).
deliver_later
end
def
invite_project_member
(
project_member
,
token
)
mailer
.
project_member_invited_email
(
project_member
.
id
,
token
).
deliver_later
end
...
...
This diff is collapsed.
Click to expand it.
app/views/layouts/nav/_project.html.haml
View file @
17c22156
...
...
@@ -8,6 +8,19 @@
=
icon
(
'caret-down'
)
%ul
.dropdown-menu.dropdown-menu-align-right
=
render
'layouts/nav/project_settings'
-
if
access
%li
=
link_to
leave_namespace_project_project_members_path
(
@project
.
namespace
,
@project
),
data:
{
confirm:
leave_project_message
(
@project
)
},
method: :delete
,
title:
'Leave project'
do
Leave Project
-
else
=
link_to
request_access_namespace_project_project_members_path
(
@project
.
namespace
,
@project
),
class:
'btn btn-gray'
,
style:
'margin-left: 10px'
,
method: :post
,
title:
'Request access'
do
Request Access
%li
.divider
-
if
can_edit
%li
...
...
@@ -18,6 +31,11 @@
=
link_to
leave_namespace_project_project_members_path
(
@project
.
namespace
,
@project
),
data:
{
confirm:
leave_project_message
(
@project
)
},
method: :delete
,
title:
'Leave project'
do
Leave Project
-
else
%li
=
link_to
request_access_namespace_project_project_members_path
(
@project
.
namespace
,
@project
),
class:
'btn btn-gray'
,
style:
'margin-left: 10px'
,
method: :post
,
title:
'Request access'
do
Request Access
%div
{
class:
nav_control_class
}
%ul
.nav-links.scrolling-tabs
...
...
This diff is collapsed.
Click to expand it.
app/views/notify/project_request_access_accepted_email.html.haml
0 → 100644
View file @
17c22156
%p
Your request to join project
#{
link_to
@project
.
name_with_namespace
,
namespace_project_url
(
@project
.
namespace
,
@project
)
}
has been granted with
#{
@project_member
.
human_access
}
access.
This diff is collapsed.
Click to expand it.
app/views/notify/project_request_access_accepted_email.text.erb
0 → 100644
View file @
17c22156
Your request to join project
<%=
@project
.
name_with_namespace
%>
has been granted with
<%=
@project_member
.
human_access
%>
access.
<%=
namespace_project_url
(
@project
.
namespace
,
@project
)
%>
This diff is collapsed.
Click to expand it.
app/views/notify/project_request_access_denied_email.html.haml
0 → 100644
View file @
17c22156
%p
Your request to join project
#{
link_to
@project
.
name_with_namespace
,
namespace_project_url
(
@project
.
namespace
,
@project
)
}
has been denied.
This diff is collapsed.
Click to expand it.
app/views/notify/project_request_access_denied_email.text.erb
0 → 100644
View file @
17c22156
Your request to join project
<%=
@project
.
name_with_namespace
%>
has been denied.
<%=
namespace_project_url
(
@project
.
namespace
,
@project
)
%>
This diff is collapsed.
Click to expand it.
app/views/projects/project_members/_pending.html.haml
0 → 100644
View file @
17c22156
.panel.panel-default
.panel-heading
%strong
#{
@project
.
name
}
candidates
%small
(
#{
members
.
count
}
)
.controls
=
form_tag
namespace_project_project_members_path
(
@project
.
namespace
,
@project
),
method: :get
,
class:
'form-inline member-search-form'
do
.form-group
=
search_field_tag
:search
,
params
[
:search
],
{
placeholder:
'Find existing member by name'
,
class:
'form-control'
,
spellcheck:
false
}
=
button_tag
class:
'btn'
,
title:
'Search'
do
=
icon
(
"search"
)
%ul
.content-list
-
members
.
each
do
|
project_member
|
=
render
'project_member'
,
member:
project_member
:javascript
$
(
'
form.member-search-form
'
).
on
(
'
submit
'
,
function
(
event
)
{
event
.
preventDefault
();
Turbolinks
.
visit
(
this
.
action
+
'
?
'
+
$
(
this
).
serialize
());
});
This diff is collapsed.
Click to expand it.
app/views/projects/project_members/_project_member.html.haml
View file @
17c22156
...
...
@@ -13,6 +13,9 @@
-
if
user
.
blocked?
%label
.label.label-danger
%strong
Blocked
-
if
member
.
request?
%span
.label.label-info
Pending Approval
-
else
=
image_tag
avatar_icon
(
member
.
invite_email
,
24
),
class:
"avatar s24"
,
alt:
''
%strong
...
...
@@ -27,7 +30,6 @@
-
if
can?
(
current_user
,
:admin_project_member
,
@project
)
=
link_to
resend_invite_namespace_project_project_member_path
(
@project
.
namespace
,
@project
,
member
),
method: :post
,
class:
"btn-xs btn"
,
title:
'Resend invite'
do
Resend invite
-
if
can?
(
current_user
,
:admin_project_member
,
@project
)
.pull-right
%strong
=
member
.
human_access
...
...
@@ -35,10 +37,19 @@
=
button_tag
class:
"btn-xs btn-grouped inline btn js-toggle-button"
,
title:
'Edit access level'
,
type:
'button'
do
=
icon
(
'pencil'
)
-
if
member
.
request?
=
link_to
approval_namespace_project_project_member_path
(
@project
.
namespace
,
@project
,
member
),
class:
"btn-xs btn btn-success"
,
title:
'Grant access'
,
type:
'button'
do
%i
.fa.fa-check.fa-inverse
-
if
can?
(
current_user
,
:destroy_project_member
,
member
)
-
if
current_user
==
user
-
if
member
.
request?
=
link_to
namespace_project_project_member_path
(
@project
.
namespace
,
@project
,
member
),
data:
{
confirm:
remove_from_project_team_message
(
@project
,
member
)
},
method: :delete
,
class:
"btn-xs btn btn-remove"
,
title:
'Deny access'
do
%i
.fa.fa-times.fa-inverse
-
elsif
current_user
==
user
=
link_to
leave_namespace_project_project_members_path
(
@project
.
namespace
,
@project
),
data:
{
confirm:
leave_project_message
(
@project
)
},
method: :delete
,
class:
"btn-xs btn btn-remove"
,
title:
'Leave project'
do
=
icon
(
"sign-out"
)
Leave
...
...
This diff is collapsed.
Click to expand it.
app/views/projects/project_members/index.html.haml
View file @
17c22156
...
...
@@ -12,8 +12,9 @@
%p
.light
Users with access to this project are listed below.
=
render
"new_project_member"
=
render
"pending"
,
members:
@project_members
.
request
=
render
"team"
,
members:
@project_members
=
render
"team"
,
members:
@project_members
.
non_request
-
if
@group
=
render
"group_members"
,
members:
@group_members
...
...
This diff is collapsed.
Click to expand it.
config/routes.rb
View file @
17c22156
...
...
@@ -768,6 +768,7 @@ Rails.application.routes.draw do
resources
:project_members
,
except:
[
:new
,
:edit
],
constraints:
{
id:
/[a-zA-Z.\/0-9_\-#%+]+/
}
do
collection
do
delete
:leave
post
:request_access
# Used for import team
# from another project
...
...
@@ -777,6 +778,7 @@ Rails.application.routes.draw do
member
do
post
:resend_invite
post
:approval
end
end
...
...
This diff is collapsed.
Click to expand it.
db/migrate/20160314114439_add_membership_request.rb
0 → 100644
View file @
17c22156
class
AddMembershipRequest
<
ActiveRecord
::
Migration
def
change
add_column
:members
,
:requested
,
:boolean
end
end
This diff is collapsed.
Click to expand it.
db/schema.rb
View file @
17c22156
...
...
@@ -536,6 +536,7 @@ ActiveRecord::Schema.define(version: 20160610301627) do
t
.
string
"invite_email"
t
.
string
"invite_token"
t
.
datetime
"invite_accepted_at"
t
.
boolean
"requested"
end
add_index
"members"
,
[
"access_level"
],
name:
"index_members_on_access_level"
,
using: :btree
...
...
This diff is collapsed.
Click to expand it.
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