Class: PersonalAccessToken

Inherits:
ApplicationRecord show all
Extended by:
Gitlab::Utils::Override
Includes:
AfterCommitQueue, CreatedAtFilterable, EachBatch, Expirable, Gitlab::SQL::Pattern, PolicyActor, SafelyChangeColumnDefault, Sortable, TokenAuthenticatable
Defined in:
app/models/personal_access_token.rb

Constant Summary collapse

NOTIFICATION_INTERVALS =
{
  seven_days: 0..7,
  thirty_days: 8..30,
  sixty_days: 31..60
}.freeze
PERSONAL_TOKEN_PREFIX =
'glpat-'
MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS_BUFFERED =
400
MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS =
365

Constants included from Gitlab::SQL::Pattern

Gitlab::SQL::Pattern::MIN_CHARS_FOR_PARTIAL_MATCHING, Gitlab::SQL::Pattern::REGEX_QUOTED_TERM

Constants included from Expirable

Expirable::DAYS_TO_EXPIRE

Constants inherited from ApplicationRecord

ApplicationRecord::MAX_PLUCK

Constants included from HasCheckConstraints

HasCheckConstraints::NOT_NULL_CHECK_PATTERN

Constants included from ResetOnColumnErrors

ResetOnColumnErrors::MAX_RESET_PERIOD

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Gitlab::Utils::Override

extended, extensions, included, method_added, override, prepended, queue_verification, verify!

Methods included from PolicyActor

#access_locked?, #admin?, #alert_bot?, #automation_bot?, #blocked?, #can?, #can_access_admin_area?, #can_create_group, #can_read_all_resources?, #confirmation_required_on_sign_in?, #deactivated?, #external?, #from_ci_job_token?, #internal?, #password_expired_if_applicable?, #preferred_language, #required_terms_not_accepted?, #requires_ldap_check?, #security_bot?, #support_bot?, #try_obtain_ldap_lease

Methods included from Gitlab::SQL::Pattern

split_query_to_search_terms

Methods included from Expirable

#expired?, #expires?, #expires_soon?

Methods included from AfterCommitQueue

#run_after_commit, #run_after_commit_or_now

Methods inherited from ApplicationRecord

===, cached_column_list, #create_or_load_association, current_transaction, declarative_enum, default_select_columns, delete_all_returning, #deleted_from_database?, id_in, id_not_in, iid_in, nullable_column?, primary_key_in, #readable_by?, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, #to_ability_name, underscore, where_exists, where_not_exists, with_fast_read_statement_timeout, without_order

Methods included from Organizations::Sharding

#sharding_organization

Methods included from ResetOnColumnErrors

#reset_on_union_error, #reset_on_unknown_attribute_error

Methods included from Gitlab::SensitiveSerializableHash

#serializable_hash

Class Method Details

.max_expiration_lifetime_in_daysObject



171
172
173
174
175
176
177
# File 'app/models/personal_access_token.rb', line 171

def self.max_expiration_lifetime_in_days
  if ::Feature.enabled?(:buffered_token_expiration_limit) # rubocop:disable Gitlab/FeatureFlagWithoutActor -- Group setting but checked at user
    MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS_BUFFERED
  else
    MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS
  end
end

.notification_interval(interval) ⇒ Object



148
149
150
# File 'app/models/personal_access_token.rb', line 148

def self.notification_interval(interval)
  NOTIFICATION_INTERVALS.fetch(interval).max
end

.scope_for_notification_interval(interval, min_expires_at: nil, max_expires_at: nil) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'app/models/personal_access_token.rb', line 152

def self.scope_for_notification_interval(interval, min_expires_at: nil, max_expires_at: nil)
  interval_range = NOTIFICATION_INTERVALS.fetch(interval).minmax
  min_expiry_date, max_expiry_date = interval_range.map { |range| Date.current + range }
  min_expiry_date = min_expires_at if min_expires_at
  max_expiry_date = max_expires_at if max_expires_at
  interval_attr = "#{interval}_notification_sent_at"

  sql_string = "    revoked = FALSE\n    AND \#{interval_attr} IS NULL\n    AND expire_notification_delivered = FALSE\n    AND expires_at BETWEEN ? AND ?\n  SQL\n\n  # this scope must use a string condition rather than activerecord syntax,\n  # otherwise Postgres will not use the correct indices\n  where(sql_string, min_expiry_date, max_expiry_date).without_impersonation\nend\n"

.search(query) ⇒ Object



144
145
146
# File 'app/models/personal_access_token.rb', line 144

def self.search(query)
  fuzzy_search(query, [:name])
end

.simple_sortsObject



119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'app/models/personal_access_token.rb', line 119

def self.simple_sorts
  super.merge(
    {
      'name_asc' => -> { order_name_asc_id_asc },
      'name_desc' => -> { order_name_desc_id_desc },
      'created_asc' => -> { order_created_at_asc_id_asc },
      'created_desc' => -> { order_created_at_desc_id_desc },
      'expires_asc' => -> { order_expires_at_asc_id_asc },
      'expires_desc' => -> { order_expires_at_desc_id_desc },
      'last_used_asc' => -> { order_last_used_at_asc_id_asc },
      'last_used_desc' => -> { order_last_used_at_desc_id_desc }
    }
  )
end

.token_prefixObject



134
135
136
137
138
139
140
141
142
# File 'app/models/personal_access_token.rb', line 134

def self.token_prefix
  # Instance wide token prefixes take precedence over the personal_access_token_prefix
  # See https://gitlab.com/gitlab-org/gitlab/-/issues/388379#note_2477892999
  if ::Authn::TokenField::PrefixHelper.instance_prefix.blank?
    Gitlab::CurrentSettings.current_application_settings.personal_access_token_prefix
  else
    ::Authn::TokenField::PrefixHelper.prepend_instance_prefix(PERSONAL_TOKEN_PREFIX)
  end
end

Instance Method Details

#active?Boolean

Returns:

  • (Boolean)


114
115
116
# File 'app/models/personal_access_token.rb', line 114

def active?
  !revoked? && !expired?
end

#hook_attrsObject



179
180
181
# File 'app/models/personal_access_token.rb', line 179

def hook_attrs
  Gitlab::HookData::ResourceAccessTokenBuilder.new(self).build
end

#revoke!Object



104
105
106
107
108
109
110
111
112
# File 'app/models/personal_access_token.rb', line 104

def revoke!
  return true if revoked?

  if persisted?
    update_columns(revoked: true, updated_at: Time.zone.now)
  else
    self.revoked = true
  end
end