diff --git a/Gemfile b/Gemfile index 7d680ba798a62..8289387e45f6d 100644 --- a/Gemfile +++ b/Gemfile @@ -39,6 +39,8 @@ end gem 'net-ldap', '~> 0.17' gem 'omniauth-cas', '~> 2.0' +gem 'omniauth-github', '~> 1.4' +gem 'omniauth-gitlab', '~> 1.0.2' gem 'omniauth-saml', '~> 1.10' gem 'gitlab-omniauth-openid-connect', '~>0.10.0', require: 'omniauth_openid_connect' gem 'omniauth', '~> 1.9' diff --git a/Gemfile.lock b/Gemfile.lock index 3de14856da185..db7724a34af4c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -418,6 +418,7 @@ GEM minitest (5.16.3) msgpack (1.5.4) multi_json (1.15.0) + multi_xml (0.6.0) multipart-post (2.1.1) net-ldap (0.17.1) net-scp (4.0.0.rc1) @@ -432,6 +433,12 @@ GEM concurrent-ruby (~> 1.0, >= 1.0.2) sidekiq (>= 3.5) statsd-ruby (~> 1.4, >= 1.4.0) + oauth2 (1.4.7) + faraday (>= 0.8, < 2.0) + jwt (>= 1.0, < 3.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) oj (3.13.21) omniauth (1.9.2) hashie (>= 3.4.6) @@ -440,6 +447,15 @@ GEM addressable (~> 2.3) nokogiri (~> 1.5) omniauth (~> 1.2) + omniauth-github (1.4.0) + omniauth (~> 1.5) + omniauth-oauth2 (>= 1.4.0, < 2.0) + omniauth-gitlab (1.0.4) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.0) + omniauth-oauth2 (1.7.2) + oauth2 (~> 1.4) + omniauth (>= 1.9, < 3) omniauth-rails_csrf_protection (0.1.2) actionpack (>= 4.2) omniauth (>= 1.3.1) @@ -819,6 +835,8 @@ DEPENDENCIES oj (~> 3.13) omniauth (~> 1.9) omniauth-cas (~> 2.0) + omniauth-github (~> 1.4) + omniauth-gitlab (~> 1.0.2) omniauth-rails_csrf_protection (~> 0.1) omniauth-saml (~> 1.10) ox (~> 2.14) diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb index f9cf6d6551f68..5e6d8042071b1 100644 --- a/app/controllers/auth/omniauth_callbacks_controller.rb +++ b/app/controllers/auth/omniauth_callbacks_controller.rb @@ -7,6 +7,12 @@ def self.provides_callback_for(provider) define_method provider do @user = User.find_for_oauth(request.env['omniauth.auth'], current_user) + if @user.nil? + redirect_to new_user_registration_url + set_flash_message(:alert, 'should_register_before_auth_login', scope: 'devise.failure') + return + end + if @user.persisted? LoginActivity.create( user: @user, diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index e47b1d0c01a0f..9d8d755cf9617 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -94,6 +94,19 @@ outline: 0 !important; } + &.button-primary, + &.button-alternative, + &.button-secondary, + &.button-alternative-2, + &.button-github, + &.button-gitlab { + font-size: 16px; + line-height: 36px; + height: auto; + text-transform: none; + padding: 4px 16px; + } + &.button-alternative { color: $inverted-text-color; background: $ui-primary-color; @@ -154,6 +167,28 @@ } } + &.button-github { + color: $white; + background: #24292f; + + &:active, + &:focus, + &:hover { + background-color: lighten(#24292f, 4%); + } + } + + &.button-gitlab { + color: $white; + background: #fc6d27; + + &:active, + &:focus, + &:hover { + background-color: darken(#fc6d27, 4%); + } + } + &.button--block { display: block; width: 100%; diff --git a/app/models/concerns/omniauthable.rb b/app/models/concerns/omniauthable.rb index a90d5d888a5aa..5be9bde1ad65c 100644 --- a/app/models/concerns/omniauthable.rb +++ b/app/models/concerns/omniauthable.rb @@ -31,6 +31,8 @@ def find_for_oauth(auth, signed_in_resource = nil) user = signed_in_resource || identity.user user ||= create_for_oauth(auth) + return nil if user.nil? + if identity.user.nil? identity.user = user identity.save! @@ -48,15 +50,18 @@ def create_for_oauth(auth) assume_verified = strategy&.security&.assume_email_is_verified email_is_verified = auth.info.verified || auth.info.verified_email || auth.info.email_verified || assume_verified email = auth.info.verified_email || auth.info.email + # email = nil unless email_is_verified or trusted_auth_provider(auth) - user = User.find_by(email: email) if email_is_verified + user = User.find_by(email: email) if email_is_verified or trusted_auth_provider(auth) return user unless user.nil? + return nil if is_registration_limited + user = User.new(user_params_from_auth(email, auth)) user.account.avatar_remote_url = auth.info.image if /\A#{URI::DEFAULT_PARSER.make_regexp(%w(http https))}\z/.match?(auth.info.image) - user.skip_confirmation! if email_is_verified + user.skip_confirmation! if email_is_verified or trusted_auth_provider(auth) user.save! user end @@ -69,19 +74,31 @@ def user_params_from_auth(email, auth) agreement: true, external: true, account_attributes: { - username: ensure_unique_username(ensure_valid_username(auth.uid)), - display_name: auth.info.full_name || auth.info.name || [auth.info.first_name, auth.info.last_name].join(' '), + username: ensure_unique_username(auth), + display_name: trusted_auth_provider_display_name(auth) || + auth.info.full_name || auth.info.name || [auth.info.first_name, auth.info.last_name].join(' '), }, } end - def ensure_unique_username(starting_username) - username = starting_username + def ensure_unique_username(auth) + username = auth.uid + auth_provideded_username = nil i = 0 + if auth.provider == 'github' + auth_provideded_username = auth.info.nickname + elsif auth.provider == 'gitlab' + auth_provideded_username = auth.info.username + end + + username = auth_provideded_username unless auth_provideded_username.empty? + + username = ensure_valid_username(username) + while Account.exists?(username: username, domain: nil) i += 1 - username = "#{starting_username}_#{i}" + username = "#{auth_provideded_username || auth.uid}_#{i}" end username @@ -93,5 +110,29 @@ def ensure_valid_username(starting_username) validated_username = temp_username.truncate(30, omission: '') validated_username end + + def trusted_auth_provider(auth) + auth.provider == 'github' || auth.provider == 'gitlab' + end + + def trusted_auth_provider_display_name(auth) + if auth.provider == 'github' + return auth.info.name + elsif auth.provider == 'gitlab' + return auth.info.name + end + + return nil + end + + def is_registration_limited() + if ENV['AUTO_REGISTRATION_WITH_AUTH_PROVIDERS'] == 'true' + return false + elsif Setting.registrations_mode != 'open' + return true + else + return false + end + end end end diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index f0167014676c9..9908b7d8e92d8 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -101,4 +101,14 @@ oidc_options[:security][:assume_email_is_verified] = ENV['OIDC_SECURITY_ASSUME_EMAIL_IS_VERIFIED'] == 'true' #OPTIONAL config.omniauth :openid_connect, oidc_options end + + # Github strategy + if ENV['GITHUB_OAUTH2_ENABLED'] == 'true' + config.omniauth :github, ENV['GITHUB_OAUTH2_KEY'], ENV['GITHUB_OAUTH2_SECRET'], scope: 'user.info' + end + + # Gitlab strategy + if ENV['GITLAB_OAUTH2_ENABLED'] == 'true' + config.omniauth :gitlab, ENV['GITLAB_OAUTH2_KEY'], ENV['GITLAB_OAUTH2_SECRET'], scope: 'read_user' + end end diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml index 458fa6d7596e5..3b2ac750e7d76 100644 --- a/config/locales/devise.en.yml +++ b/config/locales/devise.en.yml @@ -16,6 +16,7 @@ en: timeout: Your session expired. Please sign in again to continue. unauthenticated: You need to sign in or sign up before continuing. unconfirmed: You have to confirm your email address before continuing. + should_register_before_auth_login: You need to sign up before continuing. mailer: confirmation_instructions: action: Verify email address diff --git a/config/locales/devise.zh-CN.yml b/config/locales/devise.zh-CN.yml index e2f7bafd1d107..aae09e9e0a4ba 100644 --- a/config/locales/devise.zh-CN.yml +++ b/config/locales/devise.zh-CN.yml @@ -16,6 +16,7 @@ zh-CN: timeout: 你的会话已过期。请重新登录再继续操作。 unauthenticated: 继续操作前请注册或者登录。 unconfirmed: 你必须先确认你的电子邮件地址才能继续。 + should_register_before_auth_login: 请先注册。 mailer: confirmation_instructions: action: 验证电子邮件地址 diff --git a/config/locales/devise.zh-HK.yml b/config/locales/devise.zh-HK.yml index 2d9e8ddeada10..ef9aaac97b98a 100644 --- a/config/locales/devise.zh-HK.yml +++ b/config/locales/devise.zh-HK.yml @@ -16,6 +16,7 @@ zh-HK: timeout: 你的登入階段已經過期,請重新登入以繼續使用。 unauthenticated: 你必須先登入或登記,以繼續使用。 unconfirmed: 你必須先確認電郵地址,繼續使用。 + should_register_before_auth_login: 你必須先登記,以繼續使用。 mailer: confirmation_instructions: action: 驗證電子郵件地址 diff --git a/config/locales/devise.zh-TW.yml b/config/locales/devise.zh-TW.yml index 0d9e6a56ad792..2b7cf9f9a867b 100644 --- a/config/locales/devise.zh-TW.yml +++ b/config/locales/devise.zh-TW.yml @@ -16,6 +16,7 @@ zh-TW: timeout: 登入階段逾時。請重新登入以繼續。 unauthenticated: 您必須先登入或註冊才能繼續使用。 unconfirmed: 您必須先確認電子信箱才能繼續使用。 + should_register_before_auth_login: 您必須先註冊才能繼續使用。 mailer: confirmation_instructions: action: 驗證電子信箱地址 diff --git a/config/locales/en.yml b/config/locales/en.yml index a3046eb8ef827..4b3717efe587d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1020,6 +1020,8 @@ en: providers: cas: CAS saml: SAML + github: GitHub + gitlab: GitLab register: Sign up registration_closed: "%{instance} is not accepting new members" resend_confirmation: Resend confirmation instructions