Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-shell
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
nexedi
gitlab-shell
Commits
dcc20876
Commit
dcc20876
authored
Aug 20, 2016
by
Drew Blessing
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add option to recover 2FA via SSH
parent
3043b31c
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
224 additions
and
12 deletions
+224
-12
CHANGELOG
CHANGELOG
+3
-0
lib/gitlab_net.rb
lib/gitlab_net.rb
+9
-0
lib/gitlab_shell.rb
lib/gitlab_shell.rb
+47
-9
spec/gitlab_net_spec.rb
spec/gitlab_net_spec.rb
+18
-0
spec/gitlab_shell_spec.rb
spec/gitlab_shell_spec.rb
+63
-3
spec/vcr_cassettes/two-factor-recovery-codes-fail.yml
spec/vcr_cassettes/two-factor-recovery-codes-fail.yml
+42
-0
spec/vcr_cassettes/two-factor-recovery-codes.yml
spec/vcr_cassettes/two-factor-recovery-codes.yml
+42
-0
No files found.
CHANGELOG
View file @
dcc20876
v3.5.0
- Add option to recover 2FA via SSH
v3.4.0
- Redis Sentinel support
...
...
lib/gitlab_net.rb
View file @
dcc20876
...
...
@@ -72,6 +72,15 @@ class GitlabNet
nil
end
def
two_factor_recovery_codes
(
key
)
key_id
=
key
.
gsub
(
'key-'
,
''
)
resp
=
post
(
"
#{
host
}
/two_factor_recovery_codes"
,
key_id:
key_id
)
JSON
.
parse
(
resp
.
body
)
if
resp
.
code
==
'200'
rescue
{}
end
def
redis_client
redis_config
=
config
.
redis
database
=
redis_config
[
'database'
]
||
0
...
...
lib/gitlab_shell.rb
View file @
dcc20876
...
...
@@ -8,9 +8,10 @@ class GitlabShell
class
InvalidRepositoryPathError
<
StandardError
;
end
GIT_COMMANDS
=
%w(git-upload-pack git-receive-pack git-upload-archive git-annex-shell git-lfs-authenticate)
.
freeze
API_COMMANDS
=
%w(2fa_recovery_codes)
GL_PROTOCOL
=
'ssh'
.
freeze
attr_accessor
:key_id
,
:repo_name
,
:
git_cm
d
attr_accessor
:key_id
,
:repo_name
,
:
comman
d
attr_reader
:repo_path
def
initialize
(
key_id
)
...
...
@@ -30,7 +31,7 @@ class GitlabShell
args
=
Shellwords
.
shellwords
(
origin_cmd
)
parse_cmd
(
args
)
verify_access
verify_access
if
GIT_COMMANDS
.
include?
(
args
.
first
)
process_cmd
(
args
)
...
...
@@ -58,12 +59,14 @@ class GitlabShell
protected
def
parse_cmd
(
args
)
@
git_cm
d
=
args
.
first
@git_access
=
@
git_cm
d
@
comman
d
=
args
.
first
@git_access
=
@
comman
d
r
aise
DisallowedCommandError
unless
GIT_COMMANDS
.
include?
(
@git_cm
d
)
r
eturn
if
API_COMMANDS
.
include?
(
@comman
d
)
case
@git_cmd
raise
DisallowedCommandError
unless
GIT_COMMANDS
.
include?
(
@command
)
case
@command
when
'git-annex-shell'
raise
DisallowedCommandError
unless
@config
.
git_annex_enabled?
...
...
@@ -94,7 +97,9 @@ class GitlabShell
end
def
process_cmd
(
args
)
if
@git_cmd
==
'git-annex-shell'
return
self
.
send
(
"api_
#{
@command
}
"
)
if
API_COMMANDS
.
include?
(
@command
)
if
@command
==
'git-annex-shell'
raise
DisallowedCommandError
unless
@config
.
git_annex_enabled?
# Make sure repository has git-annex enabled
...
...
@@ -113,8 +118,8 @@ class GitlabShell
$logger
.
info
"gitlab-shell: executing git-annex command <
#{
parsed_args
.
join
(
' '
)
}
> for
#{
log_username
}
."
exec_cmd
(
*
parsed_args
)
else
$logger
.
info
"gitlab-shell: executing git command <
#{
@
git_cm
d
}
#{
repo_path
}
> for
#{
log_username
}
."
exec_cmd
(
@
git_cm
d
,
repo_path
)
$logger
.
info
"gitlab-shell: executing git command <
#{
@
comman
d
}
#{
repo_path
}
> for
#{
log_username
}
."
exec_cmd
(
@
comman
d
,
repo_path
)
end
end
...
...
@@ -181,6 +186,39 @@ class GitlabShell
private
def
continue?
(
question
)
puts
"
#{
question
}
(yes/no)"
STDOUT
.
flush
# Make sure the question gets output before we wait for input
continue
=
STDIN
.
gets
.
chomp
puts
''
# Add a buffer in the output
continue
==
'yes'
end
def
api_2fa_recovery_codes
continue
=
continue?
(
"Are you sure you want to generate new two-factor recovery codes?
\n
"
\
"Any existing recovery codes you saved will be invalidated."
)
unless
continue
puts
'New recovery codes have *not* been generated. Existing codes will remain valid.'
return
end
resp
=
api
.
two_factor_recovery_codes
(
key_id
)
if
resp
[
'success'
]
codes
=
resp
[
'recovery_codes'
].
join
(
"
\n
"
)
puts
"Your two-factor authentication recovery codes are:
\n\n
"
\
"
#{
codes
}
\n\n
"
\
"During sign in, use one of the codes above when prompted for
\n
"
\
"your two-factor code. Then, visit your Profile Settings and add
\n
"
\
"a new device so you do not lose access to your account again."
else
puts
"An error occurred while trying to generate new recovery codes.
\n
"
\
"
#{
resp
[
'message'
]
}
"
end
end
def
repo_path
=
(
repo_path
)
raise
ArgumentError
,
"Repository path not provided. Please make sure you're using GitLab v8.10 or later."
unless
repo_path
raise
InvalidRepositoryPathError
if
File
.
absolute_path
(
repo_path
)
!=
repo_path
...
...
spec/gitlab_net_spec.rb
View file @
dcc20876
...
...
@@ -106,6 +106,24 @@ describe GitlabNet, vcr: true do
end
end
describe
'#two_factor_recovery_codes'
do
it
'returns two factor recovery codes'
do
VCR
.
use_cassette
(
'two-factor-recovery-codes'
)
do
result
=
gitlab_net
.
two_factor_recovery_codes
(
'key-1'
)
expect
(
result
[
'success'
]).
to
be_true
expect
(
result
[
'recovery_codes'
]).
to
eq
([
'f67c514de60c4953'
,
'41278385fc00c1e0'
])
end
end
it
'returns false when recovery codes cannot be generated'
do
VCR
.
use_cassette
(
'two-factor-recovery-codes-fail'
)
do
result
=
gitlab_net
.
two_factor_recovery_codes
(
'key-1'
)
expect
(
result
[
'success'
]).
to
be_false
expect
(
result
[
'message'
]).
to
eq
(
'Could not find the given key'
)
end
end
end
describe
:check_access
do
context
'ssh key with access to project'
do
it
'should allow pull access for dev.gitlab.org'
do
...
...
spec/gitlab_shell_spec.rb
View file @
dcc20876
...
...
@@ -23,6 +23,10 @@ describe GitlabShell do
double
(
GitlabNet
).
tap
do
|
api
|
api
.
stub
(
discover:
{
'name'
=>
'John Doe'
})
api
.
stub
(
check_access:
GitAccessStatus
.
new
(
true
,
'ok'
,
repo_path
))
api
.
stub
(
two_factor_recovery_codes:
{
'success'
=>
true
,
'recovery_codes'
=>
[
'f67c514de60c4953'
,
'41278385fc00c1e0'
]
})
end
end
...
...
@@ -53,7 +57,7 @@ describe GitlabShell do
end
its
(
:repo_name
)
{
should
==
'gitlab-ci.git'
}
its
(
:
git_cm
d
)
{
should
==
'git-upload-pack'
}
its
(
:
comman
d
)
{
should
==
'git-upload-pack'
}
end
context
'namespace'
do
...
...
@@ -65,7 +69,7 @@ describe GitlabShell do
end
its
(
:repo_name
)
{
should
==
'dmitriy.zaporozhets/gitlab-ci.git'
}
its
(
:
git_cm
d
)
{
should
==
'git-upload-pack'
}
its
(
:
comman
d
)
{
should
==
'git-upload-pack'
}
end
context
'with an invalid number of arguments'
do
...
...
@@ -75,6 +79,24 @@ describe GitlabShell do
expect
{
subject
.
send
:parse_cmd
,
ssh_args
}.
to
raise_error
(
GitlabShell
::
DisallowedCommandError
)
end
end
context
'with an API command'
do
before
do
subject
.
send
:parse_cmd
,
ssh_args
end
context
'when generating recovery codes'
do
let
(
:ssh_args
)
{
%w(2fa_recovery_codes)
}
it
'sets the correct command'
do
expect
(
subject
.
command
).
to
eq
(
'2fa_recovery_codes'
)
end
it
'does not set repo name'
do
expect
(
subject
.
repo_name
).
to
be_nil
end
end
end
end
describe
'git-annex'
do
...
...
@@ -88,7 +110,7 @@ describe GitlabShell do
end
its
(
:repo_name
)
{
should
==
'dzaporozhets/gitlab.git'
}
its
(
:
git_cm
d
)
{
should
==
'git-annex-shell'
}
its
(
:
comman
d
)
{
should
==
'git-annex-shell'
}
end
end
...
...
@@ -233,6 +255,44 @@ describe GitlabShell do
end
end
end
context
'with an API command'
do
before
do
allow
(
subject
).
to
receive
(
:continue?
).
and_return
(
true
)
end
context
'when generating recovery codes'
do
let
(
:ssh_cmd
)
{
'2fa_recovery_codes'
}
after
do
subject
.
exec
(
ssh_cmd
)
end
it
'does not call verify_access'
do
expect
(
subject
).
not_to
receive
(
:verify_access
)
end
it
'calls the corresponding method'
do
expect
(
subject
).
to
receive
(
:api_2fa_recovery_codes
)
end
it
'outputs recovery codes'
do
expect
(
$stdout
).
to
receive
(
:puts
)
.
with
(
/f67c514de60c4953\n41278385fc00c1e0/
)
end
context
'when the process is unsuccessful'
do
it
'displays the error to the user'
do
api
.
stub
(
two_factor_recovery_codes:
{
'success'
=>
false
,
'message'
=>
'Could not find the given key'
})
expect
(
$stdout
).
to
receive
(
:puts
)
.
with
(
/Could not find the given key/
)
end
end
end
end
end
describe
:validate_access
do
...
...
spec/vcr_cassettes/two-factor-recovery-codes-fail.yml
0 → 100644
View file @
dcc20876
---
http_interactions
:
-
request
:
method
:
post
uri
:
https://dev.gitlab.org/api/v3/internal/two_factor_recovery_codes
body
:
encoding
:
US-ASCII
string
:
username=user-1&secret_token=a123
headers
:
Accept-Encoding
:
-
gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept
:
-
"
*/*"
User-Agent
:
-
Ruby
Content-Type
:
-
application/x-www-form-urlencoded
response
:
status
:
code
:
200
message
:
OK
headers
:
Server
:
-
nginx
Date
:
-
Tue, 16 Aug 2016 22:10:11 GMT
Content-Type
:
-
application/json
Connection
:
-
keep-alive
Status
:
-
200 OK
X-Request-Id
:
-
4467029d-51c6-41bc-af5f-6da279dbb238
X-Runtime
:
-
'
0.004589'
body
:
encoding
:
UTF-8
string
:
'
{
"success":
false,
"message":
"Could
not
find
the
given
key"
}'
http_version
:
recorded_at
:
Tue, 16 Aug 2016 22:10:11 GMT
recorded_with
:
VCR 2.4.0
spec/vcr_cassettes/two-factor-recovery-codes.yml
0 → 100644
View file @
dcc20876
---
http_interactions
:
-
request
:
method
:
post
uri
:
https://dev.gitlab.org/api/v3/internal/two_factor_recovery_codes
body
:
encoding
:
US-ASCII
string
:
username=user-1&secret_token=a123
headers
:
Accept-Encoding
:
-
gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept
:
-
"
*/*"
User-Agent
:
-
Ruby
Content-Type
:
-
application/x-www-form-urlencoded
response
:
status
:
code
:
200
message
:
OK
headers
:
Server
:
-
nginx
Date
:
-
Tue, 16 Aug 2016 22:10:11 GMT
Content-Type
:
-
application/json
Connection
:
-
keep-alive
Status
:
-
200 OK
X-Request-Id
:
-
4467029d-51c6-41bc-af5f-6da279dbb238
X-Runtime
:
-
'
0.004589'
body
:
encoding
:
UTF-8
string
:
'
{
"success":
true,
"recovery_codes":
["f67c514de60c4953","41278385fc00c1e0"]
}'
http_version
:
recorded_at
:
Tue, 16 Aug 2016 22:10:11 GMT
recorded_with
:
VCR 2.4.0
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