Class: ApiKey

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
app/models/api_key.rb

Defined Under Namespace

Classes: KeyAccessError

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.hash_key(key) ⇒ Object



98
99
100
# File 'app/models/api_key.rb', line 98

def self.hash_key(key)
  Digest::SHA256.hexdigest key
end

.last_used_epochObject



43
44
45
# File 'app/models/api_key.rb', line 43

def self.last_used_epoch
  SiteSetting.api_key_last_used_epoch.presence
end

.revoke_max_life_keys!Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'app/models/api_key.rb', line 74

def self.revoke_max_life_keys!
  return if SiteSetting.revoke_api_keys_maxlife_days == 0

  revoke_days_ago = SiteSetting.revoke_api_keys_maxlife_days.days.ago
  to_revoke = ApiKey.active.where("created_at < ?", revoke_days_ago)

  to_revoke.find_each do |api_key|
    ApiKey.transaction do
      api_key.update!(revoked_at: Time.zone.now)

      StaffActionLogger.new(Discourse.system_user).log_api_key(
        api_key,
        UserHistory.actions[:api_key_update],
        changes: api_key.saved_changes,
        context:
          I18n.t(
            "staff_action_logs.api_key.automatic_revoked_max_life",
            count: SiteSetting.revoke_api_keys_maxlife_days,
          ),
      )
    end
  end
end

.revoke_unused_keys!Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'app/models/api_key.rb', line 47

def self.revoke_unused_keys!
  return if SiteSetting.revoke_api_keys_unused_days == 0 # Never expire keys
  to_revoke =
    active.where(
      "GREATEST(last_used_at, created_at, updated_at, :epoch) < :threshold",
      epoch: last_used_epoch,
      threshold: SiteSetting.revoke_api_keys_unused_days.days.ago,
    )

  to_revoke.find_each do |api_key|
    ApiKey.transaction do
      api_key.update!(revoked_at: Time.zone.now)

      StaffActionLogger.new(Discourse.system_user).log_api_key(
        api_key,
        UserHistory.actions[:api_key_update],
        changes: api_key.saved_changes,
        context:
          I18n.t(
            "staff_action_logs.api_key.automatic_revoked",
            count: SiteSetting.revoke_api_keys_unused_days,
          ),
      )
    end
  end
end

Instance Method Details

#generate_keyObject



24
25
26
27
28
29
30
# File 'app/models/api_key.rb', line 24

def generate_key
  if !self.key_hash
    @key ||= SecureRandom.hex(32) # Not saved to DB
    self.truncated_key = key[0..3]
    self.key_hash = ApiKey.hash_key(key)
  end
end

#keyObject



32
33
34
35
36
37
# File 'app/models/api_key.rb', line 32

def key
  unless key_available?
    raise KeyAccessError.new "API key is only accessible immediately after creation"
  end
  @key
end

#key_available?Boolean

Returns:

  • (Boolean)


39
40
41
# File 'app/models/api_key.rb', line 39

def key_available?
  @key.present?
end

#request_allowed?(env) ⇒ Boolean

Returns:

  • (Boolean)


102
103
104
105
106
107
108
109
# File 'app/models/api_key.rb', line 102

def request_allowed?(env)
  if allowed_ips.present? && allowed_ips.none? { |ip| ip.include?(Rack::Request.new(env).ip) }
    return false
  end
  return true if RouteMatcher.new(methods: :get, actions: "session#scopes").match?(env: env)

  api_key_scopes.blank? || api_key_scopes.any? { |s| s.permits?(env) }
end

#update_last_used!(now = Time.zone.now) ⇒ Object



111
112
113
114
115
116
# File 'app/models/api_key.rb', line 111

def update_last_used!(now = Time.zone.now)
  return if last_used_at && (last_used_at > 1.minute.ago)

  # using update_column to avoid the AR transaction
  update_column(:last_used_at, now)
end