Commit 27a9cc6d authored by Adam Hegyi's avatar Adam Hegyi

Merge branch 'dblessing-atlassian-integration' into 'master'

Add Atlassian Identity to store identity/credentials

See merge request gitlab-org/gitlab!40176
parents 785e0235 db07a143
# frozen_string_literal: true
module Atlassian
class Identity < ApplicationRecord
self.table_name = 'atlassian_identities'
belongs_to :user
validates :extern_uid, presence: true, uniqueness: true
validates :user, presence: true, uniqueness: true
attr_encrypted :token,
mode: :per_attribute_iv,
key: Settings.attr_encrypted_db_key_base_truncated,
algorithm: 'aes-256-gcm',
encode: false,
encode_iv: false
attr_encrypted :refresh_token,
mode: :per_attribute_iv,
key: Settings.attr_encrypted_db_key_base_truncated,
algorithm: 'aes-256-gcm',
encode: false,
encode_iv: false
end
end
......@@ -181,6 +181,7 @@ class User < ApplicationRecord
has_one :user_detail
has_one :user_highest_role
has_one :user_canonical_email
has_one :atlassian_identity, class_name: 'Atlassian::Identity'
has_many :reviews, foreign_key: :author_id, inverse_of: :author
......
---
title: Add Atlassian Identity to store identity/credentials
merge_request: 40176
author:
type: added
# frozen_string_literal: true
class CreateAtlassianIdentities < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
unless table_exists?(:atlassian_identities)
with_lock_retries do
create_table :atlassian_identities, id: false do |t|
t.references :user, index: false, foreign_key: { on_delete: :cascade }, null: false, primary_key: true
t.timestamps_with_timezone
t.datetime_with_timezone :expires_at
t.text :extern_uid, null: false, index: { unique: true }
t.binary :encrypted_token
t.binary :encrypted_token_iv
t.binary :encrypted_refresh_token
t.binary :encrypted_refresh_token_iv
end
end
end
add_text_limit :atlassian_identities, :extern_uid, 255
add_check_constraint :atlassian_identities, 'octet_length(encrypted_token) <= 2048', 'atlassian_identities_token_length_constraint'
add_check_constraint :atlassian_identities, 'octet_length(encrypted_token_iv) <= 12', 'atlassian_identities_token_iv_length_constraint'
add_check_constraint :atlassian_identities, 'octet_length(encrypted_refresh_token) <= 512', 'atlassian_identities_refresh_token_length_constraint'
add_check_constraint :atlassian_identities, 'octet_length(encrypted_refresh_token_iv) <= 12', 'atlassian_identities_refresh_token_iv_length_constraint'
end
def down
with_lock_retries do
drop_table :atlassian_identities
end
end
end
d92cdef33a892fdd1761d9491bc8e4c782e9db348d4a6848a1470e99e644fbfd
\ No newline at end of file
......@@ -9479,6 +9479,32 @@ CREATE TABLE public.ar_internal_metadata (
updated_at timestamp(6) without time zone NOT NULL
);
CREATE TABLE public.atlassian_identities (
user_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
expires_at timestamp with time zone,
extern_uid text NOT NULL,
encrypted_token bytea,
encrypted_token_iv bytea,
encrypted_refresh_token bytea,
encrypted_refresh_token_iv bytea,
CONSTRAINT atlassian_identities_refresh_token_iv_length_constraint CHECK ((octet_length(encrypted_refresh_token_iv) <= 12)),
CONSTRAINT atlassian_identities_refresh_token_length_constraint CHECK ((octet_length(encrypted_refresh_token) <= 512)),
CONSTRAINT atlassian_identities_token_iv_length_constraint CHECK ((octet_length(encrypted_token_iv) <= 12)),
CONSTRAINT atlassian_identities_token_length_constraint CHECK ((octet_length(encrypted_token) <= 2048)),
CONSTRAINT check_32f5779763 CHECK ((char_length(extern_uid) <= 255))
);
CREATE SEQUENCE public.atlassian_identities_user_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.atlassian_identities_user_id_seq OWNED BY public.atlassian_identities.user_id;
CREATE TABLE public.audit_events (
id integer NOT NULL,
author_id integer NOT NULL,
......@@ -16880,6 +16906,8 @@ ALTER TABLE ONLY public.approver_groups ALTER COLUMN id SET DEFAULT nextval('pub
ALTER TABLE ONLY public.approvers ALTER COLUMN id SET DEFAULT nextval('public.approvers_id_seq'::regclass);
ALTER TABLE ONLY public.atlassian_identities ALTER COLUMN user_id SET DEFAULT nextval('public.atlassian_identities_user_id_seq'::regclass);
ALTER TABLE ONLY public.audit_events ALTER COLUMN id SET DEFAULT nextval('public.audit_events_id_seq'::regclass);
ALTER TABLE ONLY public.award_emoji ALTER COLUMN id SET DEFAULT nextval('public.award_emoji_id_seq'::regclass);
......@@ -17800,6 +17828,9 @@ ALTER TABLE ONLY public.approvers
ALTER TABLE ONLY public.ar_internal_metadata
ADD CONSTRAINT ar_internal_metadata_pkey PRIMARY KEY (key);
ALTER TABLE ONLY public.atlassian_identities
ADD CONSTRAINT atlassian_identities_pkey PRIMARY KEY (user_id);
ALTER TABLE ONLY public.audit_events_part_5fc467ac26
ADD CONSTRAINT audit_events_part_5fc467ac26_pkey PRIMARY KEY (id, created_at);
......@@ -19237,6 +19268,8 @@ CREATE INDEX index_approvers_on_target_id_and_target_type ON public.approvers US
CREATE INDEX index_approvers_on_user_id ON public.approvers USING btree (user_id);
CREATE UNIQUE INDEX index_atlassian_identities_on_extern_uid ON public.atlassian_identities USING btree (extern_uid);
CREATE INDEX index_audit_events_on_entity_id_entity_type_id_desc_author_id ON public.audit_events USING btree (entity_id, entity_type, id DESC, author_id);
CREATE INDEX index_award_emoji_on_awardable_type_and_awardable_id ON public.award_emoji USING btree (awardable_type, awardable_id);
......@@ -23097,6 +23130,9 @@ ALTER TABLE ONLY public.resource_weight_events
ALTER TABLE ONLY public.design_management_designs
ADD CONSTRAINT fk_rails_bfe283ec3c FOREIGN KEY (issue_id) REFERENCES public.issues(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.atlassian_identities
ADD CONSTRAINT fk_rails_c02928bc18 FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.serverless_domain_cluster
ADD CONSTRAINT fk_rails_c09009dee1 FOREIGN KEY (pages_domain_id) REFERENCES public.pages_domains(id) ON DELETE CASCADE;
......
# frozen_string_literal: true
FactoryBot.define do
factory :atlassian_identity, class: 'Atlassian::Identity' do
extern_uid { generate(:username) }
user { create(:user) }
expires_at { 2.weeks.from_now }
token { SecureRandom.alphanumeric(1254) }
refresh_token { SecureRandom.alphanumeric(45) }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Atlassian::Identity do
describe 'associations' do
it { is_expected.to belong_to(:user) }
end
describe 'validations' do
subject { create(:atlassian_identity) }
it { is_expected.to validate_presence_of(:extern_uid) }
it { is_expected.to validate_uniqueness_of(:extern_uid) }
it { is_expected.to validate_presence_of(:user) }
it { is_expected.to validate_uniqueness_of(:user) }
end
describe 'encrypted tokens' do
let(:token) { SecureRandom.alphanumeric(1254) }
let(:refresh_token) { SecureRandom.alphanumeric(45) }
let(:identity) { create(:atlassian_identity, token: token, refresh_token: refresh_token) }
it 'saves the encrypted token, refresh token and corresponding ivs' do
expect(identity.encrypted_token).not_to be_nil
expect(identity.encrypted_token_iv).not_to be_nil
expect(identity.encrypted_refresh_token).not_to be_nil
expect(identity.encrypted_refresh_token_iv).not_to be_nil
expect(identity.token).to eq(token)
expect(identity.refresh_token).to eq(refresh_token)
end
end
end
......@@ -68,6 +68,7 @@ RSpec.describe User do
it { is_expected.to have_one(:namespace) }
it { is_expected.to have_one(:status) }
it { is_expected.to have_one(:user_detail) }
it { is_expected.to have_one(:atlassian_identity) }
it { is_expected.to have_one(:user_highest_role) }
it { is_expected.to have_many(:snippets).dependent(:destroy) }
it { is_expected.to have_many(:members) }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment