4253: Users can manage their own repositories.
[arvados.git] / services / api / app / models / repository.rb
index 0189ee7236940934831713ff18b2b92ffb53bd87..0cab4dcf8760641b95f7eb8f3c5dfa2b05d11018 100644 (file)
@@ -3,34 +3,84 @@ class Repository < ArvadosModel
   include KindAndEtag
   include CommonApiTemplate
 
+  # Order is important here.  We must validate the owner before we can
+  # validate the name.
+  validate :valid_owner
+  validate :name_format, :if => Proc.new { |r| r.errors[:owner_uuid].empty? }
+  validates(:name, uniqueness: true, allow_nil: false)
+
   api_accessible :user, extend: :common do |t|
     t.add :name
     t.add :fetch_url
     t.add :push_url
   end
 
+  def self.attributes_required_columns
+    super.merge({"push_url" => ["name"], "fetch_url" => ["name"]})
+  end
+
   def push_url
-    super || self.name && "git@git.#{Rails.configuration.uuid_prefix}.arvadosapi.com:#{self.name}.git"
+    "git@git.%s.arvadosapi.com:%s.git" % [Rails.configuration.uuid_prefix, name]
   end
 
   def fetch_url
-    super || push_url
+    push_url
+  end
+
+  def server_path
+    # Find where the repository is stored on the API server's filesystem,
+    # and return that path, or nil if not found.
+    # This method is only for the API server's internal use, and should not
+    # be exposed through the public API.  Following our current gitolite
+    # setup, it searches for repositories stored by UUID, then name; and it
+    # prefers bare repositories over checkouts.
+    [["%s.git"], ["%s", ".git"]].each do |repo_base, *join_args|
+      [:uuid, :name].each do |path_attr|
+        git_dir = File.join(Rails.configuration.git_repositories_dir,
+                            repo_base % send(path_attr), *join_args)
+        return git_dir if File.exist?(git_dir)
+      end
+    end
+    nil
   end
 
   protected
 
-  def permission_to_create
-    current_user and current_user.is_admin
+  def permission_to_update
+    if not super
+      false
+    elsif current_user.is_admin
+      true
+    elsif name_changed?
+      current_user.uuid == owner_uuid
+    else
+      true
+    end
   end
 
-  def permission_to_update
-    return false if not current_user
-    return true if current_user.is_admin
-    # For normal objects, this is a way to check whether you have
-    # write permission. Repositories should be brought closer to the
-    # normal permission model during #4253. Meanwhile, we'll
-    # special-case this so arv-git-httpd can detect write permission:
-    return super if changed_attributes.keys - ['modified_at', 'updated_at'] == []
-    false
+  def owner
+    User.find_by_uuid(owner_uuid)
+  end
+
+  def valid_owner
+    if owner.nil? or (owner.username.nil? and (owner.uuid != system_user_uuid))
+      errors.add(:owner_uuid, "must refer to a user with a username")
+      false
+    end
+  end
+
+  def name_format
+    if owner.uuid == system_user_uuid
+      prefix_match = ""
+      errmsg_start = "must be"
+    else
+      prefix_match = Regexp.escape(owner.username + "/")
+      errmsg_start = "must be the owner's username, then '/', then"
+    end
+    if not /^#{prefix_match}[A-Za-z][A-Za-z0-9]*$/.match(name)
+      errors.add(:name,
+                 "#{errmsg_start} a letter followed by alphanumerics")
+      false
+    end
   end
 end