reregistration workaround, prevent undefined NCC status (bnc#631173)
[opensuse:yast-web-client.git] / plugins / registration / app / controllers / registration_controller.rb
1 require 'yast/service_resource'
2
3 # = Registration controller
4 # Provides all functionality, that handles system registration.
5
6 class RegistrationController < ApplicationController
7   before_filter :login_required
8   layout 'main'
9   include ProxyLoader
10
11   def initialize
12     @trans = {  'email' => _("Email"),
13                 'moniker' => _("System name"),
14                 'regcode-sles' => _("SLES registration code"),
15                 'regcode-sled' => _("SLED registration code"),
16                 'appliance-regcode' => _("Appliance registration code"),
17                 'regcode-webyast'   => _("WebYaST registration code"),
18                 '___getittranslated1' => _("Registration code"),
19                 '___getittranslated2' => _("Hostname"),
20                 '___getittranslated3' => _("Device name"),
21                 '___getittranslated4' => _("Appliance name"),
22                 '___getittranslated5' => _("registration code")
23              }
24     @trans.freeze
25     @options = { 'debug'=>2 , 'forcereg' => 0 }
26   end
27
28   private
29   def client_permissions
30     @client = YaST::ServiceResource.proxy_for('org.opensuse.yast.modules.registration.registration')
31     unless @client
32       flash[:notice] = _("Invalid session, please login again.")
33       redirect_to( logout_path ) and return
34     end
35     @client.timeout = 120 #increasing server timeout cause registration can take a while
36     @permissions = @client.permissions
37     @arguments = []
38   end
39
40   def redirect_success
41     # Use our own redirect_success!
42     # the one in application_controller.rb has a different behaviour which got fixed in a later version
43     # this registration module is a port of a version that relies on the new behaviour of redirect_success
44     logger.debug session.inspect
45     if Basesystem::in_process?(session)
46       logger.debug "wizard redirect DONE"
47       redirect_to :controller => "controlpanel", :action => "nextstep"
48     else
49       logger.debug "Success non-wizard redirect"
50       redirect_to :controller => "controlpanel", :action => "index"
51     end
52   end
53
54   def register_permissions
55     unless @permissions[:statelessregister]
56       flash[:warning] = _("No permissions for registration")
57       redirect_to root_path
58       return false
59     end
60     true
61   end
62
63   def translate_argument_key(key)
64     return '-unknown-' unless key
65     return @trans[key] if ( @trans.kind_of?(Hash) && @trans[key] )
66     key
67   end
68
69   def sort_arguments(args)
70     begin
71       args.collect! do |arg|
72         arg['description'] = translate_argument_key( arg.kind_of?(Hash) ? arg['name'] : nil )
73         arg
74       end
75       # sort by names alphabetically
76       return args.sort! { |a,b|  a['name'] <=> b['name'] }
77     rescue
78       return Array.new
79     end
80   end
81
82   # Initialize GetText and Content-Type.
83   init_gettext "yast_webclient_registration"  # textdomain, options(:charset, :content_type)
84
85   def client_guid
86     # handle config error in backend (bnc#592620)
87     begin
88       status = @client.find
89     rescue
90       logger.debug "Registration could not find registration information: system is unregistered."
91     end
92
93     begin
94       @guid = status.guid if (status.respond_to?('guid') && !status.guid.blank?)
95       @config_error = true if (status.respond_to?('configerror') && status.configerror == 'true')
96       logger.debug "found GUID: #{@guid}"
97     rescue
98       logger.error "Registration could neither read guid nor detect a configuration error."
99     end
100
101     return @guid
102   end
103
104   def try_again_msg
105     _("Please try to register again later.")
106   end
107
108   def not_succeeded_msg
109     _("Registration did not succeed.")
110   end
111
112   def skipped_msg
113     _("Registration was skipped.")
114   end
115
116   def temporary_issue_msg
117     _("This may be a temporary issue.")
118   end
119
120   def no_updates_msg
121     _("The system might not receive necessary updates.")
122   end
123
124   def registration_skip_flash
125     "<b>#{ skipped_msg }</b><p>#{ no_updates_msg }<br />#{ try_again_msg }</p>"
126   end
127
128   def server_error_flash(msg)
129     "<b>#{ not_succeeded_msg }</b><p>#{ msg || '' } #{ temporary_issue_msg }<br />#{ try_again_msg }</p>"
130   end
131
132   def data_error_flash(msg)
133     "<b>#{ not_succeeded_msg }</b><p>#{ msg || try_again_msg }</p>"
134   end
135
136   def registration_logic_error
137     flash[:error] = server_error_flash _("The registration server returned invalid or incomplete data.")
138     logger.error "Registration resulted in an error, registration server or SuseRegister backend returned invalid or incomplete data."
139     # success: allow users to skip registration in case of an error (bnc#578684) (bnc#579463)
140     redirect_success
141   end
142
143   def registration_backend_error
144     logger.error "Registration could not read the configuration. Most likely the backend is not installed correctly. Please check the package dependencies."
145     flash[:error] = _("Could not read the registration configuration.") + "<br>" + _("The registration backend is not installed correctly") +
146                     " " + _("Please refer to your support contact.")
147   end
148
149   def collect_missing_arguments(missed_args)
150     arg_error_count = 0
151     begin
152       @arguments.collect! do |argument|
153         missed_args.each do |missed_arg|
154           if missed_arg["name"] == argument["name"] then
155             if argument["value"] != missed_arg["value"] then
156               # flag error if value is rejected by registration server
157               argument["error"] = true if missed_arg["flag"] == "m"
158               arg_error_count += 1
159             end
160             argument["value"] = missed_arg["value"]
161             argument["flag"] = missed_arg["flag"]
162             argument["kind"] = missed_arg["kind"]
163             missed_args.reject! {|del_arg| del_arg["name"] == argument["name"] }
164             break
165           end
166         end
167         argument
168       end
169     rescue
170       logger.error "Registration process can not collect the argument details."
171     end
172
173     # add remaining arguments to the list
174     @arguments.concat(missed_args)
175     flash[:error] = _("Please fill out missing entries.") if arg_error_count > 0
176   end
177
178   def split_arguments
179     begin
180       # split arguments into two lists to show them separately and sort each list to show them in a unique order
181       @arguments_mandatory = sort_arguments( @arguments.select { |arg| (arg["flag"] == "m") if arg.kind_of?(Hash) } )
182       @arguments_automatic = sort_arguments( @arguments.select { |arg| (arg["flag"] == "a") if arg.kind_of?(Hash) } )
183       @arguments_detail    = sort_arguments( @arguments.select { |arg| ( (arg["flag"] != "m") && (arg["flag"] != "a") ) if arg.kind_of?(Hash) } )
184     rescue
185       logger.error "Registration found invalid argument data. Nothing to display to the user."
186       @arguments_mandatory = []
187       @arguments_automatic = []
188       @arguments_detail = []
189     end
190   end
191
192   def sources_changes_flash(msg='')
193     # use an own type for this message
194     # because it needs to be displayed and bypass the UI-expert-filter (bnc600842)
195     ftype = :repoinfo
196     flash[ftype] ||= ''
197     flash[ftype] += msg
198   end
199
200   def check_service_changes
201     begin
202       changes = false
203       if @changed_services && @changed_services.kind_of?(Array) && @changed_services.size > 0 then
204         flash_msg = "<ul>"
205         @changed_services.each do |c|
206           if c.respond_to?(:name) && c.name && c.respond_to?(:status) && c.status == 'added' then
207             flash_msg += "<li>" + _("Service added:") + " #{c.name}</li>"
208           end
209           if c.respond_to?(:catalogs) && c.catalogs && c.catalogs.respond_to?(:catalog) && c.catalogs.catalog then
210             if c.catalogs.catalog.respond_to?(:name) && c.catalogs.catalog.respond_to?(:status) && c.catalogs.catalog.status == 'added' then
211               flash_msg += "<ul><li>" + _("Catalog enabled:") + " #{c.catalogs.catalog.name}</li></ul>"
212               changes = true
213             elsif c.catalogs.catalog.kind_of?(Array) then
214               flash_msg += "<ul>"
215               c.catalogs.catalog.each do |s|
216                 if s && s.respond_to?(:name) && s.respond_to?(:status) && s.status == 'added' then
217                   flash_msg += "<li>" + _("Catalog enabled:") + " #{s.name}</li>"
218                   changes = true
219                 end
220               end
221               flash_msg += "</ul>"
222             end
223           end
224         end
225         flash_msg += "</ul>"
226         sources_changes_flash flash_msg if changes
227       else
228         return false
229       end
230     rescue
231       logger.error "Registration could not check the services for changes."
232       return false
233     end
234     true
235   end
236
237   def check_repository_changes
238     begin
239       changes = false
240       if @changed_repositories && @changed_repositories.kind_of?(Array) && @changed_repositories.size > 0 then
241         flash_msg = "<ul>"
242         @changed_repositories.each do |r|
243           if r.respond_to?(:name) && r.name && r.respond_to?(:status) && r.status == 'added' then
244             flash_msg += "<li>" + _("Repository added:") + " #{r.name}</li>"
245             changes = true
246           end
247         end
248         flash_msg += "</ul>"
249         sources_changes_flash flash_msg if changes
250       else
251         return false
252       end
253     rescue
254       logger.error "Registration could not check the repositories for changes."
255       return false
256     end
257     true
258   end
259
260   public
261
262   def index
263     return unless client_permissions
264     return unless register_permissions
265
266     client_guid
267     if @config_error
268       registration_backend_error
269       redirect_success
270       return
271     end
272
273     if !@guid
274       @arguments = []
275       @nexttarget = 'update'
276       register
277     else
278       @showstatus = true
279     end
280
281   end
282
283   def skip
284     return unless client_permissions
285     return unless register_permissions
286     # redirect to the appropriate next target and show skip message
287     flash[:warning] = registration_skip_flash if !client_guid
288     redirect_success
289     return
290   end
291
292   def reregister
293     # provide a way to force a new registration, even if system is already registered
294     @reregister = true
295     @nexttarget = 'reregisterupdate'
296
297     # correctly set the forcereg parameter according to registration protocol specification
298     @options['forcereg'] = 1
299     # switch back to a wrong behaviour of the registration, as NCC implemented the specification wrongly
300     # this is just an ugly workaround and should be removed when NCC was fixed
301     if File::exists? '/var/lib/yastws/enable_SLM_webyast_registration_workaround' then
302       @options['forcereg'] = 0
303     end
304
305     register
306   end
307
308   def reregisterupdate
309     # update function for reregistration mode - adaption for (bnc#631173)
310     #   in reregistration mode only the first request should contain the "forcereg" option
311     #   the following should not contain them. The "update" function would stop the registration, as the system is already registered.
312     @reregister = true
313     @nexttarget = 'reregisterupdate'
314     # no forcereg parameter here
315
316     register
317   end
318
319   def update
320     @nexttarget = 'update'
321     register
322   end
323
324   def register
325     return unless client_permissions
326     return unless register_permissions
327
328     client_guid
329     if @config_error
330       registration_backend_error
331       redirect_success
332       return
333     end
334
335     # redirect in case of interrupted basesetup
336     if @guid && !@reregister
337       flash[:warning] = _("This system is already registered.")
338       redirect_success
339       return
340     end
341
342     begin
343       params.each do | key, value |
344         if key.starts_with? "registration_arg_"
345           argument = Hash.new
346           argument["name"] = key[17, key.size-17]
347           argument["value"] = value
348           @arguments << argument
349         end
350       end
351     rescue
352       logger.debug "No arguments were passed to the registration call."
353     end
354
355     @changed_repositories = []
356     @changed_services = []
357     success = false
358     begin
359       register = @client.create({ :arguments => { 'argument' => @arguments },
360                                   :options => @options })
361       logger.debug "registration finished: #{register.to_xml}"
362
363       if register.respond_to?(:status) && register.status == "finished" then
364         @changed_repositories = register.changedrepos if register.respond_to? :changedrepos
365         @changed_services = register.changedservices if register.respond_to? :changedservices
366         flash[:notice] = _("Registration finished successfully.")
367         success = true
368       else
369         logger.error "Registration is in success mode, but the backend returned no status information."
370         return registration_logic_error
371       end
372
373       # display warning if no repos/services are added/changed during registration (bnc#558854)
374       if !check_service_changes && !check_repository_changes
375       then
376         flash[:warning] = _("<p><b>Repositories were not modified during the registration process.</b></p><p>It is likely that an incorrect registration code was used. If this is the case, please attempt the registration process again to get an update repository.</p><p>Please make sure that this system has an update repository configured, otherwise it will not receive updates.</p>")
377       end
378
379     rescue ActiveResource::ClientError => e
380       error = Hash.from_xml(e.response.body)["registration"]
381       logger.debug "Error mode in registration process: #{error.inspect}"
382
383       unless error && error.kind_of?(Hash) && error["status"] then
384         logger.error "Registration is in error mode but no error status information is provided from the backend."
385         return registration_logic_error
386       end
387
388       if error["status"] == "missinginfo" && !error["missingarguments"].blank?
389         logger.debug "missing arguments #{error["missingarguments"].inspect}"
390         logger.info "Registration is in needinfo - more information is required"
391         # collect and merge the argument data
392         collect_missing_arguments error["missingarguments"]
393
394       elsif error["status"] == "finished"
395         # status is "finished" but we are in rescure block - this does not fit
396         logger.error "Registration finished successfully (according to backend), but it returned an error (http status 4xx)."
397         logger.error "The registration status is unknown."
398         return registration_logic_error
399
400       elsif error["status"] == "error"
401         e_exitcode = error["exitcode"] || 0
402         e_exitcode = e_exitcode.to_i
403         e_exitcode = 'unknown' if e_exitcode == 0
404
405         logger.error "Registration resulted in an error, ID: #{e_exitcode}."
406         case e_exitcode
407         when 199 then
408           # 199 means that even the initialization of the backend did not succeed
409           logger.error "Registration backend could not be initialized. Maybe due to network problem, SSL certificate issue or blocked by firewall."
410           flash[:error] = server_error_flash _("A connection to the registration server could not be established.")
411         when  2 then
412           logger.error "Registration failed due to invalid data passed to the registration server. Most likely due to a wrong regcode."
413           logger.error "  The registration server thus rejected the registration. User can try again."
414           dataerror = _("The supplied registration data was invalid.")
415           if ( !error["invaliddataerrormessage"].blank?  &&
416                 ( error["invaliddataerrormessage"].to_s.match /(invalid regcode)|(improper code was supplied)/i )  ) then
417             logger.error "  Yep, the registration server says that the regcode was wrong."
418             dataerror = _("The registration code you entered was invalid.")
419           end
420           flash[:error] = data_error_flash  ( dataerror + '<br />' + _("Please perform the registration again with correct registration data.") )
421         when  3 then
422           # 3 means that there is a conflict with the sent and the required data - it could not be solved by asking again
423           logger.error "Registration data is conflicting. Contact your vendor."
424           flash[:error] = server_error_flash _("The transmitted registration data created a conflict.")
425         when 99 then
426           # 99 is an internal error id for missing error status or missing exit codes
427           logger.error "Registration backend sent no or invalid data. Maybe network problem or slow connection or too much load on registration server."
428           return registration_logic_error
429         when 100..101 then
430           # 100 and 101 means that no product is installed that can be registered (100: no product, 101: FACTORY)
431           logger.error "Registration process did not find any products that can be registered."
432           flash[:error] = "<b>" + _("Registration can not be performed. There is no product installed that can be registered.") + "</b>"
433         else
434           # unknown error
435           logger.error "Registration backend returned an unknown error. Please run in debug mode and report a bug."
436           return registration_logic_error
437         end
438         redirect_success
439         return
440       else
441         logger.debug "error while registration: #{error.inspect}"
442         logger.error "Registration resulted in an error: Server returned invalid data"
443         return registration_logic_error
444       end
445
446     rescue Exception => e
447       flash[:error] = server_error_flash _("A connection to the registration server could not be established or it did not reply.")
448       logger.error "Registration resulted in an error, execution of SuseRegister backend expired: Maybe network problem or severe configuration error."
449       redirect_success
450       return
451     end
452
453     if success
454       logger.info "Registration succeed"
455       redirect_success
456     else
457       logger.info "Registration is not yet finished"
458
459       # split into madatory and detail arguments
460       split_arguments
461
462       if @arguments_mandatory.blank? && @arguments_detail.blank? then
463         # redirect if the registration server is in needinfo but arguments list is empty
464         flash[:error] = server_error_flash _("The registration server returned invalid data.")
465         logger.error "Registration resulted in an error: Logic issue, unspecified data requested by registration server"
466         redirect_success
467         return
468       end
469
470       respond_to do |format|
471         format.html { render :action => "index" }
472       end
473     end
474   end
475
476 end