Skip to content

Commit

Permalink
Merge branch 'master' into stable
Browse files Browse the repository at this point in the history
  • Loading branch information
mtnstar committed Jul 19, 2023
2 parents 7cc721f + 6b144d0 commit 40af068
Show file tree
Hide file tree
Showing 67 changed files with 996 additions and 230 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5.1
5.2
28 changes: 27 additions & 1 deletion app/controllers/api/teams_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Api::TeamsController < ApiController
# GET /api/teams
def index
if team_id.present?
authorize Team.find(team_id), :team_member?
fetch_team
elsif params['only_teammember_user_id'].present?
authorize ::Team, :only_teammember?
else
Expand All @@ -28,6 +28,32 @@ def index

private

def fetch_team
@team = Team.find(team_id)
authorize @team, :team_member?

unless already_recrypted?(@team)
receive_transferred_encryptables(@team) if team.personal_team?
recrypt(@team)
end
end

def receive_transferred_encryptables(team)
team.encryptables.select(&:transferred?).each do |encryptable|
team_password = decrypted_team_password(team)
EncryptableTransfer.new.receive(encryptable, session[:private_key], team_password)
end
end

def already_recrypted?(team)
Crypto::Symmetric.latest_algorithm?(team)
end

def recrypt(team)
private_key = session[:private_key]
Crypto::Symmetric::Recrypt.new(current_user, team, private_key).perform
end

def build_entry
instance_variable_set(:"@#{ivar_name}",
Team::Shared.create(current_user, model_params))
Expand Down
69 changes: 51 additions & 18 deletions app/models/encryptable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
class Encryptable < ApplicationRecord
serialize :encrypted_data, ::EncryptedData

class_attribute :used_encrypted_attrs

attr_readonly :type
validates :type, presence: true

Expand All @@ -26,12 +28,25 @@ class Encryptable < ApplicationRecord
validates :name, presence: true
validates :description, length: { maximum: 4000 }

def encrypt(_team_password)
raise 'implement in subclass'
def encrypt(team_password, encryption_algorithm = nil)
used_encrypted_attrs.each do |a|
encrypt_attr(a, team_password, encryption_algorithm)
end
end

def decrypt(_team_password)
raise 'implement in subclass'
def decrypt(team_password)
encrypted_attrs = encrypted_data.used_attributes
encrypted_attrs.each do |a|
decrypt_attr(a, team_password)
end
end

def recrypt(team_password, new_team_password, new_encryption_class)
decrypt(team_password)

@new_encryption_class = new_encryption_class
encrypt(new_team_password)
save!
end

def self.policy_class
Expand Down Expand Up @@ -75,30 +90,27 @@ def used_encrypted_data_attrs

private

def encrypt_attr(attr, team_password)
def encrypt_attr(attr, team_password, encryption_algorithm = nil)
cleartext_value = send(:"cleartext_#{attr}")

encrypted_value = if cleartext_value.presence
Crypto::Symmetric::Aes256.encrypt(cleartext_value, team_password)
end
encrypted_value =
encrypt_with_encryption_class(cleartext_value, team_password, encryption_algorithm)

return if transferred? && encrypted_value.blank?

build_encrypted_data(attr, encrypted_value)
end

def build_encrypted_data(attr, encrypted_value)
attr_label = cleartext_custom_attr_label if attr == :custom_attr
encrypted_data.[]=(
attr, **{ label: attr_label, data: encrypted_value, iv: nil }
)
encrypted_data.[]=(attr, **{
label: attr_label,
data: encrypted_value[:data],
iv: encrypted_value[:iv]
})
end

def decrypt_attr(attr, team_password)
encrypted_value = encrypted_data[attr].try(:[], :data)
encrypted_value = encrypted_value_hash(attr)

cleartext_value = if encrypted_value
Crypto::Symmetric::Aes256.decrypt(encrypted_value, team_password)
cleartext_value = if encrypted_value[:data].present?
encryption_class.decrypt(encrypted_value, team_password)
end

if attr == :custom_attr
Expand All @@ -108,6 +120,27 @@ def decrypt_attr(attr, team_password)
instance_variable_set("@cleartext_#{attr}", cleartext_value)
end

