summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2016-02-29 13:39:29 +0000
committerPhil Hughes <me@iamphill.com>2016-03-01 13:17:02 +0000
commitb6eda35b9d171e9d2ed487dc92646c1cc9c68430 (patch)
treefeef4927a8359ca10b7fe7962b6e8ec14a345c5d
parent38bcbdfb7e710fa11a9ddcde361d651c82d10105 (diff)
downloadgitlab-ce-b6eda35b9d171e9d2ed487dc92646c1cc9c68430.tar.gz
Account settings
-rw-r--r--app/assets/javascripts/profile.js.coffee7
-rw-r--r--app/assets/stylesheets/framework/common.scss3
-rw-r--r--app/assets/stylesheets/framework/variables.scss4
-rw-r--r--app/assets/stylesheets/pages/profile.scss60
-rw-r--r--app/controllers/profiles/accounts_controller.rb24
-rw-r--r--app/views/profiles/accounts/show.html.haml229
6 files changed, 208 insertions, 119 deletions
diff --git a/app/assets/javascripts/profile.js.coffee b/app/assets/javascripts/profile.js.coffee
index 15fd9e99ce9..c93daa18505 100644
--- a/app/assets/javascripts/profile.js.coffee
+++ b/app/assets/javascripts/profile.js.coffee
@@ -4,12 +4,13 @@ class @Profile
$('.js-preferences-form').on 'change.preference', 'input[type=radio]', ->
$(this).parents('form').submit()
- $('.update-username form').on 'ajax:before', ->
- $('.loading-gif').show()
+ $('.update-username').on 'ajax:before', ->
+ $('.loading-username').show()
$(this).find('.update-success').hide()
$(this).find('.update-failed').hide()
- $('.update-username form').on 'ajax:complete', ->
+ $('.update-username').on 'ajax:complete', ->
+ $('.loading-username').hide()
$(this).find('.btn-save').enable()
$(this).find('.loading-gif').hide()
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 4cf68545e11..fb493d6728a 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -12,9 +12,10 @@
.prepend-top-default { margin-top: $gl-padding !important; }
.prepend-top-20 { margin-top:20px }
.prepend-left-10 { margin-left:10px }
-.prepend-left-default { margin-left:$gl-padding }
+.prepend-left-default { margin-left: $gl-padding; }
.prepend-left-20 { margin-left:20px }
.append-right-10 { margin-right:10px }
+.append-right-default { margin-right: $gl-padding; }
.append-right-20 { margin-right:20px }
.append-bottom-10 { margin-bottom:10px }
.append-bottom-15 { margin-bottom:15px }
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 7834cb0bfa5..107fa92447d 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -64,7 +64,7 @@ $orange-light: rgba(252, 109, 38, 0.80);
$orange-normal: #E75E40;
$orange-dark: #CE5237;
-$red-light: #F43263;
+$red-light: #F06559;
$red-normal: #E52C5A;
$red-dark: #D22852;
@@ -88,7 +88,7 @@ $border-orange-light: #fc6d26;
$border-orange-normal: #CE5237;
$border-orange-dark: #C14E35;
-$border-red-light: #E52C5A;
+$border-red-light: #F24F41;
$border-red-normal: #D22852;
$border-red-dark: #CA264F;
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index 07ed2490d7f..da721b1d055 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -1,10 +1,3 @@
-.account-page {
- fieldset {
- margin-bottom: 15px;
- padding-bottom: 15px;
- }
-}
-
.profile-settings-sidebar {
a {
color: $gl-primary;
@@ -61,6 +54,18 @@
}
}
+.two-factor-new-manual-panel {
+ padding: 10px 10px;
+ background-color: #FAFAFA;
+ border: 1px solid #E5E5E5;
+ border-radius: 3px;
+}
+
+.two-factor-new-manual-content {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
.calendar-hint {
margin-top: -12px;
float: right;
@@ -140,3 +145,44 @@
width: auto;
}
}
+
+.change-username-title {
+ color: #FC6D26;
+}
+
+.remove-account-title {
+ color: #F00;
+}
+
+.provider-btn-group {
+ display: inline-block;
+ margin-right: 10px;
+ border: 1px solid #E5E5E5;
+ border-radius: 3px;
+
+ &:last-child {
+ margin-right: 0;
+ }
+}
+
+.provider-btn-image {
+ display: inline-block;
+ padding: 5px 10px;
+ border-right: 1px solid #E5E5E5;
+
+ > img {
+ width: 20px;
+ }
+}
+
+.provider-btn {
+ display: inline-block;
+ padding: 5px 10px;
+ margin-left: -3px;
+ line-height: 22px;
+ background-color: $gray-light;
+
+ &.not-active {
+ color: #4688F1;
+ }
+}
diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb
index 175afbf8425..f2fe9de7a13 100644
--- a/app/controllers/profiles/accounts_controller.rb
+++ b/app/controllers/profiles/accounts_controller.rb
@@ -1,6 +1,18 @@
class Profiles::AccountsController < Profiles::ApplicationController
def show
+ unless current_user.otp_secret
+ current_user.otp_secret = User.generate_otp_secret(32)
+ end
+
+ unless current_user.otp_grace_period_started_at && two_factor_grace_period
+ current_user.otp_grace_period_started_at = Time.current
+ end
+
+ # current_user.save! if current_user.changed?
+
@user = current_user
+
+ @qr_code = build_qr_code
end
def unlink
@@ -8,4 +20,16 @@ class Profiles::AccountsController < Profiles::ApplicationController
current_user.identities.find_by(provider: provider).destroy
redirect_to profile_account_path
end
+
+ private
+
+ def build_qr_code
+ issuer = "#{issuer_host} | #{current_user.email}"
+ uri = current_user.otp_provisioning_uri(current_user.email, issuer: issuer)
+ RQRCode::render_qrcode(uri, :svg, level: :m, unit: 3)
+ end
+
+ def issuer_host
+ Gitlab.config.gitlab.host
+ end
end
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index 9fa96084f94..af72e506b4b 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -5,114 +5,131 @@
.alert.alert-info
Some options are unavailable for LDAP accounts
-.account-page.prepend-top-default
- .panel.panel-default.update-token
- .panel-heading
- Reset Private token
- .panel-body
- = form_for @user, url: reset_private_token_profile_path, method: :put do |f|
- .data
- %p
- Your private token is used to access application resources without authentication.
- %br
- It can be used for atom feeds or the API.
- %span.cred
- Keep it secret!
-
- %p.cgray
- - if current_user.private_token
- = text_field_tag "token", current_user.private_token, class: "form-control"
- - else
- %span You don`t have one yet. Click generate to fix it.
-
- .form-actions
- - if current_user.private_token
- = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default"
+.row.prepend-top-default
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0
+ Private Token
+ %p
+ Your private token is used to access application resources without authentication.
+ .col-lg-9
+ = form_for @user, url: reset_private_token_profile_path, method: :put do |f|
+ %p.cgray
+ - if current_user.private_token
+ = label_tag "token", "Private token", class: "label-light"
+ = text_field_tag "token", current_user.private_token, class: "form-control"
+ - else
+ %span You don`t have one yet. Click generate to fix it.
+ %p.help-block
+ It can be used for atom feeds or the API. Keep it secret!
+ .prepend-top-default
+ - if current_user.private_token
+ = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default"
+ - else
+ = f.submit 'Generate', class: "btn btn-default"
+%hr
+.row.prepend-top-default
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0
+ Two-factor Authentication
+ %p
+ Increase your account's security by enabling two-factor authentication (2FA).
+ .col-lg-9
+ %p
+ Status: #{current_user.two_factor_enabled? ? 'enabled' : 'disabled'}
+ - if !current_user.two_factor_enabled?
+ %p
+ Download the Google Authenticator application from App Store for iOS or Google Play for Android and scan this code.
+ More information is available in the #{link_to('documentation', help_page_path('profile', 'two_factor_authentication'))}.
+ .row.append-bottom-10
+ .col-md-3
+ = raw @qr_code
+ .col-md-9
+ .two-factor-new-manual-panel
+ %p.two-factor-new-manual-content
+ Can't scan the code?
+ %p.two-factor-new-manual-content
+ To add the entry manually, provide the following details to the application on your phone.
+ %p.two-factor-new-manual-content
+ Account:
+ = current_user.email
+ %p.two-factor-new-manual-content
+ Key:
+ = current_user.otp_secret.scan(/.{4}/).join(' ')
+ %p.two-factor-new-manual-content
+ Time based: Yes
+ = form_for @user, url: "", method: :put do |f|
+ .form-group
+ = label_tag :pin_code, nil, class: "label-light"
+ = text_field_tag :pin_code, nil, class: "form-control", required: true
+ .prepend-top-default
+ = submit_tag 'Enable two-factor authentication', class: 'btn btn-success'
+%hr
+- if button_based_providers.any?
+ .row.prepend-top-default
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0
+ Social sign-in
+ %p
+ Activate signin with one of the following services
+ .col-lg-9
+ %label.label-light
+ Connected Accounts
+ %p Click on icon to activate signin with one of the following services
+ - button_based_providers.each do |provider|
+ .provider-btn-group
+ .provider-btn-image
+ = provider_image_tag(provider)
+ - if auth_active?(provider)
+ = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'provider-btn' do
+ Disconnect
- else
- = f.submit 'Generate', class: "btn btn-default"
+ = link_to user_omniauth_authorize_path(provider), method: :post, class: "provider-btn #{'not-active' if !auth_active?(provider)}", "data-no-turbolink" => "true" do
+ Connect
+ %hr
+- if current_user.can_change_username?
+ .row.prepend-top-default
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0.change-username-title
+ Change username
+ %p
+ Changing your username will change path to all personal projects!
+ .col-lg-9
+ = form_for @user, url: update_username_profile_path, method: :put, remote: true, html: {class: "update-username"} do |f|
+ .form-group
+ = f.label :username, "Path", class: "label-light"
+ .input-group
+ .input-group-addon
+ = "#{root_url}u/"
+ = f.text_field :username, required: true, class: 'form-control'
+ .help-block
+ Current path:
+ = "#{root_url}u/#{current_user.username}"
+ .prepend-top-default
+ = f.button class: "btn btn-warning", type: "submit" do
+ = icon "spinner spin", class: "hidden loading-username"
+ Update username
+ %hr
- .panel.panel-default
- .panel-heading
- Two-factor Authentication
- .panel-body
- - if current_user.two_factor_enabled?
- .pull-right
- = link_to 'Disable Two-factor Authentication', profile_two_factor_auth_path, method: :delete, class: 'btn btn-close btn-sm',
- data: { confirm: 'Are you sure?' }
- %p.text-success
- %strong
- Two-factor Authentication is enabled
+- if signup_enabled?
+ .row.prepend-top-default
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0.remove-account-title
+ Remove account
+ .col-lg-9
+ - if @user.can_be_removed?
%p
- If you lose your recovery codes you can
- %strong
- = succeed ',' do
- = link_to 'generate new ones', codes_profile_two_factor_auth_path, method: :post, data: { confirm: 'Are you sure?' }
- invalidating all previous codes.
-
+ Deleting an account has the following effects:
+ %ul
+ %li All user content like authored issues, snippets, comments will be removed
+ - rp = current_user.personal_projects.count
+ - unless rp.zero?
+ %li #{pluralize rp, 'personal project'} will be removed and cannot be restored
+ = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
- else
- %p
- Increase your account's security by enabling two-factor authentication (2FA).
- %p
- Each time you log in you’ll be required to provide your username and
- password as usual, plus a randomly-generated code from your phone.
-
- .form-actions
- = link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success'
-
- - if button_based_providers.any?
- .panel.panel-default
- .panel-heading
- Connected Accounts
- .panel-body
- .oauth-buttons.append-bottom-10
- %p Click on icon to activate signin with one of the following services
- - button_based_providers.each do |provider|
- .btn-group
- = link_to provider_image_tag(provider), user_omniauth_authorize_path(provider), method: :post, class: "btn btn-lg #{'active' if auth_active?(provider)}", "data-no-turbolink" => "true"
-
- - if auth_active?(provider)
- = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'btn btn-lg' do
- = icon('close')
-
- - if current_user.can_change_username?
- .panel.panel-warning.update-username
- .panel-heading
- Change Username
- .panel-body
- = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f|
+ - if @user.solo_owned_groups.present?
%p
- Changing your username will change path to all personal projects!
- %div
- .input-group
- .input-group-addon
- = "#{root_url}u/"
- = f.text_field :username, required: true, class: 'form-control'
- &nbsp;
- .loading-gif.hide
- %p
- = icon('spinner spin')
- Saving new username
- .form-actions
- = f.submit 'Save username', class: "btn btn-warning"
-
- - if signup_enabled?
- .panel.panel-danger.remove-account
- .panel-heading
- Remove account
- .panel-body
- - if @user.can_be_removed?
- %p Deleting an account has the following effects:
- %ul
- %li All user content like authored issues, snippets, comments will be removed
- - rp = current_user.personal_projects.count
- - unless rp.zero?
- %li #{pluralize rp, 'personal project'} will be removed and cannot be restored
- .form-actions
- = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
- - else
- - if @user.solo_owned_groups.present?
- %p
- Your account is currently an owner in these groups:
- %strong #{@user.solo_owned_groups.map(&:name).join(', ')}
- %p
- You must transfer ownership or delete these groups before you can delete your account.
+ Your account is currently an owner in these groups:
+ %strong #{@user.solo_owned_groups.map(&:name).join(', ')}
+ %p
+ You must transfer ownership or delete these groups before you can delete your account.
+.append-bottom-default