# frozen_string_literal: true # # ronin-db-activerecord - ActiveRecord backend for the Ronin Database. # # Copyright (c) 2022-2024 Hal Brodigan (postmodern.mod3 at gmail.com) # # ronin-db-activerecord is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # ronin-db-activerecord is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with ronin-db-activerecord. If not, see . # require 'ronin/db/model' require 'ronin/db/model/importable' require 'active_record' module Ronin module DB # # Represents Credentials used to access services or websites. # class Credential < ActiveRecord::Base include Model include Model::Importable # @!attribute [rw] id # Primary key of the credential. # # @return [Integer] attribute :id, :integer # @!attribute [rw] user_name # User name of the credential. # # @return [UserName, nil] belongs_to :user_name, optional: true validates :user_name, presence: true, if: ->(cred) { cred.email_address.nil? } # @!attribute [rw] email_address # The optional email address associated with the Credential # # @return [EmailAddress, nil] belongs_to :email_address, optional: true validates :email_address, presence: true, if: ->(cred) { cred.user_name.nil? } # @!attribute [rw] password # Password of the credential. # # @return [Password] belongs_to :password, required: true # @!attribute [rw] service_credentials # The service credentials. # # @return [Array] has_many :service_credentials, dependent: :destroy # @!attribute [rw] open_ports # The open ports that accept this credential pair. # # @return [Array] has_many :open_ports, through: :service_credentials # @!attribute [rw] web_credentials # The Web credentials. # # @return [Array] has_many :web_credentials, dependent: :destroy # @!attribute [rw] urls # The URLs that accept this credential pair. # # @return [Array] has_many :urls, through: :web_credentials # @!attribute [rw] notes # The associated notes. # # @return [Array] # # @since 0.2.0 has_many :notes, dependent: :destroy # # Searches for all credentials for a specific user. # # @param [String] name # The name of the user. # # @return [Array] # The credentials for the user. # # @api public # def self.for_user(name) joins(:user_name).where(user_name: {name: name}) end # # Searches all web credentials that are associated with an # email address. # # @param [String] email # The email address to search for. # # @return [Array] # The web credentials associated with the email address. # # @raise [ArgumentError] # The given email address was not a valid email address. # # @api public # def self.with_email_address(email) unless email.include?('@') raise(ArgumentError,"invalid email address #{email.inspect}") end user, domain = email.split('@',2) return joins(email_address: [:user_name, :host_name]).where( email_address: { ronin_user_names: {name: user}, ronin_host_names: {name: domain} } ) end # # Searches for all credentials with a common password. # # @param [String] password # The password to search for. # # @return [Array] # The credentials with the common password. # # @api public # def self.with_password(password) joins(:password).where(password: {plain_text: password}) end # # Looks up the given credential. # # @param [String] cred # The credential String # (ex: `user:password` or `user@example.com:password`). # # @return [Credential, nil] # The found credential. # def self.lookup(cred) unless cred.include?(':') raise(ArgumentError,"credential must be of the form user:password or email:password: #{cred.inspect}") end user_or_email, password = cred.split(':',2) query = if user_or_email.include?('@') with_email_address(user_or_email) else for_user(user_or_email) end query.with_password(password) return query.first end # # Imports the given credential. # # @param [String] cred # The credential String # (ex: `user:password` or `user@example.com:password`). # # @return [Credential] # The imported credential. # def self.import(cred) unless cred.include?(':') raise(ArgumentError,"credential must be of the form user:password or email:password: #{cred.inspect}") end user_or_email, password = cred.split(':',2) if user_or_email.include?('@') create( email_address: EmailAddress.find_or_import(user_or_email), password: Password.find_or_import(password) ) else create( user_name: UserName.find_or_import(user_or_email), password: Password.find_or_import(password) ) end end # # The user the credential belongs to. # # @return [String] # The user name. # # @api public # def user self.user_name.name if self.user_name end # # The clear-text password of the credential. # # @return [String] # The clear-text password. # # @api public # def plain_text self.password.plain_text if self.password end # # Converts the credentials to a String. # # @return [String] # The user name and the password. # # @api public # def to_s "#{self.user_name}:#{self.password}" end end end end require 'ronin/db/user_name' require 'ronin/db/email_address' require 'ronin/db/password' require 'ronin/db/service_credential' require 'ronin/db/web_credential' require 'ronin/db/note'