def encrypted_value_hash(attr)
data_attr = encrypted_data[attr]

data = data_attr.try(:[], :data)
iv = data_attr.try(:[], :iv)
{ data: data, iv: iv }
end

def encrypt_with_encryption_class(cleartext_value, team_password, encryption_algorithm)
if cleartext_value.presence
@new_encryption_class = encryption_algorithm unless encryption_algorithm.nil?
encryption_class.encrypt(cleartext_value, team_password)
else
{ data: nil, iv: nil }
end
end

def encryption_class
@new_encryption_class || team.encryption_class
end

def assert_human_receiver?
unless User.find(receiver_id).is_a?(User::Human)
errors.add(:receiver_id, 'Must be a human user')
Expand Down
21 changes: 2 additions & 19 deletions app/models/encryptable/credentials.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ class Encryptable::Credentials < Encryptable
attr_accessor :cleartext_password, :cleartext_username, :cleartext_token, :cleartext_pin,
:cleartext_email, :cleartext_custom_attr_label, :cleartext_custom_attr

self.used_encrypted_attrs = [:username, :password, :token, :pin, :email, :custom_attr].freeze

has_many :encryptable_files,
class_name: 'Encryptable::File',
foreign_key: :credential_id,
Expand All @@ -12,23 +14,4 @@ class Encryptable::Credentials < Encryptable
validates :name, length: { maximum: 70 }
validates :name, uniqueness: { scope: :folder }
validates :folder_id, presence: true

def decrypt(team_password)
decrypt_attr(:username, team_password)
decrypt_attr(:password, team_password)
decrypt_attr(:token, team_password)
decrypt_attr(:pin, team_password)
decrypt_attr(:email, team_password)
decrypt_attr(:custom_attr, team_password)
end

def encrypt(team_password)
encrypt_attr(:username, team_password)
encrypt_attr(:password, team_password)
encrypt_attr(:token, team_password)
encrypt_attr(:pin, team_password)
encrypt_attr(:email, team_password)
encrypt_attr(:custom_attr, team_password)
end

end
12 changes: 2 additions & 10 deletions app/models/encryptable/file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
class Encryptable::File < Encryptable
attr_accessor :cleartext_file

self.used_encrypted_attrs = [:file].freeze

belongs_to :encryptable_credential,
class_name: 'Encryptable::Credentials',
foreign_key: :credential_id
Expand All @@ -11,16 +13,6 @@ class Encryptable::File < Encryptable

validate :file_size, on: [:create, :update]

def decrypt(team_password)
decrypt_attr(:file, team_password)
end

def encrypt(team_password)
return if cleartext_file.empty?

encrypt_attr(:file, team_password)
end

def team
folder&.team || encryptable_credential.folder.team
end
Expand Down
8 changes: 6 additions & 2 deletions app/models/encrypted_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,19 @@ def []=(key, data:, iv:, label: nil)
end

def to_json(*_args)
@data.reject { |_, value| value[:data].blank? }.to_json
present_data.to_json
end

def used_attributes
@data.keys.map(&:to_s)
present_data.keys
end

private

def present_data
@data.reject { |_, value| value[:data].blank? }
end

def data_hash(iv, data, label = nil)
hash = { iv: iv, data: data }
hash[:label] = label if label
Expand Down
37 changes: 34 additions & 3 deletions app/models/team.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ class Team < ApplicationRecord
validates :name, presence: true
validates :name, length: { maximum: 40 }
validates :description, length: { maximum: 300 }
validates :encryption_algorithm,
inclusion: { in: ::Crypto::Symmetric::ALGORITHMS.keys }, allow_nil: false

after_initialize :set_encryption_algorithm, if: :new_record?

enum recrypt_state: {
failed: 0,
done: 1,
in_progress: 2
}, _prefix: :recrypt

def label
name
Expand All @@ -41,8 +51,8 @@ def teammember(user_id)
end

def decrypt_team_password(user, plaintext_private_key)
crypted_team_password = teammember(user.id).password
Crypto::Rsa.decrypt(crypted_team_password, plaintext_private_key)
encrypted_team_password = teammember(user.id).encrypted_team_password
Crypto::Rsa.decrypt(encrypted_team_password, plaintext_private_key)
end

