diff --git a/Gemfile b/Gemfile index c5f4f2ab6..c01570e41 100644 --- a/Gemfile +++ b/Gemfile @@ -118,6 +118,10 @@ gem 'deep_cloneable', '~> 3.0.0' # oauth2 gem 'omniauth', '~> 1.9.0' gem 'omniauth-oauth2', '~> 1.6.0' +gem "omniauth-github" +gem "omniauth-rails_csrf_protection" +gem 'omniauth-gitee', '~> 1.0.0' +gem "omniauth-wechat-oauth2" # global var gem 'request_store' diff --git a/app/controllers/concerns/login_helper.rb b/app/controllers/concerns/login_helper.rb index 86ab175ef..c0e8d01c0 100644 --- a/app/controllers/concerns/login_helper.rb +++ b/app/controllers/concerns/login_helper.rb @@ -11,7 +11,7 @@ module LoginHelper def set_autologin_cookie(user) token = Token.get_or_create_permanent_login_token(user, "autologin") - sync_user_token_to_trustie(user.login, token.value) + # sync_user_token_to_trustie(user.login, token.value) Rails.logger.info "###### def set_autologin_cookie and get_or_create_permanent_login_token result: #{token&.value}" cookie_options = { diff --git a/app/controllers/concerns/register_helper.rb b/app/controllers/concerns/register_helper.rb index e1b7dee24..06c005084 100644 --- a/app/controllers/concerns/register_helper.rb +++ b/app/controllers/concerns/register_helper.rb @@ -1,18 +1,21 @@ module RegisterHelper extend ActiveSupport::Concern - def autologin_register(username, email, password, platform= 'forge', need_edit_info = false) + def autologin_register(username, email, password, platform = 'forge', phone = nil, nickname =nil, need_edit_info = false) result = {message: nil, user: nil} + email = email.blank? ? "#{username}@example.org" : email user = User.new(admin: false, login: username, mail: email, type: "User") user.password = password user.platform = platform + user.phone = phone if phone.present? + user.nickname = nickname if nickname.present? if need_edit_info user.need_edit_info - else + else user.activate end - + return unless user.valid? interactor = Gitea::RegisterInteractor.call({username: username, email: email, password: password}) @@ -67,7 +70,7 @@ module RegisterHelper user.password = params[:password] user.mail = params[:email] - if user.save! + if user.save! sync_params = { password: params[:password].to_s, email: params[:email], @@ -75,9 +78,9 @@ module RegisterHelper new_name: params[:username], source_id: 0 } - + interactor = Gitea::User::UpdateInteractor.call(before_login, sync_params) - if interactor.success? + if interactor.success? result[:user] = user else result[:message] = '用户同步Gitea失败!' diff --git a/app/controllers/oauth/base_controller.rb b/app/controllers/oauth/base_controller.rb index 6956c9ce9..6e651d5d7 100644 --- a/app/controllers/oauth/base_controller.rb +++ b/app/controllers/oauth/base_controller.rb @@ -3,6 +3,7 @@ class Oauth::BaseController < ActionController::Base include LoginHelper include ControllerRescueHandler include LoggerHelper + include RegisterHelper # include LaboratoryHelper skip_before_action :verify_authenticity_token @@ -15,11 +16,11 @@ class Oauth::BaseController < ActionController::Base def tip_exception(status = -1, message) raise Educoder::TipException.new(status, message) end - + def tip_show_exception(status = -2, message) raise Educoder::TipException.new(status, message) end - + def tip_show(exception) uid_logger("Tip show status is #{exception.status}, message is #{exception.message}") render json: exception.tip_json @@ -35,7 +36,7 @@ class Oauth::BaseController < ActionController::Base end def auth_hash - Rails.logger.info("[OAuth2] omniauth.auth -> #{request.env['omniauth.auth'].inspect}") + # Rails.logger.info("[OAuth2] omniauth.auth -> #{request.env['omniauth.auth'].inspect}") request.env['omniauth.auth'] end diff --git a/app/controllers/oauth/callbacks_controller.rb b/app/controllers/oauth/callbacks_controller.rb new file mode 100644 index 000000000..513563d23 --- /dev/null +++ b/app/controllers/oauth/callbacks_controller.rb @@ -0,0 +1,65 @@ +class Oauth::CallbacksController < Oauth::BaseController + def create + process_callback + rescue Exception => e + tip_exception("授权失败") + end + + private + + def config_providers + config = Rails.application.config_for(:configuration) + config.dig("oauth").keys + end + + # QQ: {"ret":0,"msg":"","is_lost":0,"nickname":"颜值不算太高","gender":"男","gender_type":1,"province":"","city":"","year":"2013","constellation":"","figureurl":"http://qzapp.qlogo.cn/qzapp/101508858/0F860F4B329768F47B22341C5FD9089C/30","figureurl_1":"http://qzapp.qlogo.cn/qzapp/101508858/0F860F4B329768F47B22341C5FD9089C/50","figureurl_2":"http://qzapp.qlogo.cn/qzapp/101508858/0F860F4B329768F47B22341C5FD9089C/100","figureurl_qq_1":"http://thirdqq.qlogo.cn/g?b=oidb\u0026k=My3segFVHFqVmauibJQUltw\u0026s=40\u0026t=1568887757","figureurl_qq_2":"http://thirdqq.qlogo.cn/g?b=oidb\u0026k=My3segFVHFqVmauibJQUltw\u0026s=100\u0026t=1568887757","figureurl_qq":"http://thirdqq.qlogo.cn/g?b=oidb\u0026k=My3segFVHFqVmauibJQUltw\u0026s=140\u0026t=1568887757","figureurl_type":"1","is_yellow_vip":"0","vip":"0","yellow_vip_level":"0","level":"0","is_yellow_year_vip":"0"} + def process_callback + Rails.logger.info("[OAuth2] omniauth.auth -> #{request.env['omniauth.auth'].inspect}") + if auth_hash.blank? + redirect_to("/login") && return + end + + new_user = false + platform = auth_hash[:provider] + uid = auth_hash[:uid] + mail = auth_hash.info.email || nil + nickname = ["gitee", "github"].include?(platform) ? auth_hash.info.name : auth_hash.info.nickname + + open_user = "OpenUsers::#{platform.to_s.capitalize}".constantize.find_by(uid: uid) + if open_user.present? && open_user.user.present? + successful_authentication(open_user.user) + else + if current_user.blank? || !current_user.logged? + has_user = User.find_by(mail: mail) + if has_user.present? + "OpenUsers::#{platform.to_s.capitalize}".constantize.create!(user_id: has_user.id, uid: uid, extra: auth_hash.extra) + successful_authentication(has_user) + else + new_user = true + login = build_login_name(platform, auth_hash.info.nickname) + mail = "#{login}@example.org" if mail.blank? + reg_result = autologin_register(login, mail, "Ec#{login}2022#", platform, nil, nickname) + Rails.logger.info("[OAuth2] omniauth.auth [reg_result] #{reg_result} ") + if reg_result[:message].blank? + open_user = "OpenUsers::#{platform.to_s.capitalize}".constantize.create!(user_id: reg_result[:user][:id], uid: uid, extra: auth_hash.extra) + successful_authentication(open_user.user) + else + tip_exception(reg_result.present? ? reg_result[:message] : "授权失败") + end + end + else + "OpenUsers::#{platform.to_s.capitalize}".constantize.create!(user: current_user, uid: login, extra: auth_hash.extra) + end + end + redirect_to root_path(new_user: new_user) + end + + # gitee,github nickname=login,如果系统未占用保留原用户名 + def build_login_name(provider, nickname) + if ["gitee", "github"].include?(provider) && User.find_by(login: nickname).blank? + nickname + else + User.generate_user_login('p') + end + end +end \ No newline at end of file diff --git a/app/models/open_users/gitee.rb b/app/models/open_users/gitee.rb new file mode 100644 index 000000000..a743eb72c --- /dev/null +++ b/app/models/open_users/gitee.rb @@ -0,0 +1,27 @@ +# == Schema Information +# +# Table name: open_users +# +# id :integer not null, primary key +# user_id :integer +# type :string(255) +# uid :string(255) +# created_at :datetime not null +# updated_at :datetime not null +# extra :text(65535) +# +# Indexes +# +# index_open_users_on_type_and_uid (type,uid) UNIQUE +# index_open_users_on_user_id (user_id) +# + +class OpenUsers::Gitee < OpenUser + def nickname + extra&.[]('nickname') + end + + def en_type + 'gitee' + end +end diff --git a/app/models/open_users/github.rb b/app/models/open_users/github.rb new file mode 100644 index 000000000..03c79ba58 --- /dev/null +++ b/app/models/open_users/github.rb @@ -0,0 +1,27 @@ +# == Schema Information +# +# Table name: open_users +# +# id :integer not null, primary key +# user_id :integer +# type :string(255) +# uid :string(255) +# created_at :datetime not null +# updated_at :datetime not null +# extra :text(65535) +# +# Indexes +# +# index_open_users_on_type_and_uid (type,uid) UNIQUE +# index_open_users_on_user_id (user_id) +# + +class OpenUsers::Github < OpenUser + def nickname + extra&.[]('name') + end + + def en_type + 'github' + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 41e932a03..9fa8e331b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -114,7 +114,7 @@ class User < Owner # trustie: 来自Trustie平台 # forge: 平台本身注册的用户 # military: 军科的用户 - enumerize :platform, in: [:forge, :educoder, :trustie, :military], default: :forge, scope: :shallow + enumerize :platform, in: [:forge, :educoder, :trustie, :military, :github, :gitee, :qq, :wechat], default: :forge, scope: :shallow belongs_to :laboratory, optional: true has_one :user_extension, dependent: :destroy @@ -791,6 +791,15 @@ class User < Owner login end + # 生成数字账号 + CODES = %W(0 1 2 3 4 5 6 7 8 9) + def self.generate_user_login type + code = CODES.sample(8).join + code = type + code.to_s + return User.generate_user_login(type) if User.where(login: code).present? + code + end + def bind_open_user?(type) case type when 'wechat' then wechat_open_user.present? diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index 3eed48c20..543b2249f 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -1,21 +1,27 @@ -OmniAuth.config.add_camelization 'qq', 'QQ' +config = Rails.application.config_for(:configuration) + +OmniAuth.config.add_camelization 'qq', 'QQ' if config.dig("oauth", "qq") +OmniAuth.config.add_camelization 'github', 'GitHub' if config.dig("oauth", "github") +OmniAuth.config.add_camelization 'gitee', 'Gitee' if config.dig("oauth", "gitee") +OmniAuth.config.add_camelization 'wechat', 'Wechat' if config.dig("oauth", "wechat") OmniAuth.config.logger = Rails.logger +OmniAuth.config.before_request_phase = nil +OmniAuth.config.before_callback_phase = nil OmniAuth.config.on_failure = Proc.new { |env| OmniAuth::FailureEndpoint.new(env).redirect_to_failure } -oauth_config = {} -begin - config = Rails.application.config_for(:configuration) - oauth_config = config.dig('oauth', 'qq') - raise 'oauth qq config missing' if oauth_config.blank? -rescue => ex - raise ex if Rails.env.production? - - puts %Q{\033[33m [warning] qq oauth config or configuration.yml missing, - please add it or execute 'cp config/configuration.yml.example config/configuration.yml' \033[0m} -end - Rails.application.config.middleware.use OmniAuth::Builder do - provider :qq, oauth_config['appid'], oauth_config['secret'], { provider_ignores_state: true } + if config.dig("oauth", "qq") + provider :qq, config.dig("oauth", "qq", "appid"), config.dig("oauth", "github", "secret"), { provider_ignores_state: true } + end + if config.dig("oauth", "github").present? + provider :github, config.dig("oauth", "github", "appid"), config.dig("oauth", "github", "secret"), { provider_ignores_state: true, scope: "user:email" } + end + if config.dig("oauth", "gitee").present? + provider :gitee, config.dig("oauth", "gitee", "appid"), config.dig("oauth", "gitee", "secret"), { provider_ignores_state: true, scope: "user_info emails" } + end + if config.dig("oauth", "wechat").present? + provider :gitee, config.dig("oauth", "wechat", "appid"), config.dig("oauth", "wechat", "secret"), { provider_ignores_state: true, scope: "snsapi_login" } + end end diff --git a/config/initializers/per_form_csrf_tokens.rb b/config/initializers/per_form_csrf_tokens.rb new file mode 100644 index 000000000..5afb75e59 --- /dev/null +++ b/config/initializers/per_form_csrf_tokens.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +# Be sure to restart your server when you modify this file. + +# Enable per-form CSRF tokens. +# Rails.application.config.action_controller.per_form_csrf_tokens = true +# Rails.application.config.action_controller.forgery_protection_origin_check = true \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index f47bd5aba..0efac72c0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -23,6 +23,7 @@ Rails.application.routes.draw do get 'auth/qq/callback', to: 'oauth/qq#create' get 'auth/failure', to: 'oauth/base#auth_failure' get 'auth/cas/callback', to: 'oauth/cas#create' + get 'auth/:provider/callback', to: 'oauth/callbacks#create' get 'oauth/bind', to: 'oauth/educoder#bind' get 'oauth/register', to: 'oauth#register'