Merge branch 'master' into 13561-collection-versions-api
[arvados.git] / services / api / app / models / repository.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 class Repository < ArvadosModel
6   include HasUuid
7   include KindAndEtag
8   include CommonApiTemplate
9
10   # Order is important here.  We must validate the owner before we can
11   # validate the name.
12   validate :valid_owner
13   validate :name_format, :if => Proc.new { |r| r.errors[:owner_uuid].empty? }
14   validates(:name, uniqueness: true, allow_nil: false)
15
16   api_accessible :user, extend: :common do |t|
17     t.add :name
18     t.add :fetch_url
19     t.add :push_url
20     t.add :clone_urls
21   end
22
23   def self.attributes_required_columns
24     super.merge("clone_urls" => ["name"],
25                 "fetch_url" => ["name"],
26                 "push_url" => ["name"])
27   end
28
29   # Deprecated. Use clone_urls instead.
30   def push_url
31     ssh_clone_url
32   end
33
34   # Deprecated. Use clone_urls instead.
35   def fetch_url
36     ssh_clone_url
37   end
38
39   def clone_urls
40     [ssh_clone_url, https_clone_url].compact
41   end
42
43   def server_path
44     # Find where the repository is stored on the API server's filesystem,
45     # and return that path, or nil if not found.
46     # This method is only for the API server's internal use, and should not
47     # be exposed through the public API.  Following our current gitolite
48     # setup, it searches for repositories stored by UUID, then name; and it
49     # prefers bare repositories over checkouts.
50     [["%s.git"], ["%s", ".git"]].each do |repo_base, *join_args|
51       [:uuid, :name].each do |path_attr|
52         git_dir = File.join(Rails.configuration.git_repositories_dir,
53                             repo_base % send(path_attr), *join_args)
54         return git_dir if File.exist?(git_dir)
55       end
56     end
57     nil
58   end
59
60   protected
61
62   def permission_to_update
63     if not super
64       false
65     elsif current_user.is_admin
66       true
67     elsif name_changed?
68       current_user.uuid == owner_uuid
69     else
70       true
71     end
72   end
73
74   def owner
75     User.find_by_uuid(owner_uuid)
76   end
77
78   def valid_owner
79     if owner.nil? or (owner.username.nil? and (owner.uuid != system_user_uuid))
80       errors.add(:owner_uuid, "must refer to a user with a username")
81       false
82     end
83   end
84
85   def name_format
86     if owner.uuid == system_user_uuid
87       prefix_match = ""
88       errmsg_start = "must be"
89     else
90       prefix_match = Regexp.escape(owner.username + "/")
91       errmsg_start = "must be the owner's username, then '/', then"
92     end
93     if not (/^#{prefix_match}[A-Za-z][A-Za-z0-9]*$/.match(name))
94       errors.add(:name,
95                  "#{errmsg_start} a letter followed by alphanumerics")
96       false
97     end
98   end
99
100   def ssh_clone_url
101     _clone_url :git_repo_ssh_base, 'git@git.%s.arvadosapi.com:'
102   end
103
104   def https_clone_url
105     _clone_url :git_repo_https_base, 'https://git.%s.arvadosapi.com/'
106   end
107
108   def _clone_url config_var, default_base_fmt
109     configured_base = Rails.configuration.send config_var
110     return nil if configured_base == false
111     prefix = new_record? ? Rails.configuration.uuid_prefix : uuid[0,5]
112     if prefix == Rails.configuration.uuid_prefix and configured_base != true
113       base = configured_base
114     else
115       base = default_base_fmt % prefix
116     end
117     '%s%s.git' % [base, name]
118   end
119 end