def personal_team?
Expand All @@ -60,10 +70,31 @@ def self.policy_class
TeamPolicy
end

def encryption_class
Crypto::Symmetric::ALGORITHMS[encryption_algorithm]
end

def password_bitsize
encryption_class.password_bitsize
end

def new_team_password
encryption_class.random_key
end

def encryptables
encryptable_ids = Encryptable.joins(folder: :team).where('teams.id': id).pluck(:id)
Encryptable.where(id: encryptable_ids).or(Encryptable.where(credential_id: encryptable_ids))
end

private

def create_teammember(user, plaintext_team_password)
encrypted_team_password = Crypto::Rsa.encrypt(plaintext_team_password, user.public_key)
teammembers.create!(password: encrypted_team_password, user: user)
teammembers.create!(encrypted_team_password: encrypted_team_password, user: user)
end

def set_encryption_algorithm
self.encryption_algorithm = Crypto::Symmetric::LATEST_ALGORITHM
end
end
2 changes: 1 addition & 1 deletion app/models/team/personal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def create(owner)
team = super(name: 'personal-team', personal_owner: owner, private: true)
return team unless team.valid?

plaintext_team_password = Crypto::Symmetric::Aes256.random_key
plaintext_team_password = team.new_team_password
team.add_user(owner, plaintext_team_password)

team
Expand Down
2 changes: 1 addition & 1 deletion app/models/team/shared.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def create(creator, params)
team = super(params)
return team unless team.valid?

plaintext_team_password = Crypto::Symmetric::Aes256.random_key
plaintext_team_password = team.new_team_password
team.add_user(creator, plaintext_team_password)
unless team.private?
User::Human.admins.each do |a|
Expand Down
10 changes: 4 additions & 6 deletions app/models/teammember.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,10 @@ class Teammember < ApplicationRecord
scope :in_private_teams, (-> { joins(:team).where('teams.private' => true) })


def recrypt_team_password(user, admin, private_key)
teammember_admin = admin.teammembers.find_by(team_id: team_id)
team_password = Crypto::Rsa.decrypt(teammember_admin.
password, private_key)

self.password = Crypto::Rsa.encrypt(team_password, user.public_key)
def reset_team_password(new_team_password)
public_key = user.public_key
encrypted_team_password = Crypto::Rsa.encrypt(new_team_password, public_key)
self.encrypted_team_password = encrypted_team_password
save!
end

Expand Down
2 changes: 1 addition & 1 deletion app/models/user/human.rb
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def empower(actor, private_key)
next if t.teammember?(self)

active_teammember = t.teammembers.find_by user_id: actor.id
team_password = Crypto::Rsa.decrypt(active_teammember.password, private_key)
team_password = Crypto::Rsa.decrypt(active_teammember.encrypted_team_password, private_key)
t.add_user(self, team_password)
end
end
Expand Down
7 changes: 6 additions & 1 deletion app/serializers/team_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
class TeamSerializer < ApplicationSerializer
# To hide STI name in Frontend
type Team.name.pluralize
attributes :id, :name, :description, :private, :favourised, :deletable, :type
attributes :id, :name, :description, :private, :favourised, :deletable, :type,
:encryption_algorithm, :password_bitsize

has_many :folders, serializer: FolderSerializer

Expand All @@ -31,4 +32,8 @@ def deletable
def personal_team
object.personal_team?
end

def password_bitsize
object.encryption_class.password_bitsize
end
end
15 changes: 14 additions & 1 deletion app/utils/crypto/symmetric.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@
require 'openssl'
require 'digest/sha1'

class Crypto::Symmetric
class ::Crypto::Symmetric
class_attribute :password_bitsize

LATEST_ALGORITHM = 'AES256IV'

# Add further algorithms at the bottom
ALGORITHMS = {
AES256: ::Crypto::Symmetric::Aes256,
AES256IV: ::Crypto::Symmetric::Aes256iv
}.with_indifferent_access.freeze

class << self

Expand All @@ -18,5 +27,9 @@ def decrypt
def random_key
raise 'Implement in subclass'
end

def latest_algorithm?(entry)
LATEST_ALGORITHM == entry.encryption_algorithm
end
end
end
Loading

0 comments on commit 40af068

Please sign in to comment.