| @@ -1,6 +1,7 @@ | |||
| class AccountsController < ApplicationController | |||
| before_action :require_login, only: [:login_check, :simple_update, :change_password] | |||
| include ApplicationHelper | |||
| include AesCryptHelper | |||
| #skip_before_action :check_account, :only => [:logout] | |||
| @@ -143,7 +144,8 @@ class AccountsController < ApplicationController | |||
| user = Users::RegisterService.call(register_params) | |||
| user.mail = "#{user.login}@example.org" if user.mail.blank? | |||
| password = register_params[:password].strip | |||
| password = decrypt(register_params[:password]) rescue "" | |||
| password = password.strip | |||
| # gitea用户注册, email, username, password | |||
| interactor = Gitea::RegisterInteractor.call({username: user.login, email: user.mail, password: password}) | |||
| @@ -193,8 +195,9 @@ class AccountsController < ApplicationController | |||
| # 用户登录 | |||
| def login | |||
| Users::LoginForm.new(login_params).validate! | |||
| @user = User.try_to_login(params[:login], params[:password]) | |||
| password = decrypt(login_params[:password]) rescue "" | |||
| Users::LoginForm.new(login_params.merge!({password: password})).validate! | |||
| @user = User.try_to_login(params[:login], password) | |||
| return normal_status(-2, "错误的账号或密码") if @user.blank? | |||
| # user is already in local database | |||
| @@ -203,7 +206,7 @@ class AccountsController < ApplicationController | |||
| login_control = LimitForbidControl::UserLogin.new(@user) | |||
| return normal_status(-2, "登录密码出错已达上限,账号已被锁定,请#{login_control.forbid_expires/60}分钟后重新登录或找回密码") if login_control.forbid? | |||
| password_ok = @user.check_password?(params[:password].to_s) | |||
| password_ok = @user.check_password?(password.to_s) | |||
| unless password_ok | |||
| if login_control.remain_times-1 == 0 | |||
| normal_status(-2, "登录密码出错已达上限,账号已被锁定,请#{login_control.forbid_expires/60}分钟后重新登录或找回密码") | |||
| @@ -216,21 +219,24 @@ class AccountsController < ApplicationController | |||
| LimitForbidControl::UserLogin.new(@user).clear | |||
| successful_authentication(@user) | |||
| sync_pwd_to_gitea!(@user, {password: params[:password].to_s}) # TODO用户密码未同步 | |||
| sync_pwd_to_gitea!(@user, {password: password.to_s}) # TODO用户密码未同步 | |||
| # session[:user_id] = @user.id | |||
| end | |||
| def change_password | |||
| return render_error("两次输入的密码不一致") if params[:password].to_s != params[:new_password_repeat].to_s | |||
| password = decrypt(params[:password]) rescue "" | |||
| new_password_repeat = decrypt(params[:new_password_repeat]) rescue "" | |||
| old_password = decrypt(params[:old_password]) rescue "" | |||
| return render_error("两次输入的密码不一致") if password.to_s != new_password_repeat.to_s | |||
| @user = User.find_by(login: params[:login]) | |||
| return render_forbidden unless User.current.login == @user&.login | |||
| return render_error("此用户禁止修改密码!") if @user.id.to_i === 104691 | |||
| return render_error("未找到相关用户!") if @user.blank? | |||
| return render_error("旧密码不正确") unless @user.check_password?(params[:old_password]) | |||
| return render_error("旧密码不正确") unless @user.check_password?(old_password) | |||
| sync_params = { | |||
| password: params[:password].to_s, | |||
| password: password.to_s, | |||
| email: @user.mail, | |||
| login_name: @user.name, | |||
| source_id: 0 | |||
| @@ -238,7 +244,7 @@ class AccountsController < ApplicationController | |||
| interactor = Gitea::User::UpdateInteractor.call(@user.login, sync_params) | |||
| if interactor.success? | |||
| @user.update_attribute(:password, params[:password]) | |||
| @user.update_attribute(:password, password) | |||
| render_ok | |||
| else | |||
| render_error(interactor.error) | |||
| @@ -1,4 +1,5 @@ | |||
| class Api::V1::UsersController < Api::V1::BaseController | |||
| include AesCryptHelper | |||
| before_action :load_observe_user, except: [:check_user_id, :check_user_login] | |||
| before_action :check_auth_for_observe_user, except: [:check_user_id, :check_user_login] | |||
| @@ -53,7 +54,7 @@ class Api::V1::UsersController < Api::V1::BaseController | |||
| end | |||
| def check_password | |||
| password = params[:password] | |||
| password = decrypt(params[:password]) rescue "" | |||
| return tip_exception(-5, "8~16位密码,支持字母数字和符号") unless password =~ CustomRegexp::PASSWORD | |||
| return tip_exception(-5, "密码错误") unless @observe_user.check_password?(password) | |||
| render_ok | |||
| @@ -126,7 +127,8 @@ class Api::V1::UsersController < Api::V1::BaseController | |||
| def destroy | |||
| return tip_exception(-1, "密码不正确.") unless @observe_user.check_password?(params[:password]) | |||
| password = decrypt(params[:password]) rescue "" | |||
| return tip_exception(-1, "密码不正确.") unless @observe_user.check_password?(password) | |||
| org_ids = TeamUser.where(user_id: @observe_user.id).pluck(:organization_id) | OrganizationUser.where(user_id: @observe_user.id).pluck(:organization_id) | |||
| org_count = TeamUser.where(organization_id: org_ids).where(user_id: @observe_user.id).joins(:team).where(teams: {authorize: %w(owner)}).count | |||
| project_count = Project.where(user_id: @observe_user.id).count | |||
| @@ -1,4 +1,5 @@ | |||
| class Organizations::OrganizationsController < Organizations::BaseController | |||
| include AesCryptHelper | |||
| before_action :require_login, except: [:index, :show, :recommend, :languages] | |||
| # before_action :require_profile_completed, only: [:create] | |||
| before_action :convert_image!, only: [:create, :update] | |||
| @@ -139,7 +140,7 @@ class Organizations::OrganizationsController < Organizations::BaseController | |||
| end | |||
| def password | |||
| params.fetch(:password, "") | |||
| decrypt(params[:password]) rescue "" | |||
| end | |||
| def load_organization | |||
| @@ -1,5 +1,6 @@ | |||
| class BaseForm | |||
| include ActiveModel::Model | |||
| include AesCryptHelper | |||
| Error = Class.new(StandardError) | |||
| EmailError = Class.new(Error) | |||
| @@ -53,11 +54,15 @@ class BaseForm | |||
| end | |||
| def check_password(password) | |||
| password = decrypt(password) rescue "" | |||
| password = strip(password) | |||
| raise PasswordFormatError, "密码8~16位密码,支持字母数字和符号" unless password =~ CustomRegexp::PASSWORD | |||
| end | |||
| def check_password_confirmation(password, password_confirmation) | |||
| password = decrypt(password) rescue "" | |||
| password_confirmation = decrypt(password_confirmation) rescue "" | |||
| password = strip(password) | |||
| password_confirmation = strip(password_confirmation) | |||
| @@ -0,0 +1,45 @@ | |||
| module AesCryptHelper | |||
| AES_KEY = EduSetting.get("login_crypt_key") || '59c96c3572ab8cc1' | |||
| def encrypt(plain_text, output_encoding = 'base64') | |||
| # 将字符串密钥和IV转换为16字节的字节数组 | |||
| key = AES_KEY.byteslice(0, 16) | |||
| iv = AES_KEY.byteslice(0, 16) | |||
| # 创建并设置AES-CBC加密器 | |||
| cipher = OpenSSL::Cipher.new('AES-128-CBC') | |||
| cipher.encrypt | |||
| cipher.key = key | |||
| cipher.iv = iv | |||
| # 加密数据,并添加PKCS7填充 | |||
| encrypted_data = cipher.update(plain_text) + cipher.final | |||
| # 将加密数据转换为Base64编码 | |||
| Base64.strict_encode64(encrypted_data) | |||
| end | |||
| def decrypt(cipher_text, input_encoding = 'base64') | |||
| # 确保密钥是16字节长 | |||
| key = AES_KEY.byteslice(0, 16) # 如果密钥不足16字节,填充空格;如果超过,截断 | |||
| iv = AES_KEY.byteslice(0, 16) | |||
| decipher = OpenSSL::Cipher.new('AES-128-CBC') | |||
| decipher.decrypt | |||
| decipher.key = key | |||
| decipher.iv = iv | |||
| # 根据输入编码解码密文 | |||
| decrypted_data = case input_encoding | |||
| when 'base64' | |||
| Base64.strict_decode64(cipher_text) | |||
| else | |||
| cipher_text | |||
| end | |||
| decrypted = decipher.update(decrypted_data) + decipher.final | |||
| decrypted | |||
| end | |||
| end | |||
| @@ -9,17 +9,16 @@ class ChangeIssueStatusByMessageJob < ApplicationJob | |||
| def get_pm_issue_data(user, org, pm_project_id, issue_id) | |||
| url = URI("#{EduSetting.get("pms_server_url")}/api/pms/#{org.login}/pmsProjectIssues/#{issue_id}?pmProjectId=#{pm_project_id}") | |||
| url = "#{EduSetting.get("pms_server_url")}/api/pms/#{org.login}/pmsProjectIssues/#{issue_id}?pmProjectId=#{pm_project_id}" | |||
| headers = { | |||
| 'Cookie' => "autologin_trustie=#{Token.get_or_create_permanent_login_token(user, 'autologin')&.value}", | |||
| } | |||
| response = RestClient.get(url, headers) | |||
| https = Net::HTTP.new(url.host, url.port) | |||
| https.use_ssl = true | |||
| request = Net::HTTP::Get.new(url) | |||
| request["Cookie"] = "autologin_trustie=#{Token.get_or_create_permanent_login_token(user, 'autologin')&.value}" | |||
| response = https.request(request) | |||
| puts response.read_body | |||
| return JSON.parse(response.read_body)['code'].to_i == 200 | |||
| puts response.body | |||
| return JSON.parse(response.body)["code"].to_i == 200 | |||
| rescue | |||
| return false | |||
| end | |||
| @@ -33,6 +32,7 @@ class ChangeIssueStatusByMessageJob < ApplicationJob | |||
| issue = project.issues.issue_issue.where(project_issues_index: issue_id).where.not(id: issue_id).take || Issue.issue_issue.find_by_id(issue_id) | |||
| next unless issue.present? # issue不存在 跳过 | |||
| next if issue.project.present? && !issue.project.member?(user) # issue归属项目,用户没有修改issue的权限,跳过 | |||
| next if issue.pm_project_id.nil? && project.id.to_i != issue.project&.id.to_i | |||
| next if issue.pm_project_id.present? && !get_pm_issue_data(user, project.owner, issue.pm_project_id, issue.id) # issue是组织下工作项,不具备组织的访问权限,跳过 | |||
| issue_project = issue.project || Project.new(id: 0, user_id: 0, name: 'pm_mm', identifier: 'pm_mm', is_public:true) | |||
| @@ -1,22 +1,23 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: edu_settings | |||
| # | |||
| # id :integer not null, primary key | |||
| # name :string(255) | |||
| # value :string(255) | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # description :string(255) | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_edu_settings_on_name (name) UNIQUE | |||
| # | |||
| # == Schema Information | |||
| # | |||
| # Table name: edu_settings | |||
| # | |||
| # id :integer not null, primary key | |||
| # name :string(255) | |||
| # value :string(255) | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # description :string(255) | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_edu_settings_on_name (name) UNIQUE | |||
| # | |||
| class EduSetting < ApplicationRecord | |||
| after_commit :expire_value_cache | |||
| after_commit :expire_value_cache, on: [:create, :update] | |||
| after_commit :clear_value_cache, on: :destroy | |||
| scope :by_search, -> (keyword){ where("name LIKE :keyword OR value LIKE :keyword", keyword: "%#{strip_param(keyword)}%") unless strip_param(keyword).blank? } | |||
| @@ -25,7 +26,11 @@ class EduSetting < ApplicationRecord | |||
| end | |||
| def self.get(key) | |||
| Rails.cache.fetch(value_cache_key(key), expires_in: 1.days) do | |||
| begin | |||
| Rails.cache.fetch(value_cache_key(key), expires_in: 1.days) do | |||
| find_by_name(key.to_s)&.value | |||
| end | |||
| rescue Exception => e | |||
| find_by_name(key.to_s)&.value | |||
| end | |||
| end | |||
| @@ -41,4 +46,8 @@ class EduSetting < ApplicationRecord | |||
| def expire_value_cache | |||
| Rails.cache.write(value_cache_key, value) | |||
| end | |||
| def clear_value_cache | |||
| Rails.cache.delete(value_cache_key) | |||
| end | |||
| end | |||
| @@ -1,10 +1,11 @@ | |||
| module Accounts | |||
| class ResetPasswordService < ApplicationService | |||
| include AesCryptHelper | |||
| # login、code、password、password_confirmation | |||
| def initialize(user, params) | |||
| @user = user | |||
| @password = params[:password] | |||
| @password_confirmation = params[:password_confirmation] | |||
| @password = decrypt(params[:password]) rescue "" | |||
| @password_confirmation = decrypt(params[:password_confirmation]) rescue "" | |||
| end | |||
| def call | |||
| @@ -1,5 +1,6 @@ | |||
| class Api::V1::Users::UpdateEmailService < ApplicationService | |||
| include ActiveModel::Model | |||
| include AesCryptHelper | |||
| attr_reader :user, :token, :password, :mail, :old_mail, :code, :verify_code | |||
| attr_accessor :gitea_data | |||
| @@ -10,7 +11,7 @@ class Api::V1::Users::UpdateEmailService < ApplicationService | |||
| def initialize(user, params, token =nil) | |||
| @user = user | |||
| @token = token | |||
| @password = params[:password] | |||
| @password = decrypt(params[:password]) rescue "" | |||
| @mail = params[:email] | |||
| @old_mail = user.mail | |||
| @code = params[:code] | |||
| @@ -1,5 +1,6 @@ | |||
| class Api::V1::Users::UpdatePhoneService < ApplicationService | |||
| include ActiveModel::Model | |||
| include AesCryptHelper | |||
| attr_reader :user, :password, :phone, :code, :verify_code | |||
| @@ -8,7 +9,7 @@ class Api::V1::Users::UpdatePhoneService < ApplicationService | |||
| def initialize(user, params) | |||
| @user = user | |||
| @password = params[:password] | |||
| @password = decrypt(params[:password]) rescue "" | |||
| @phone = params[:phone] | |||
| @code = params[:code] | |||
| @verify_code = VerificationCode.where(phone: @phone, code_type: 4).last | |||
| @@ -1,8 +1,10 @@ | |||
| class Users::RegisterService < ApplicationService | |||
| include AesCryptHelper | |||
| def initialize(params) | |||
| @login = params[:login] | |||
| @namespace = params[:namespace] | |||
| @password = params[:password] | |||
| @password = decrypt(params[:password]) rescue "" | |||
| @code = params[:code] | |||
| end | |||