Class User
In: app/models/user.rb
Parent: ActiveRecord::Base

We always assume the following fields exists:

> :user_name, :last_name, :first_name

If there are added columns, add the default values to default_values

Methods

Constants

STUDENT = 'Student'   role constants
ADMIN = 'Admin'
TA = 'Ta'
AUTHENTICATE_SUCCESS = 0   Authentication constants to be used as return values see self.authenticated? and main_controller for details
AUTHENTICATE_NO_SUCH_USER = 1
AUTHENTICATE_BAD_PASSWORD = 2
AUTHENTICATE_ERROR = 3
AUTHENTICATE_BAD_CHAR = 4
AUTHENTICATE_BAD_PLATFORM = 5

Public Class methods

[Source]

     # File app/models/user.rb, line 159
159:   def self.add_user(user_class, row)
160:     # convert each line to a hash with FIELDS as corresponding keys
161:     # and create or update a user with the hash values
162:     #return nil if values.length < UPLOAD_FIELDS.length
163:     user_attributes = {}
164:     # Loop through the resulting array as key, value pairs
165:    
166:     user_class::CSV_UPLOAD_ORDER.zip(row) do |key, val|
167:       # append them to the hash that is returned by User.get_default_ta/student_attrs
168:       user_attributes[key] = val
169:     end
170:     
171:     # Is there already a Student with this User number?
172:     current_user = user_class.find_or_create_by_user_name(user_attributes[:user_name])
173:     current_user.attributes = user_attributes
174: 
175:     if !current_user.save
176:       return nil
177:     end
178:     
179:     return current_user
180:   end

Authenticates login against its password through a script specified by config VALIDATE_FILE

[Source]

    # File app/models/user.rb, line 46
46:   def self.authenticate(login, password)
47:     # Do not allow the following characters in usernames/passwords
48:     # Right now, this is \n and \0 only, since username and password
49:     # are delimited by \n and C programs use \0 to terminate strings
50:     not_allowed_regexp = Regexp.new(/[\n\0]+/)
51:     if !(not_allowed_regexp.match(login) || not_allowed_regexp.match(password))
52:       # Open a pipe and write to stdin of the program specified by config VALIDATE_FILE. 
53:       # We could read something from the programs stdout, but there is no need
54:       # for that at the moment (you would do it by e.g. pipe.readlines)
55:       
56:       # External validation is supported on *NIX only
57:       if RUBY_PLATFORM =~ /(:?mswin|mingw)/ # should match for Windows only
58:         return AUTHENTICATE_BAD_PLATFORM
59:       end
60:       
61:       # In general, the external password validation program will return the
62:       # following codes (other than 0):
63:       #  1 means no such user
64:       #  2 means bad password
65:       #  3 is used for other error exits
66:       pipe = IO.popen( MarkusConfigurator.markus_config_validate_file, "w+" )
67:       pipe.puts("#{login}\n#{password}") # write to stdin of markus_config_validate
68:       pipe.close
69:       m_logger = MarkusLogger.instance
70:       case $?.exitstatus
71:         when 0
72:           m_logger.log(I18n.t("markus_logger.user_login_message", :user_name => login), MarkusLogger::INFO)
73:           return AUTHENTICATE_SUCCESS
74:         when 1
75:           m_logger.log(I18n.t("markus_logger.user_wrong_credentials", :user_name => login), MarkusLogger::ERROR)
76:           return AUTHENTICATE_NO_SUCH_USER
77:         when 2
78:           m_logger.log(I18n.t("markus_logger.user_wrong_credentials", :user_name => login), MarkusLogger::ERROR)
79:           return AUTHENTICATE_BAD_PASSWORD
80:         else
81:           m_logger.log(I18n.t("markus_logger.user_login_error", :user_name => login),MarkusLogger::ERROR)
82:           return AUTHENTICATE_ERROR
83:       end
84:     else
85:       m_logger.log(I18n.t("markus_logger.user_login_error", :user_name => login),MarkusLogger::ERROR)
86:       return AUTHENTICATE_BAD_CHAR
87:     end
88:   end

Verifies if user is allowed to enter MarkUs Returns user object representing the user with the given login.

