BigW Consortium Gitlab
Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
gitlab-ce
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Forest Godfrey
gitlab-ce
Commits
900dd7fa
Commit
900dd7fa
authored
Oct 10, 2017
by
Phil Hughes
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch '38869-u2f' into 'master'
Remove u2f from global namespace See merge request gitlab-org/gitlab-ce!14776
parents
e4e8e0fc
41b430b2
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
321 additions
and
374 deletions
+321
-374
dispatcher.js
app/assets/javascripts/dispatcher.js
+5
-2
main.js
app/assets/javascripts/main.js
+0
-6
two_factor_auth.js
app/assets/javascripts/two_factor_auth.js
+2
-1
authenticate.js
app/assets/javascripts/u2f/authenticate.js
+89
-99
error.js
app/assets/javascripts/u2f/error.js
+20
-23
register.js
app/assets/javascripts/u2f/register.js
+71
-80
util.js
app/assets/javascripts/u2f/util.js
+3
-12
authenticate_spec.js
spec/javascripts/u2f/authenticate_spec.js
+50
-59
mock_u2f_device.js
spec/javascripts/u2f/mock_u2f_device.js
+25
-28
register_spec.js
spec/javascripts/u2f/register_spec.js
+56
-64
No files found.
app/assets/javascripts/dispatcher.js
View file @
900dd7fa
...
@@ -79,6 +79,7 @@ import initChangesDropdown from './init_changes_dropdown';
...
@@ -79,6 +79,7 @@ import initChangesDropdown from './init_changes_dropdown';
import
AbuseReports
from
'./abuse_reports'
;
import
AbuseReports
from
'./abuse_reports'
;
import
{
ajaxGet
,
convertPermissionToBoolean
}
from
'./lib/utils/common_utils'
;
import
{
ajaxGet
,
convertPermissionToBoolean
}
from
'./lib/utils/common_utils'
;
import
AjaxLoadingSpinner
from
'./ajax_loading_spinner'
;
import
AjaxLoadingSpinner
from
'./ajax_loading_spinner'
;
import
U2FAuthenticate
from
'./u2f/authenticate'
;
(
function
()
{
(
function
()
{
var
Dispatcher
;
var
Dispatcher
;
...
@@ -536,14 +537,16 @@ import AjaxLoadingSpinner from './ajax_loading_spinner';
...
@@ -536,14 +537,16 @@ import AjaxLoadingSpinner from './ajax_loading_spinner';
case
'sessions'
:
case
'sessions'
:
case
'omniauth_callbacks'
:
case
'omniauth_callbacks'
:
if
(
!
gon
.
u2f
)
break
;
if
(
!
gon
.
u2f
)
break
;
gl
.
u2fAuthenticate
=
new
gl
.
U2FAuthenticate
(
const
u2fAuthenticate
=
new
U2FAuthenticate
(
$
(
'#js-authenticate-u2f'
),
$
(
'#js-authenticate-u2f'
),
'#js-login-u2f-form'
,
'#js-login-u2f-form'
,
gon
.
u2f
,
gon
.
u2f
,
document
.
querySelector
(
'#js-login-2fa-device'
),
document
.
querySelector
(
'#js-login-2fa-device'
),
document
.
querySelector
(
'.js-2fa-form'
),
document
.
querySelector
(
'.js-2fa-form'
),
);
);
gl
.
u2fAuthenticate
.
start
();
u2fAuthenticate
.
start
();
// needed in rspec
gl
.
u2fAuthenticate
=
u2fAuthenticate
;
case
'admin'
:
case
'admin'
:
new
Admin
();
new
Admin
();
switch
(
path
[
1
])
{
switch
(
path
[
1
])
{
...
...
app/assets/javascripts/main.js
View file @
900dd7fa
...
@@ -46,12 +46,6 @@ import './lib/utils/url_utility';
...
@@ -46,12 +46,6 @@ import './lib/utils/url_utility';
// behaviors
// behaviors
import
'./behaviors/'
;
import
'./behaviors/'
;
// u2f
import
'./u2f/authenticate'
;
import
'./u2f/error'
;
import
'./u2f/register'
;
import
'./u2f/util'
;
// everything else
// everything else
import
'./activities'
;
import
'./activities'
;
import
'./admin'
;
import
'./admin'
;
...
...
app/assets/javascripts/two_factor_auth.js
View file @
900dd7fa
/* global U2FRegister */
import
U2FRegister
from
'./u2f/register'
;
document
.
addEventListener
(
'DOMContentLoaded'
,
()
=>
{
document
.
addEventListener
(
'DOMContentLoaded'
,
()
=>
{
const
twoFactorNode
=
document
.
querySelector
(
'.js-two-factor-auth'
);
const
twoFactorNode
=
document
.
querySelector
(
'.js-two-factor-auth'
);
const
skippable
=
twoFactorNode
.
dataset
.
twoFactorSkippable
===
'true'
;
const
skippable
=
twoFactorNode
.
dataset
.
twoFactorSkippable
===
'true'
;
...
...
app/assets/javascripts/u2f/authenticate.js
View file @
900dd7fa
/* eslint-disable func-names,
space-before-function-paren, no-var, prefer-rest-params, wrap-iife, prefer-arrow-callback, no-else-return, quotes, quote-props, comma-dangle, one-var, one-var-declaration-per-line, max-len
*/
/* eslint-disable func-names,
wrap-iife
*/
/* global u2f */
/* global u2f */
/* global U2FError */
/* global U2FUtil */
import
_
from
'underscore'
;
import
_
from
'underscore'
;
import
isU2FSupported
from
'./util'
;
import
U2FError
from
'./error'
;
// Authenticate U2F (universal 2nd factor) devices for users to authenticate with.
// Authenticate U2F (universal 2nd factor) devices for users to authenticate with.
//
//
// State Flow #1: setup -> in_progress -> authenticated -> POST to server
// State Flow #1: setup -> in_progress -> authenticated -> POST to server
// State Flow #2: setup -> in_progress -> error -> setup
// State Flow #2: setup -> in_progress -> error -> setup
(
function
()
{
export
default
class
U2FAuthenticate
{
const
global
=
window
.
gl
||
(
window
.
gl
=
{});
constructor
(
container
,
form
,
u2fParams
,
fallbackButton
,
fallbackUI
)
{
this
.
container
=
container
;
global
.
U2FAuthenticate
=
(
function
()
{
this
.
renderNotSupported
=
this
.
renderNotSupported
.
bind
(
this
);
function
U2FAuthenticate
(
container
,
form
,
u2fParams
,
fallbackButton
,
fallbackUI
)
{
this
.
renderAuthenticated
=
this
.
renderAuthenticated
.
bind
(
this
);
this
.
container
=
container
;
this
.
renderError
=
this
.
renderError
.
bind
(
this
);
this
.
renderNotSupported
=
this
.
renderNotSupported
.
bind
(
this
);
this
.
renderInProgress
=
this
.
renderInProgress
.
bind
(
this
);
this
.
renderAuthenticated
=
this
.
renderAuthenticated
.
bind
(
this
);
this
.
renderTemplate
=
this
.
renderTemplate
.
bind
(
this
);
this
.
renderError
=
this
.
renderError
.
bind
(
this
);
this
.
authenticate
=
this
.
authenticate
.
bind
(
this
);
this
.
renderInProgress
=
this
.
renderInProgress
.
bind
(
this
);
this
.
start
=
this
.
start
.
bind
(
this
);
this
.
renderTemplate
=
this
.
renderTemplate
.
bind
(
this
);
this
.
appId
=
u2fParams
.
app_id
;
this
.
authenticate
=
this
.
authenticate
.
bind
(
this
);
this
.
challenge
=
u2fParams
.
challenge
;
this
.
start
=
this
.
start
.
bind
(
this
);
this
.
form
=
form
;
this
.
appId
=
u2fParams
.
app_id
;
this
.
fallbackButton
=
fallbackButton
;
this
.
challenge
=
u2fParams
.
challenge
;
this
.
fallbackUI
=
fallbackUI
;
this
.
form
=
form
;
if
(
this
.
fallbackButton
)
{
this
.
fallbackButton
=
fallbackButton
;
this
.
fallbackButton
.
addEventListener
(
'click'
,
this
.
switchToFallbackUI
.
bind
(
this
));
this
.
fallbackUI
=
fallbackUI
;
if
(
this
.
fallbackButton
)
this
.
fallbackButton
.
addEventListener
(
'click'
,
this
.
switchToFallbackUI
.
bind
(
this
));
this
.
signRequests
=
u2fParams
.
sign_requests
.
map
(
function
(
request
)
{
// The U2F Javascript API v1.1 requires a single challenge, with
// _no challenges per-request_. The U2F Javascript API v1.0 requires a
// challenge per-request, which is done by copying the single challenge
// into every request.
//
// In either case, we don't need the per-request challenges that the server
// has generated, so we can remove them.
//
// Note: The server library fixes this behaviour in (unreleased) version 1.0.0.
// This can be removed once we upgrade.
// https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4
return
_
(
request
).
omit
(
'challenge'
);
});
}
}
U2FAuthenticate
.
prototype
.
start
=
function
()
{
// The U2F Javascript API v1.1 requires a single challenge, with
if
(
U2FUtil
.
isU2FSupported
())
{
// _no challenges per-request_. The U2F Javascript API v1.0 requires a
return
this
.
renderInProgress
();
// challenge per-request, which is done by copying the single challenge
}
else
{
// into every request.
return
this
.
renderNotSupported
();
//
}
// In either case, we don't need the per-request challenges that the server
};
// has generated, so we can remove them.
//
// Note: The server library fixes this behaviour in (unreleased) version 1.0.0.
// This can be removed once we upgrade.
// https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4
this
.
signRequests
=
u2fParams
.
sign_requests
.
map
(
request
=>
_
(
request
).
omit
(
'challenge'
));
U2FAuthenticate
.
prototype
.
authenticate
=
function
()
{
this
.
templates
=
{
return
u2f
.
sign
(
this
.
appId
,
this
.
challenge
,
this
.
signRequests
,
(
function
(
_this
)
{
notSupported
:
'#js-authenticate-u2f-not-supported'
,
return
function
(
response
)
{
setup
:
'#js-authenticate-u2f-setup'
,
var
error
;
inProgress
:
'#js-authenticate-u2f-in-progress'
,
if
(
response
.
errorCode
)
{
error
:
'#js-authenticate-u2f-error'
,
error
=
new
U2FError
(
response
.
errorCode
,
'authenticate'
);
authenticated
:
'#js-authenticate-u2f-authenticated'
,
return
_this
.
renderError
(
error
);
}
else
{
return
_this
.
renderAuthenticated
(
JSON
.
stringify
(
response
));
}
};
})(
this
),
10
);
};
};
}
// Rendering #
start
()
{
U2FAuthenticate
.
prototype
.
templates
=
{
if
(
isU2FSupported
())
{
"notSupported"
:
"#js-authenticate-u2f-not-supported"
,
return
this
.
renderInProgress
();
"setup"
:
'#js-authenticate-u2f-setup'
,
}
"inProgress"
:
'#js-authenticate-u2f-in-progress'
,
return
this
.
renderNotSupported
();
"error"
:
'#js-authenticate-u2f-error'
,
}
"authenticated"
:
'#js-authenticate-u2f-authenticated'
};
U2FAuthenticate
.
prototype
.
renderTemplate
=
function
(
name
,
params
)
{
authenticate
()
{
var
template
,
templateString
;
return
u2f
.
sign
(
this
.
appId
,
this
.
challenge
,
this
.
signRequests
,
(
function
(
_this
)
{
templateString
=
$
(
this
.
templates
[
name
]).
html
();
return
function
(
response
)
{
template
=
_
.
template
(
templateString
);
if
(
response
.
errorCode
)
{
return
this
.
container
.
html
(
template
(
params
));
const
error
=
new
U2FError
(
response
.
errorCode
,
'authenticate'
);
};
return
_this
.
renderError
(
error
);
}
return
_this
.
renderAuthenticated
(
JSON
.
stringify
(
response
));
};
})(
this
),
10
);
}
U2FAuthenticate
.
prototype
.
renderInProgress
=
function
()
{
renderTemplate
(
name
,
params
)
{
this
.
renderTemplate
(
'inProgress'
);
const
templateString
=
$
(
this
.
templates
[
name
]).
html
();
return
this
.
authenticate
();
const
template
=
_
.
template
(
templateString
);
};
return
this
.
container
.
html
(
template
(
params
));
}
U2FAuthenticate
.
prototype
.
renderError
=
function
(
error
)
{
renderInProgress
()
{
this
.
renderTemplate
(
'error'
,
{
this
.
renderTemplate
(
'inProgress'
);
error_message
:
error
.
message
(),
return
this
.
authenticate
();
error_code
:
error
.
errorCode
}
});
return
this
.
container
.
find
(
'#js-u2f-try-again'
).
on
(
'click'
,
this
.
renderInProgress
);
};
U2FAuthenticate
.
prototype
.
renderAuthenticated
=
function
(
deviceResponse
)
{
renderError
(
error
)
{
this
.
renderTemplate
(
'authenticated'
);
this
.
renderTemplate
(
'error'
,
{
const
container
=
this
.
container
[
0
];
error_message
:
error
.
message
(),
container
.
querySelector
(
'#js-device-response'
).
value
=
deviceResponse
;
error_code
:
error
.
errorCode
,
container
.
querySelector
(
this
.
form
).
submit
(
);
}
);
this
.
fallbackButton
.
classList
.
add
(
'hidden'
);
return
this
.
container
.
find
(
'#js-u2f-try-again'
).
on
(
'click'
,
this
.
renderInProgress
);
};
}
U2FAuthenticate
.
prototype
.
renderNotSupported
=
function
()
{
renderAuthenticated
(
deviceResponse
)
{
return
this
.
renderTemplate
(
'notSupported'
);
this
.
renderTemplate
(
'authenticated'
);
};
const
container
=
this
.
container
[
0
];
container
.
querySelector
(
'#js-device-response'
).
value
=
deviceResponse
;
container
.
querySelector
(
this
.
form
).
submit
();
this
.
fallbackButton
.
classList
.
add
(
'hidden'
);
}
U2FAuthenticate
.
prototype
.
switchToFallbackUI
=
function
()
{
renderNotSupported
()
{
this
.
fallbackButton
.
classList
.
add
(
'hidden'
);
return
this
.
renderTemplate
(
'notSupported'
);
this
.
container
[
0
].
classList
.
add
(
'hidden'
);
}
this
.
fallbackUI
.
classList
.
remove
(
'hidden'
);
};
switchToFallbackUI
()
{
this
.
fallbackButton
.
classList
.
add
(
'hidden'
);
this
.
container
[
0
].
classList
.
add
(
'hidden'
);
this
.
fallbackUI
.
classList
.
remove
(
'hidden'
);
}
return
U2FAuthenticate
;
}
})();
})();
app/assets/javascripts/u2f/error.js
View file @
900dd7fa
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-console, quotes, prefer-template, max-len */
export
default
class
U2FError
{
/* global u2f */
constructor
(
errorCode
,
u2fFlowType
)
{
this
.
errorCode
=
errorCode
;
this
.
message
=
this
.
message
.
bind
(
this
);
this
.
httpsDisabled
=
window
.
location
.
protocol
!==
'https:'
;
this
.
u2fFlowType
=
u2fFlowType
;
}
(
function
()
{
message
()
{
this
.
U2FError
=
(
function
()
{
if
(
this
.
errorCode
===
window
.
u2f
.
ErrorCodes
.
BAD_REQUEST
&&
this
.
httpsDisabled
)
{
function
U2FError
(
errorCode
,
u2fFlowType
)
{
return
'U2F only works with HTTPS-enabled websites. Contact your administrator for more details.'
;
this
.
errorCode
=
errorCode
;
}
else
if
(
this
.
errorCode
===
window
.
u2f
.
ErrorCodes
.
DEVICE_INELIGIBLE
)
{
this
.
message
=
this
.
message
.
bind
(
this
);
if
(
this
.
u2fFlowType
===
'authenticate'
)
{
this
.
httpsDisabled
=
window
.
location
.
protocol
!==
'https:'
;
return
'This device has not been registered with us.'
;
this
.
u2fFlowType
=
u2fFlowType
;
}
U2FError
.
prototype
.
message
=
function
()
{
if
(
this
.
errorCode
===
u2f
.
ErrorCodes
.
BAD_REQUEST
&&
this
.
httpsDisabled
)
{
return
'U2F only works with HTTPS-enabled websites. Contact your administrator for more details.'
;
}
else
if
(
this
.
errorCode
===
u2f
.
ErrorCodes
.
DEVICE_INELIGIBLE
)
{
if
(
this
.
u2fFlowType
===
'authenticate'
)
return
'This device has not been registered with us.'
;
if
(
this
.
u2fFlowType
===
'register'
)
return
'This device has already been registered with us.'
;
}
}
return
"There was a problem communicating with your device."
;
if
(
this
.
u2fFlowType
===
'register'
)
{
};
return
'This device has already been registered with us.'
;
}
return
U2FError
;
}
})();
return
'There was a problem communicating with your device.'
;
}).
call
(
window
);
}
}
app/assets/javascripts/u2f/register.js
View file @
900dd7fa
/* eslint-disable func-names,
space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-else-return, quotes, quote-props, comma-dangle, one-var, one-var-declaration-per-line, max-len
*/
/* eslint-disable func-names,
wrap-iife
*/
/* global u2f */
/* global u2f */
/* global U2FError */
/* global U2FUtil */
import
_
from
'underscore'
;
import
_
from
'underscore'
;
import
isU2FSupported
from
'./util'
;
import
U2FError
from
'./error'
;
// Register U2F (universal 2nd factor) devices for users to authenticate with.
// Register U2F (universal 2nd factor) devices for users to authenticate with.
//
//
// State Flow #1: setup -> in_progress -> registered -> POST to server
// State Flow #1: setup -> in_progress -> registered -> POST to server
// State Flow #2: setup -> in_progress -> error -> setup
// State Flow #2: setup -> in_progress -> error -> setup
(
function
()
{
export
default
class
U2FRegister
{
this
.
U2FRegister
=
(
function
()
{
constructor
(
container
,
u2fParams
)
{
function
U2FRegister
(
container
,
u2fParams
)
{
this
.
container
=
container
;
this
.
container
=
container
;
this
.
renderNotSupported
=
this
.
renderNotSupported
.
bind
(
this
);
this
.
renderNotSupported
=
this
.
renderNotSupported
.
bind
(
this
);
this
.
renderRegistered
=
this
.
renderRegistered
.
bind
(
this
);
this
.
renderRegistered
=
this
.
renderRegistered
.
bind
(
this
);
this
.
renderError
=
this
.
renderError
.
bind
(
this
);
this
.
renderError
=
this
.
renderError
.
bind
(
this
);
this
.
renderInProgress
=
this
.
renderInProgress
.
bind
(
this
);
this
.
renderInProgress
=
this
.
renderInProgress
.
bind
(
this
);
this
.
renderSetup
=
this
.
renderSetup
.
bind
(
this
);
this
.
renderSetup
=
this
.
renderSetup
.
bind
(
this
);
this
.
renderTemplate
=
this
.
renderTemplate
.
bind
(
this
);
this
.
renderTemplate
=
this
.
renderTemplate
.
bind
(
this
);
this
.
register
=
this
.
register
.
bind
(
this
);
this
.
register
=
this
.
register
.
bind
(
this
);
this
.
start
=
this
.
start
.
bind
(
this
);
this
.
start
=
this
.
start
.
bind
(
this
);
this
.
appId
=
u2fParams
.
app_id
;
this
.
appId
=
u2fParams
.
app_id
;
this
.
registerRequests
=
u2fParams
.
register_requests
;
this
.
registerRequests
=
u2fParams
.
register_requests
;
this
.
signRequests
=
u2fParams
.
sign_requests
;
this
.
signRequests
=
u2fParams
.
sign_requests
;
}
U2FRegister
.
prototype
.
start
=
function
()
{
this
.
templates
=
{
if
(
U2FUtil
.
isU2FSupported
())
{
notSupported
:
'#js-register-u2f-not-supported'
,
return
this
.
renderSetup
();
setup
:
'#js-register-u2f-setup'
,
}
else
{
inProgress
:
'#js-register-u2f-in-progress'
,
return
this
.
renderNotSupported
();
error
:
'#js-register-u2f-error'
,
}
registered
:
'#js-register-u2f-registered'
,
};
};
}
U2FRegister
.
prototype
.
register
=
function
()
{
start
()
{
return
u2f
.
register
(
this
.
appId
,
this
.
registerRequests
,
this
.
signRequests
,
(
function
(
_this
)
{
if
(
isU2FSupported
())
{
return
function
(
response
)
{
return
this
.
renderSetup
();
var
error
;
}
if
(
response
.
errorCode
)
{
return
this
.
renderNotSupported
();
error
=
new
U2FError
(
response
.
errorCode
,
'register'
);
}
return
_this
.
renderError
(
error
);
}
else
{
return
_this
.
renderRegistered
(
JSON
.
stringify
(
response
));
}
};
})(
this
),
10
);
};
// Rendering #
register
()
{
U2FRegister
.
prototype
.
templates
=
{
return
u2f
.
register
(
this
.
appId
,
this
.
registerRequests
,
this
.
signRequests
,
(
function
(
_this
)
{
"notSupported"
:
"#js-register-u2f-not-supported"
,
return
function
(
response
)
{
"setup"
:
'#js-register-u2f-setup'
,
if
(
response
.
errorCode
)
{
"inProgress"
:
'#js-register-u2f-in-progress'
,
const
error
=
new
U2FError
(
response
.
errorCode
,
'register'
);
"error"
:
'#js-register-u2f-error'
,
return
_this
.
renderError
(
error
);
"registered"
:
'#js-register-u2f-registered'
}
};
return
_this
.
renderRegistered
(
JSON
.
stringify
(
response
));
};
})(
this
),
10
);
}
U2FRegister
.
prototype
.
renderTemplate
=
function
(
name
,
params
)
{
renderTemplate
(
name
,
params
)
{
var
template
,
templateString
;
const
templateString
=
$
(
this
.
templates
[
name
]).
html
();
templateString
=
$
(
this
.
templates
[
name
]).
html
();
const
template
=
_
.
template
(
templateString
);
template
=
_
.
template
(
templateString
);
return
this
.
container
.
html
(
template
(
params
));
return
this
.
container
.
html
(
template
(
params
));
}
};
U2FRegister
.
prototype
.
renderSetup
=
function
()
{
renderSetup
()
{
this
.
renderTemplate
(
'setup'
);
this
.
renderTemplate
(
'setup'
);
return
this
.
container
.
find
(
'#js-setup-u2f-device'
).
on
(
'click'
,
this
.
renderInProgress
);
return
this
.
container
.
find
(
'#js-setup-u2f-device'
).
on
(
'click'
,
this
.
renderInProgress
);
};
}
U2FRegister
.
prototype
.
renderInProgress
=
function
()
{
renderInProgress
()
{
this
.
renderTemplate
(
'inProgress'
);
this
.
renderTemplate
(
'inProgress'
);
return
this
.
register
();
return
this
.
register
();
};
}
U2FRegister
.
prototype
.
renderError
=
function
(
error
)
{
renderError
(
error
)
{
this
.
renderTemplate
(
'error'
,
{
this
.
renderTemplate
(
'error'
,
{
error_message
:
error
.
message
(),
error_message
:
error
.
message
(),
error_code
:
error
.
errorCode
error_code
:
error
.
errorCode
,
});
});
return
this
.
container
.
find
(
'#js-u2f-try-again'
).
on
(
'click'
,
this
.
renderSetup
);
return
this
.
container
.
find
(
'#js-u2f-try-again'
).
on
(
'click'
,
this
.
renderSetup
);
};
}
U2FRegister
.
prototype
.
renderRegistered
=
function
(
deviceResponse
)
{
renderRegistered
(
deviceResponse
)
{
this
.
renderTemplate
(
'registered'
);
this
.
renderTemplate
(
'registered'
);
// Prefer to do this instead of interpolating using Underscore templates
// Prefer to do this instead of interpolating using Underscore templates
// because of JSON escaping issues.
// because of JSON escaping issues.
return
this
.
container
.
find
(
"#js-device-response"
).
val
(
deviceResponse
);
return
this
.
container
.
find
(
'#js-device-response'
).
val
(
deviceResponse
);
};
}
U2FRegister
.
prototype
.
renderNotSupported
=
function
()
{
return
this
.
renderTemplate
(
'notSupported'
);
};
return
U2FRegister
;
renderNotSupported
()
{
})();
return
this
.
renderTemplate
(
'notSupported'
);
}).
call
(
window
);
}
}
app/assets/javascripts/u2f/util.js
View file @
900dd7fa
/* eslint-disable func-names, space-before-function-paren, wrap-iife */
export
default
function
isU2FSupported
()
{
(
function
()
{
return
window
.
u2f
;
this
.
U2FUtil
=
(
function
()
{
}
function
U2FUtil
()
{}
U2FUtil
.
isU2FSupported
=
function
()
{
return
window
.
u2f
;
};
return
U2FUtil
;
})();
}).
call
(
window
);
spec/javascripts/u2f/authenticate_spec.js
View file @
900dd7fa
/* eslint-disable space-before-function-paren, new-parens, quotes, comma-dangle, no-var, one-var, one-var-declaration-per-line, max-len */
import
U2FAuthenticate
from
'~/u2f/authenticate'
;
/* global MockU2FDevice */
/* global U2FAuthenticate */
import
'~/u2f/authenticate'
;
import
'~/u2f/util'
;
import
'~/u2f/error'
;
import
'vendor/u2f'
;
import
'vendor/u2f'
;
import
'./mock_u2f_device'
;
import
MockU2FDevice
from
'./mock_u2f_device'
;
describe
(
'U2FAuthenticate'
,
()
=>
{
preloadFixtures
(
'u2f/authenticate.html.raw'
);
(
function
()
{
beforeEach
(()
=>
{
describe
(
'U2FAuthenticate'
,
function
()
{
loadFixtures
(
'u2f/authenticate.html.raw'
);
preloadFixtures
(
'u2f/authenticate.html.raw'
);
this
.
u2fDevice
=
new
MockU2FDevice
();
this
.
container
=
$
(
'#js-authenticate-u2f'
);
this
.
component
=
new
U2FAuthenticate
(
this
.
container
,
'#js-login-u2f-form'
,
{
sign_requests
:
[],
},
document
.
querySelector
(
'#js-login-2fa-device'
),
document
.
querySelector
(
'.js-2fa-form'
),
);
beforeEach
(
function
()
{
// bypass automatic form submission within renderAuthenticated
loadFixtures
(
'u2f/authenticate.html.raw'
);
spyOn
(
this
.
component
,
'renderAuthenticated'
).
and
.
returnValue
(
true
);
this
.
u2fDevice
=
new
MockU2FDevice
;
this
.
container
=
$
(
"#js-authenticate-u2f"
);
this
.
component
=
new
window
.
gl
.
U2FAuthenticate
(
this
.
container
,
'#js-login-u2f-form'
,
{
sign_requests
:
[]
},
document
.
querySelector
(
'#js-login-2fa-device'
),
document
.
querySelector
(
'.js-2fa-form'
)
);
// bypass automatic form submission within renderAuthenticated
return
this
.
component
.
start
();
spyOn
(
this
.
component
,
'renderAuthenticated'
).
and
.
returnValue
(
true
);
}
);
return
this
.
component
.
start
();
it
(
'allows authenticating via a U2F device'
,
()
=>
{
const
inProgressMessage
=
this
.
container
.
find
(
'p'
);
expect
(
inProgressMessage
.
text
()).
toContain
(
'Trying to communicate with your device'
);
this
.
u2fDevice
.
respondToAuthenticateRequest
({
deviceData
:
'this is data from the device'
,
});
});
it
(
'allows authenticating via a U2F device'
,
function
()
{
expect
(
this
.
component
.
renderAuthenticated
).
toHaveBeenCalledWith
(
'{"deviceData":"this is data from the device"}'
);
var
inProgressMessage
;
});
inProgressMessage
=
this
.
container
.
find
(
"p"
);
expect
(
inProgressMessage
.
text
()).
toContain
(
"Trying to communicate with your device"
);
return
describe
(
'errors'
,
()
=>
{
it
(
'displays an error message'
,
()
=>
{
const
setupButton
=
this
.
container
.
find
(
'#js-login-u2f-device'
);
setupButton
.
trigger
(
'click'
);
this
.
u2fDevice
.
respondToAuthenticateRequest
({
this
.
u2fDevice
.
respondToAuthenticateRequest
({
deviceData
:
"this is data from the device"
errorCode
:
'error!'
,
});
});
expect
(
this
.
component
.
renderAuthenticated
).
toHaveBeenCalledWith
(
'{"deviceData":"this is data from the device"}'
);
const
errorMessage
=
this
.
container
.
find
(
'p'
);
return
expect
(
errorMessage
.
text
()).
toContain
(
'There was a problem communicating with your device'
);
});
});
return
describe
(
"errors"
,
function
()
{
return
it
(
'allows retrying authentication after an error'
,
()
=>
{
it
(
"displays an error message"
,
function
()
{
let
setupButton
=
this
.
container
.
find
(
'#js-login-u2f-device'
);
var
errorMessage
,
setupButton
;
setupButton
.
trigger
(
'click'
);
setupButton
=
this
.
container
.
find
(
"#js-login-u2f-device"
);
this
.
u2fDevice
.
respondToAuthenticateRequest
({
setupButton
.
trigger
(
'click'
);
errorCode
:
'error!'
,
this
.
u2fDevice
.
respondToAuthenticateRequest
({
errorCode
:
"error!"
});
errorMessage
=
this
.
container
.
find
(
"p"
);
return
expect
(
errorMessage
.
text
()).
toContain
(
"There was a problem communicating with your device"
);
});
});
return
it
(
"allows retrying authentication after an error"
,
function
()
{
const
retryButton
=
this
.
container
.
find
(
'#js-u2f-try-again'
);
var
retryButton
,
setupButton
;
retryButton
.
trigger
(
'click'
);
setupButton
=
this
.
container
.
find
(
"#js-login-u2f-device"
);
setupButton
=
this
.
container
.
find
(
'#js-login-u2f-device'
);
setupButton
.
trigger
(
'click'
);
setupButton
.
trigger
(
'click'
);
this
.
u2fDevice
.
respondToAuthenticateRequest
({
this
.
u2fDevice
.
respondToAuthenticateRequest
({
errorCode
:
"error!"
deviceData
:
'this is data from the device'
,
});
retryButton
=
this
.
container
.
find
(
"#js-u2f-try-again"
);
retryButton
.
trigger
(
'click'
);
setupButton
=
this
.
container
.
find
(
"#js-login-u2f-device"
);
setupButton
.
trigger
(
'click'
);
this
.
u2fDevice
.
respondToAuthenticateRequest
({
deviceData
:
"this is data from the device"
});
expect
(
this
.
component
.
renderAuthenticated
).
toHaveBeenCalledWith
(
'{"deviceData":"this is data from the device"}'
);
});
});
expect
(
this
.
component
.
renderAuthenticated
).
toHaveBeenCalledWith
(
'{"deviceData":"this is data from the device"}'
);
});
});
});
});
})
.
call
(
window
)
;
});
spec/javascripts/u2f/mock_u2f_device.js
View file @
900dd7fa
/* eslint-disable space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-expressions, no-return-assign, no-param-reassign, max-len */
/* eslint-disable prefer-rest-params, wrap-iife,
no-unused-expressions, no-return-assign, no-param-reassign*/
(
function
()
{
export
default
class
MockU2FDevice
{
this
.
MockU2FDevice
=
(
function
()
{
constructor
()
{
function
MockU2FDevice
()
{
this
.
respondToAuthenticateRequest
=
this
.
respondToAuthenticateRequest
.
bind
(
this
);
this
.
respondToAuthenticateRequest
=
this
.
respondToAuthenticateRequest
.
bind
(
this
);
this
.
respondToRegisterRequest
=
this
.
respondToRegisterRequest
.
bind
(
this
);
this
.
respondToRegisterRequest
=
this
.
respondToRegisterRequest
.
bind
(
this
);
window
.
u2f
||
(
window
.
u2f
=
{});
window
.
u2f
||
(
window
.
u2f
=
{});
window
.
u2f
.
register
=
(
function
(
_this
)
{
window
.
u2f
.
register
=
(
function
(
_this
)
{
return
function
(
appId
,
registerRequests
,
signRequests
,
callback
)
{
return
function
(
appId
,
registerRequests
,
signRequests
,
callback
)
{
return
_this
.
registerCallback
=
callback
;
return
_this
.
registerCallback
=
callback
;
};
};
})(
this
);
})(
this
);
window
.
u2f
.
sign
=
(
function
(
_this
)
{
window
.
u2f
.
sign
=
(
function
(
_this
)
{
return
function
(
appId
,
challenges
,
signRequests
,
callback
)
{
return
function
(
appId
,
challenges
,
signRequests
,
callback
)
{
return
_this
.
authenticateCallback
=
callback
;
return
_this
.
authenticateCallback
=
callback
;
};
};
})(
this
);
})(
this
);
}
}
MockU2FDevice
.
prototype
.
respondToRegisterRequest
=
function
(
params
)
{
respondToRegisterRequest
(
params
)
{
return
this
.
registerCallback
(
params
);
return
this
.
registerCallback
(
params
);
};
}
MockU2FDevice
.
prototype
.
respondToAuthenticateRequest
=
function
(
params
)
{
respondToAuthenticateRequest
(
params
)
{
return
this
.
authenticateCallback
(
params
);
return
this
.
authenticateCallback
(
params
);
};
}
}
return
MockU2FDevice
;
})();
}).
call
(
window
);
spec/javascripts/u2f/register_spec.js
View file @
900dd7fa
/* eslint-disable space-before-function-paren, new-parens, quotes, no-var, one-var, one-var-declaration-per-line, comma-dangle, max-len */
import
U2FRegister
from
'~/u2f/register'
;
/* global MockU2FDevice */
/* global U2FRegister */
import
'~/u2f/register'
;
import
'~/u2f/util'
;
import
'~/u2f/error'
;
import
'vendor/u2f'
;
import
'vendor/u2f'
;
import
'./mock_u2f_device'
;
import
MockU2FDevice
from
'./mock_u2f_device'
;
describe
(
'U2FRegister'
,
()
=>
{
preloadFixtures
(
'u2f/register.html.raw'
);
(
function
()
{
beforeEach
(()
=>
{
describe
(
'U2FRegister'
,
function
()
{
loadFixtures
(
'u2f/register.html.raw'
);
preloadFixtures
(
'u2f/register.html.raw'
);
this
.
u2fDevice
=
new
MockU2FDevice
();
this
.
container
=
$
(
'#js-register-u2f'
);
this
.
component
=
new
U2FRegister
(
this
.
container
,
$
(
'#js-register-u2f-templates'
),
{},
'token'
);
return
this
.
component
.
start
();
});
beforeEach
(
function
()
{
it
(
'allows registering a U2F device'
,
()
=>
{
loadFixtures
(
'u2f/register.html.raw'
);
const
setupButton
=
this
.
container
.
find
(
'#js-setup-u2f-device'
);
this
.
u2fDevice
=
new
MockU2FDevice
;
expect
(
setupButton
.
text
()).
toBe
(
'Setup new U2F device'
);
this
.
container
=
$
(
"#js-register-u2f"
);
setupButton
.
trigger
(
'click'
);
this
.
component
=
new
U2FRegister
(
this
.
container
,
$
(
"#js-register-u2f-templates"
),
{},
"token"
);
const
inProgressMessage
=
this
.
container
.
children
(
'p'
);
return
this
.
component
.
start
();
expect
(
inProgressMessage
.
text
()).
toContain
(
'Trying to communicate with your device'
);
this
.
u2fDevice
.
respondToRegisterRequest
({
deviceData
:
'this is data from the device'
,
});
});
it
(
'allows registering a U2F device'
,
function
()
{
const
registeredMessage
=
this
.
container
.
find
(
'p'
);
var
deviceResponse
,
inProgressMessage
,
registeredMessage
,
setupButton
;
const
deviceResponse
=
this
.
container
.
find
(
'#js-device-response'
);
setupButton
=
this
.
container
.
find
(
"#js-setup-u2f-device"
);
expect
(
registeredMessage
.
text
()).
toContain
(
'Your device was successfully set up!'
);
expect
(
setupButton
.
text
()).
toBe
(
'Setup new U2F device'
);
return
expect
(
deviceResponse
.
val
()).
toBe
(
'{"deviceData":"this is data from the device"}'
);
});
return
describe
(
'errors'
,
()
=>
{
it
(
'doesn
\'
t allow the same device to be registered twice (for the same user'
,
()
=>
{
const
setupButton
=
this
.
container
.
find
(
'#js-setup-u2f-device'
);
setupButton
.
trigger
(
'click'
);
setupButton
.
trigger
(
'click'
);
inProgressMessage
=
this
.
container
.
children
(
"p"
);
expect
(
inProgressMessage
.
text
()).
toContain
(
"Trying to communicate with your device"
);
this
.
u2fDevice
.
respondToRegisterRequest
({
this
.
u2fDevice
.
respondToRegisterRequest
({
deviceData
:
"this is data from the device"
errorCode
:
4
,
});
});
registeredMessage
=
this
.
container
.
find
(
'p'
);
const
errorMessage
=
this
.
container
.
find
(
'p'
);
deviceResponse
=
this
.
container
.
find
(
'#js-device-response'
);
return
expect
(
errorMessage
.
text
()).
toContain
(
'already been registered with us'
);
expect
(
registeredMessage
.
text
()).
toContain
(
"Your device was successfully set up!"
);
return
expect
(
deviceResponse
.
val
()).
toBe
(
'{"deviceData":"this is data from the device"}'
);
});
});
return
describe
(
"errors"
,
function
()
{
it
(
"doesn't allow the same device to be registered twice (for the same user"
,
function
()
{
it
(
'displays an error message for other errors'
,
()
=>
{
var
errorMessage
,
setupButton
;
const
setupButton
=
this
.
container
.
find
(
'#js-setup-u2f-device'
);
setupButton
=
this
.
container
.
find
(
"#js-setup-u2f-device"
);
setupButton
.
trigger
(
'click'
);
setupButton
.
trigger
(
'click'
);
this
.
u2fDevice
.
respondToRegisterRequest
({
this
.
u2fDevice
.
respondToRegisterRequest
({
errorCode
:
'error!'
,
errorCode
:
4
});
errorMessage
=
this
.
container
.
find
(
"p"
);
return
expect
(
errorMessage
.
text
()).
toContain
(
"already been registered with us"
);
});
});
it
(
"displays an error message for other errors"
,
function
()
{
const
errorMessage
=
this
.
container
.
find
(
'p'
);
var
errorMessage
,
setupButton
;
return
expect
(
errorMessage
.
text
()).
toContain
(
'There was a problem communicating with your device'
)
;
setupButton
=
this
.
container
.
find
(
"#js-setup-u2f-device"
);
}
);
setupButton
.
trigger
(
'click'
);
this
.
u2fDevice
.
respondToRegisterRequest
(
{
return
it
(
'allows retrying registration after an error'
,
()
=>
{
errorCode
:
"error!"
let
setupButton
=
this
.
container
.
find
(
'#js-setup-u2f-device'
);
}
);
setupButton
.
trigger
(
'click'
);
errorMessage
=
this
.
container
.
find
(
"p"
);
this
.
u2fDevice
.
respondToRegisterRequest
({
return
expect
(
errorMessage
.
text
()).
toContain
(
"There was a problem communicating with your device"
);
errorCode
:
'error!'
,
});
});
return
it
(
"allows retrying registration after an error"
,
function
()
{
const
retryButton
=
this
.
container
.
find
(
'#U2FTryAgain'
);
var
registeredMessage
,
retryButton
,
setupButton
;
retryButton
.
trigger
(
'click'
);
setupButton
=
this
.
container
.
find
(
"#js-setup-u2f-device"
);
setupButton
=
this
.
container
.
find
(
'#js-setup-u2f-device'
);
setupButton
.
trigger
(
'click'
);
setupButton
.
trigger
(
'click'
);
this
.
u2fDevice
.
respondToRegisterRequest
({
this
.
u2fDevice
.
respondToRegisterRequest
({
errorCode
:
"error!"
deviceData
:
'this is data from the device'
,
});
retryButton
=
this
.
container
.
find
(
"#U2FTryAgain"
);
retryButton
.
trigger
(
'click'
);
setupButton
=
this
.
container
.
find
(
"#js-setup-u2f-device"
);
setupButton
.
trigger
(
'click'
);
this
.
u2fDevice
.
respondToRegisterRequest
({
deviceData
:
"this is data from the device"
});
registeredMessage
=
this
.
container
.
find
(
"p"
);
return
expect
(
registeredMessage
.
text
()).
toContain
(
"Your device was successfully set up!"
);
});
});
const
registeredMessage
=
this
.
container
.
find
(
'p'
);
return
expect
(
registeredMessage
.
text
()).
toContain
(
'Your device was successfully set up!'
);
});
});
});
});
})
.
call
(
window
)
;
});
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