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