[Source]

    # File app/models/user.rb, line 39
39:   def self.authorize(login)
40:     # fetch login in database to see if it is registered.
41:     find_by_user_name(login)
42:   end

Classlist parsing ———————————————————

[Source]

     # File app/models/user.rb, line 122
122:   def self.generate_csv_list(user_list)
123:      file_out = FasterCSV.generate do |csv|
124:        user_list.each do |user|
125:          # csv format is user_name,last_name,first_name
126:          user_array = [user.user_name,user.last_name,user.first_name]
127:          csv << user_array
128:        end
129:      end
130:      return file_out
131:   end

Convenience method which returns a configuration Hash for the repository lib

[Source]

     # File app/models/user.rb, line 184
184:   def self.repo_config
185:     # create config
186:     conf = Hash.new
187:     conf["IS_REPOSITORY_ADMIN"] = MarkusConfigurator.markus_config_repository_admin?
188:     conf["REPOSITORY_PERMISSION_FILE"] = MarkusConfigurator.markus_config_repository_permission_file
189:     return conf
190:   end

[Source]

     # File app/models/user.rb, line 133
133:   def self.upload_user_list(user_class, user_list)
134:     num_update = 0
135:     result = {}
136:     result[:invalid_lines] = []  # store lines that were not processed    
137:     # read each line of the file and update classlist
138:     User.transaction do
139:       processed_users = []
140:       FasterCSV.parse(user_list, :skip_blanks => true, :row_sep => :auto) do |row|
141:         # don't know how to fetch line so we concat given array
142:         next if FasterCSV.generate_line(row).strip.empty?
143:         if processed_users.include?(row[0])
144:           result[:invalid_lines] = I18n.t('csv_upload_user_duplicate', {:user_name => row[0]})
145:         else
146:           if User.add_user(user_class, row).nil?
147:             result[:invalid_lines] << row.join(",")
148:           else
149:             num_update += 1
150:             processed_users.push(row[0])
151:           end
152:         end
153:       end # end prase
154:     end
155:     result[:upload_notice] = "#{num_update} user(s) added/updated."     
156:     return result
157:   end

Public Instance methods

TODO: make these proper associations. They work fine for now but

 they'll be slow in production

[Source]

    # File app/models/user.rb, line 93
93:   def active_groupings
94:     self.groupings.find(:all, :conditions => ["memberships.membership_status != :u", { :u => StudentMembership::STATUSES[:rejected]}])
95:   end

Helper methods ——————————————————

[Source]

     # File app/models/user.rb, line 99
 99:   def admin?
100:     self.class == Admin
101:   end

Resets the api key. Usually triggered, if the old md5 hash has gotten into the wrong hands.

[Source]

     # File app/models/user.rb, line 216
216:   def reset_api_key
217:     key = generate_api_key
218:     md5 = Digest::MD5.new
219:     md5.update(key)
220:     # base64 encode md5 hash
221:     self.api_key = Base64.encode64(md5.to_s).strip
222:     return self.save
223:   end

Set API key for user model. The key is a SHA2 512 bit long digest, which is in turn MD5 digested and Base64 encoded so that it doesn‘t include bad HTTP characters.

TODO: If we end up using this heavily we should probably let this token expire every X days/hours/weeks. When it does, a new token should be automatically generated.

[Source]

     # File app/models/user.rb, line 201
201:   def set_api_key
202:     if self.api_key.nil? 
203:       key = generate_api_key
204:       md5 = Digest::MD5.new
205:       md5.update(key)
206:       # base64 encode md5 hash
207:       self.api_key = Base64.encode64(md5.to_s).strip
208:       return self.save
209:     else
210:       return true
211:     end
212:   end

[Source]

     # File app/models/user.rb, line 107
107:   def student?
108:     self.class == Student
109:   end

Submission helper methods ————————————————-

[Source]

     # File app/models/user.rb, line 113
113:   def submission_for(aid)
114:     grouping = grouping_for(aid)
115:     if grouping.nil?
116:       return nil
117:     end
118:     return grouping.current_submission_used
119:   end

[Source]

     # File app/models/user.rb, line 103
103:   def ta?
104:     self.class == Ta
105:   end

[Validate]