summaryrefslogtreecommitdiff
blob: 764ef72dc134cdd16acc8c1c285755b1f9a945aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# ===GLSAMaker v2
#  Copyright (C) 2009 Alex Legler <a3li@gentoo.org>
#  Copyright (C) 2009 Pierre-Yves Rofes <py@gentoo.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# For more information, see the LICENSE file.

# GLSA model
class Glsa < ActiveRecord::Base
  validates_uniqueness_of :glsa_id, :message => "must be unique"
  validates_presence_of :glsa_id, :message => "GLSA ID needed"

  belongs_to :submitter, :class_name => "User", :foreign_key => "submitter"
  belongs_to :requester, :class_name => "User", :foreign_key => "requester"
  belongs_to :bugreadymaker, :class_name => "User", :foreign_key => "bugreadymaker"

  has_many :revisions
  has_many :comments

  # Returns the last revision object, referring to the current state of things
  def last_revision
    @last_revision ||= self.revisions.find(:first, :order => "revid DESC")
  end
  
  # Invalidates the last revision cache
  def invalidate_last_revision_cache
    @last_revision = nil
  end

  # Returns the next revision ID to be given for this GLSA
  def next_revid
    if (rev = last_revision)
      rev.revid + 1
    else
      0
    end
  end

  # Returns all approving comments
  def approvals
    comments.find(:all, :conditions => ['rating = ?', 'approval'])
  end

  # Returns all rejecting comments
  def rejections
    comments.find(:all, :conditions => ['rating = ?', 'rejection'])
  end

  # Returns true if the draft is ready for sending
  def is_approved?
    (approvals.count - rejections.count) >= 2
  end

  # Returns true if it has comments
  def has_comments?
    comments.count > 0
  end

  # The approval status of the GLSA, either :approved, :commented, or :none
  def approval_status
    if is_approved?
      return :approved
    elsif has_comments?
      if has_pending_comments?
        return :comments_pending
      else
        return :commented
      end
    end
      return :none
  end

  # Returns true if user is the owner of this GLSA.
  def is_owner?(user)
    luser = (status == "request" ? requester : submitter)
    luser == user
  end

  # Returns the workflow status of this GLSA for a given user.
  # Return values: :own (own draft), :approved (approval given), :commented (comment or rejection given)
  def workflow_status(user)
    if is_owner?(user)
      return :own
    end

    if comments.find(:all, :conditions => ['rating = ? AND user_id = ?', 'approval', user.id]).count > 1
      return :approved
    end

    if comments.find(:all, :conditions => ['user_id = ?', user.id]).count > 1
      return :commented
    end

    return :todo
  end
  
  # Returns true if there are any pending comments left
  def has_pending_comments?
    comments.find(:all, :conditions => ['`read` = ?', false]).count > 0
  end
  
  # Returns all CVEs linked to this GLSA
  def related_cves
    last_revision.bugs.map do |bug|
      CVEAssignment.find_all_by_bug(bug.bug_id).map {|assignment| assignment.cve}.uniq
    end.flatten
  end
  
  # Bulk addition of references.
  # Expects an array of hashes <tt>{:title => ..., :url => ...}</tt>
  def add_references(refs)
    rev = last_revision.deep_copy
    
    refs.each do |reference|
      rev.references.create(reference)
    end
    
    invalidate_last_revision_cache
    self
  end

  # Calculates the next GLSA ID for the given month, or the current month
  def self.next_id(month = Time.now)
    month_id = month.strftime("%Y%m")
    items = find(:all, :conditions => ['glsa_id LIKE ? AND status = ?', month_id + '%', 'release'], :order => 'glsa_id DESC')

    return "#{month_id}-01" if items.length == 0

    items.first.glsa_id =~ /^#{month_id}-(\d+)$/
    next_id = Integer($1) + 1
    "#{month_id}-#{format "%02d", next_id}"
  end

  # Files a new GLSA request
  def self.new_request(title, bugs, comment, access, import_references, user)
    glsa = Glsa.new
    glsa.requester = user
    glsa.glsa_id = Digest::MD5.hexdigest(title + Time.now.to_s)[0...9]
    glsa.restricted = (access == "confidential")
    glsa.status = "request"

    unless comment.strip.blank?
      glsa.comments << Comment.new(:rating => "neutral", :text => comment, :user => user)
    end

    begin
      glsa.save!
    rescue Exception => e
      raise Exception, "Error while saving the GLSA object: #{e.message}"
    end

    revision = Revision.new
    revision.revid = glsa.next_revid
    revision.glsa = glsa
    revision.title = title
    revision.user = user

    begin
      revision.save!
    rescue Exception => e
      glsa.delete
      raise Exception, "Error while saving Revision object: #{e.message}"
    end

    bugs = Bugzilla::Bug.str2bugIDs(bugs)

    bugs.each do |bug|
      begin
        b = Glsamaker::Bugs::Bug.load_from_id(bug)
      
        revision.bugs.create(
          :bug_id => bug,
          :title => b.summary,
          :whiteboard => b.status_whiteboard,
          :arches => b.arch_cc.join(', ')
        )
      rescue Exception => e
        # In case of bugzilla errors, just keep the bug #
        revision.bugs.create(:bug_id => bug)
      end
    end

    if import_references
      logger.debug { "importing references" }
      refs = []
      glsa.related_cves.each do |cve|
        refs << {:title => cve.cve_id, :url => cve.url}
      end
      glsa.add_references refs
    end

    glsa
  end

end