ref: refs/heads/master
commit 298cb83e6bddf440bfe413a85aa75b6b407211be
Author: Klaus Kämpf
Date: Tue Jul 14 14:01:07 2009 +0200
The resource (session) is singular, the controller is plural
Argh, fell into this trap again.
---
.../app/controllers/application_controller.rb | 4 +-
webclient/app/controllers/session_controller.rb | 132 -------------
webclient/app/controllers/sessions_controller.rb | 132 +++++++++++++
webclient/app/views/layouts/main.rhtml | 2 +-
webclient/app/views/session/new.rhtml | 19 --
webclient/app/views/sessions/new.rhtml | 19 ++
.../test/functional/session_controller_test.rb | 195 --------------------
.../test/functional/sessions_controller_test.rb | 195 ++++++++++++++++++++
8 files changed, 349 insertions(+), 349 deletions(-)
diff --git a/webclient/app/controllers/application_controller.rb b/webclient/app/controllers/application_controller.rb
index b561be1..d55cae9 100644
--- a/webclient/app/controllers/application_controller.rb
+++ b/webclient/app/controllers/application_controller.rb
@@ -22,14 +22,14 @@ class ApplicationController < ActionController::Base
def ensure_login
unless logged_in?
flash[:notice] = "Please login to continue"
- redirect_to(new_session_path)
+ redirect_to new_sessions_path
end
end
def ensure_logout
if not logged_in?
flash[:notice] = "You must logout before you can login"
- redirect_to(root_url)
+ redirect_to root_url
end
end
diff --git a/webclient/app/controllers/session_controller.rb b/webclient/app/controllers/session_controller.rb
deleted file mode 100644
index 4015605..0000000
--- a/webclient/app/controllers/session_controller.rb
+++ /dev/null
@@ -1,132 +0,0 @@
-require 'yast/service_resource/login'
-require 'yast/service_resource/logout'
-
-# This controller handles the login/logout function of the site.
-class SessionController < ApplicationController
- layout 'main'
-
- # make sure logout only happens if we are logged in
- # and the inverse
- #before_filter :ensure_login, :only => :destroy
- #before_filter :ensure_logout, :only => [:new, :create]
-
- def index
- # only used to display the flash message
- end
-
- # shows the current session
- def show
- end
-
- #
- # Start new session
- # render login screen
- #
- def new
- # we can't create session if we are logged in
- if logged_in?
- redirect_to :action => "index"
- return
- end
-
- # if the hostname is not set, go to the host controller
- # to pickup a service
- if params[:hostname].blank?
- flash[:notice] = _("Please select a host to connect to.") unless flash[:notice]
- redirect_to :controller => 'hosts', :action => 'index'
- return
- end
-
- @hostname = params[:hostname]
-
- # render login screen, asking for username/password
- end
-
- # create session
- # this is called from login form and expects to have username and password set
- #
- # if the create action is called without the hostname
- # it will show the login form
- def create
- hostname = params[:hostname]
- # if the user or password is not there, then render the login form
- if hostname.blank?
- flash[:warning] = _("You need to specify the hostname")
- redirect_to :action => "new"
- elsif params[:login].blank?
- flash[:warning] = _("No login specified")
- redirect_to :action => "new", :hostname => params[:hostname]
- elsif params[:password].blank?
- flash[:warning] = _("No password specified")
- redirect_to :action => "new", :login => params[:login], :hostname => hostname
- else
- # otherwise, we have all the data, try to login
- begin
- self.current_account, auth_token = Account.authenticate(params[:login],
- params[:password],
- hostname)
- # error handling when login to the service is pretty
- # important to get meaningful error messages to the user
- rescue Errno::ECONNREFUSED => e
- flash[:error] = _("Can't connect to host at #{hostname}, make sure the host is up and that the YaST web service is running.")
- redirect_to :action => "new"
- return
- rescue Exception => e
- logger.warn e.to_s
- logger.info e.backtrace.join("\n")
- flash[:error] = _("Exception raised when trying to login to #{hostname}. Please try again")
- redirect_to :action => "new", :hostname => hostname
- return
- end
-
- # Now check if the authentication was successful
-
- if logged_in?
- session[:auth_token] = auth_token
- session[:user] = params[:login]
- session[:host] = params[:hostname]
-
- # evaluate available service resources here or not?
- # @modules = Yast.find(:all)
-
- # FIXME: use URI lib
- @short_host_name = session[:host]
- if @short_host_name.index("://") != nil
- @short_host_name = @short_host_name[@short_host_name.index("://")+3, @short_host_name.length-1] #extract "http(s)://"
- end
-
- # success, go to the main controller
- logger.info "Login success."
- redirect_to "/"
- else
- session[:user] = session[:host] = nil
- #show # getting hosts again
- flash[:warning] = _("Login incorrect. Check your username and password.")
- redirect_to :action => "new", :hostname => params[:hostname]
- end
- end
- end
-
- def destroy
- # remove session data
- [:user, :host].each do |k|
- session[k] = nil
- end
-
- if logged_in?
- ret = YaST::ServiceResource::Logout.create rescue nil
- if (ret and ret.attributes["logout"])
- logger.debug "Logout: #{ret.attributes["logout"]}"
- else
- logger.debug "Logout: Error"
- end
- self.current_account.forget_me
- session[:auth_token] = nil
- end
-
- cookies.delete :auth_token
- reset_session
- flash[:notice] = _("You have been logged out.") unless flash[:notice]
- redirect_to new_session_path
- end
-end
diff --git a/webclient/app/controllers/sessions_controller.rb b/webclient/app/controllers/sessions_controller.rb
new file mode 100644
index 0000000..ce14aa4
--- /dev/null
+++ b/webclient/app/controllers/sessions_controller.rb
@@ -0,0 +1,132 @@
+require 'yast/service_resource/login'
+require 'yast/service_resource/logout'
+
+# This controller handles the login/logout function of the site.
+class SessionsController < ApplicationController
+ layout 'main'
+
+ # make sure logout only happens if we are logged in
+ # and the inverse
+ #before_filter :ensure_login, :only => :destroy
+ #before_filter :ensure_logout, :only => [:new, :create]
+
+ def index
+ # only used to display the flash message
+ end
+
+ # shows the current session
+ def show
+ end
+
+ #
+ # Start new session
+ # render login screen
+ #
+ def new
+ # we can't create session if we are logged in
+ if logged_in?
+ redirect_to :action => "index"
+ return
+ end
+
+ # if the hostname is not set, go to the host controller
+ # to pickup a service
+ if params[:hostname].blank?
+ flash[:notice] = _("Please select a host to connect to.") unless flash[:notice]
+ redirect_to :controller => 'hosts', :action => 'index'
+ return
+ end
+
+ @hostname = params[:hostname]
+
+ # render login screen, asking for username/password
+ end
+
+ # create session
+ # this is called from login form and expects to have username and password set
+ #
+ # if the create action is called without the hostname
+ # it will show the login form
+ def create
+ hostname = params[:hostname]
+ # if the user or password is not there, then render the login form
+ if hostname.blank?
+ flash[:warning] = _("You need to specify the hostname")
+ redirect_to :action => "new"
+ elsif params[:login].blank?
+ flash[:warning] = _("No login specified")
+ redirect_to :action => "new", :hostname => params[:hostname]
+ elsif params[:password].blank?
+ flash[:warning] = _("No password specified")
+ redirect_to :action => "new", :login => params[:login], :hostname => hostname
+ else
+ # otherwise, we have all the data, try to login
+ begin
+ self.current_account, auth_token = Account.authenticate(params[:login],
+ params[:password],
+ hostname)
+ # error handling when login to the service is pretty
+ # important to get meaningful error messages to the user
+ rescue Errno::ECONNREFUSED => e
+ flash[:error] = _("Can't connect to host at #{hostname}, make sure the host is up and that the YaST web service is running.")
+ redirect_to :action => "new"
+ return
+ rescue Exception => e
+ logger.warn e.to_s
+ logger.info e.backtrace.join("\n")
+ flash[:error] = _("Exception raised when trying to login to #{hostname}. Please try again")
+ redirect_to :action => "new", :hostname => hostname
+ return
+ end
+
+ # Now check if the authentication was successful
+
+ if logged_in?
+ session[:auth_token] = auth_token
+ session[:user] = params[:login]
+ session[:host] = params[:hostname]
+
+ # evaluate available service resources here or not?
+ # @modules = Yast.find(:all)
+
+ # FIXME: use URI lib
+ @short_host_name = session[:host]
+ if @short_host_name.index("://") != nil
+ @short_host_name = @short_host_name[@short_host_name.index("://")+3, @short_host_name.length-1] #extract "http(s)://"
+ end
+
+ # success, go to the main controller
+ logger.info "Login success."
+ redirect_to "/"
+ else
+ session[:user] = session[:host] = nil
+ #show # getting hosts again
+ flash[:warning] = _("Login incorrect. Check your username and password.")
+ redirect_to :action => "new", :hostname => params[:hostname]
+ end
+ end
+ end
+
+ def destroy
+ # remove session data
+ [:user, :host].each do |k|
+ session[k] = nil
+ end
+
+ if logged_in?
+ ret = YaST::ServiceResource::Logout.create rescue nil
+ if (ret and ret.attributes["logout"])
+ logger.debug "Logout: #{ret.attributes["logout"]}"
+ else
+ logger.debug "Logout: Error"
+ end
+ self.current_account.forget_me
+ session[:auth_token] = nil
+ end
+
+ cookies.delete :auth_token
+ reset_session
+ flash[:notice] = _("You have been logged out.") unless flash[:notice]
+ redirect_to :controller => 'hosts', :action => 'index'
+ end
+end
diff --git a/webclient/app/views/layouts/main.rhtml b/webclient/app/views/layouts/main.rhtml
index 2408b20..68f537a 100644
--- a/webclient/app/views/layouts/main.rhtml
+++ b/webclient/app/views/layouts/main.rhtml
@@ -47,7 +47,7 @@
<%= link_to _("Logout"), '/logout' %>
<% end %>
</div>
- <% elsif controller_name != "session" && controller_name != "webservices" -%>
+ <% elsif controller_name != "sessions" && controller_name != "webservices" -%>
<div id="login_session">
<%= link_to _("Login"), '/logout' %>
</div>
diff --git a/webclient/app/views/session/new.rhtml b/webclient/app/views/session/new.rhtml
deleted file mode 100644
index 97ccb90..0000000
--- a/webclient/app/views/session/new.rhtml
+++ /dev/null
@@ -1,19 +0,0 @@
-<div class="content">
- <% form_tag( :action => 'create' ) do %>
-
- <span id="content_title">Login</span>
-
- <p><%= _("Enter login credentials for host #{@hostname}.") %></p>
-
- <%= hidden_field_tag 'hostname', @hostname %>
-
- <p><label for="login"><%= _("Username:") %></label><br>
- <%= text_field_tag 'login', @user %></p>
-
- <p><label for="password"><%= _("Password:") %></label><br>
- <%= password_field_tag 'password', @password %></p>
-
- <%= submit_tag(_('Login'), :class => "button", :id => "login_button") %>
-
- <% end %>
-</div>
diff --git a/webclient/app/views/sessions/new.rhtml b/webclient/app/views/sessions/new.rhtml
new file mode 100644
index 0000000..97ccb90
--- /dev/null
+++ b/webclient/app/views/sessions/new.rhtml
@@ -0,0 +1,19 @@
+<div class="content">
+ <% form_tag( :action => 'create' ) do %>
+
+ <span id="content_title">Login</span>
+
+ <p><%= _("Enter login credentials for host #{@hostname}.") %></p>
+
+ <%= hidden_field_tag 'hostname', @hostname %>
+
+ <p><label for="login"><%= _("Username:") %></label><br>
+ <%= text_field_tag 'login', @user %></p>
+
+ <p><label for="password"><%= _("Password:") %></label><br>
+ <%= password_field_tag 'password', @password %></p>
+
+ <%= submit_tag(_('Login'), :class => "button", :id => "login_button") %>
+
+ <% end %>
+</div>
diff --git a/webclient/test/functional/session_controller_test.rb b/webclient/test/functional/session_controller_test.rb
deleted file mode 100644
index bbb87d6..0000000
--- a/webclient/test/functional/session_controller_test.rb
+++ /dev/null
@@ -1,195 +0,0 @@
-require File.dirname(__FILE__) + '/../test_helper'
-
-require 'mocha'
-
-require 'active_resource/http_mock'
-
-# Re-raise errors caught by the controller.
-require 'session_controller'
-class SessionController; def rescue_action(e) raise e end; end
-
-# test the web client part of the login, which
-# talks to a remote rest service to get the authentication
-# token
-class SessionControllerTest < ActionController::TestCase
-
- # Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead
- # Then, you can remove it from this and the units test.
- include AuthenticatedTestHelper
-
- fixtures :accounts
-
- def setup
- @login_granted = "<hash><login>granted</login></hash>"
- @login_denied = "<hash><login>denied</login></hash>"
- @logout_granted = "<hash><logout>Goodbye!</logout></hash>"
-
- @hostname = "http://localhost:8000"
- current_account = Account.new
- auth_token = "abcdef"
- Account.stubs(:authenticate).with("quentin","test",@hostname).returns([current_account, auth_token])
- Account.stubs(:authenticate).with("quentin","bad password",@hostname).returns([nil,nil])
- Account.stubs(:authenticate).with("quentin","exception","exception").raises(RuntimeError)
- Account.stubs(:authenticate).with("quentin","bad host","bad").raises(Errno::ECONNREFUSED)
- YaST::ServiceResource::Session.site = @hostname
- ActiveResource::Base.site = @hostname
- end
-
- # new without any parameters should redirect to hosts
- def test_new
- get :new
- assert_redirected_to :controller => :hosts
- end
-
- # new with :hostname empty
- def test_new_with_empty_hostname
- get :new, :hostname => ""
- assert_redirected_to :controller => :hosts
- assert flash[:notice]
- end
-
- # new with hostname, must show login
- def test_new_shows_login
- get :new, :hostname => @hostname
- assert_select "form input", 4 # hostname, username, password, submit
- end
-
- # without a service hostname to to login, we should
- # be redirected to web service choosing...
- def test_create_should_redirect_to_select_hostname
- post :create
- assert flash[:warning]
- assert_redirected_to :action => :new
- end
-
- # create with blank password
- def test_create_with_blank_password
- post :create, :password => "", :hostname => @hostname
- assert flash[:warning]
- assert_redirected_to :action => :new, :hostname => @hostname
- end
-
- def test_create_successful_login
- ActiveResource::HttpMock.respond_to do |mock|
- mock.post "/login.xml", {}, @login_granted
- end
- post :create, :hostname => @hostname,
- :login => 'quentin', :password => 'test'
- assert_nil flash[:warning]
- assert_nil flash[:error]
- assert session[:auth_token]
- assert session[:user]
- assert session[:host]
- assert_redirected_to "/"
- end
-
- def test_create_with_authentication_failure
- post :create, :login => 'quentin', :password => 'bad password', :hostname => @hostname
- assert_nil session[:account_id]
- assert flash[:warning]
- assert_nil flash[:error]
- # we should be at the login form again
- assert_redirected_to :controller => :session, :action => :new, :hostname => @hostname
- end
-
- def test_create_with_connection_refused
- post :create, :login => 'quentin', :password => 'bad host', :hostname => "bad"
- assert_nil session[:account_id]
- assert_nil flash[:warning]
- assert flash[:error]
- # we should be at the login form again
- assert_redirected_to :controller => :session, :action => :new
- end
-
- def test_create_with_exception_raised
- post :create, :login => 'quentin', :password => 'exception', :hostname => "exception"
- assert_nil flash[:warning]
- assert flash[:error]
- # we should be at the login form again
- assert_redirected_to :controller => :session, :action => :new, :hostname => "exception"
- end
-
-
- def test_should_logout
- ActiveResource::HttpMock.respond_to do |mock|
- mock.post "/logout.xml", {}, nil
- end
-
- login_as :quentin
- delete :destroy
- assert_nil session[:account_id]
- assert_response :redirect
- end
-
- def test_should_remember_me
- ActiveResource::HttpMock.respond_to do |mock|
- mock.post "/login.xml", {}, @login_granted
- end
-
- post :create, :login => 'quentin', :password => 'test', :remember_me => "1"
- # FIXME, not working
- #assert_not_nil @response.session[:auth_token]
- end
-
- def test_should_not_remember_me
- ActiveResource::HttpMock.respond_to do |mock|
- mock.post "/login.xml", {}, @login_granted
- end
-
- post :create, :login => 'quentin', :password => 'test', :remember_me => "0"
- assert_nil @response.session[:auth_token]
- end
-
- def test_should_delete_token_on_logout
- ActiveResource::HttpMock.respond_to do |mock|
- mock.post "/logout.xml", {}, @logout_granted
- end
-
- login_as :quentin
- delete :destroy
- assert_nil @response.cookies[:auth_token]
- end
-
- def test_should_login_with_cookie
- ActiveResource::HttpMock.respond_to do |mock|
- mock.post "/login.xml", {}, @login_granted
- end
-
- accounts(:quentin).remember_me
- @request.cookies["auth_token"] = cookie_for(:quentin)
- get :new
- assert @controller.send(:logged_in?)
- end
-
- def test_should_fail_expired_cookie_login
- ActiveResource::HttpMock.respond_to do |mock|
- mock.post "/login.xml", {}, @login_granted
- end
-
- accounts(:quentin).remember_me
- accounts(:quentin).update_attribute :remember_token_expires_at, 5.minutes.ago
- @request.cookies["auth_token"] = cookie_for(:quentin)
- get :new
- assert !@controller.send(:logged_in?)
- end
-
- def test_should_fail_cookie_login
- ActiveResource::HttpMock.respond_to do |mock|
- mock.post "/login.xml", {}, @login_granted
- end
-
- accounts(:quentin).remember_me
- @request.cookies[:auth_token] = auth_token('invalid_auth_token')
- get :new
- assert !@controller.send(:logged_in?)
- end
-
- protected
- def auth_token(token)
- CGI::Cookie.new('name' => 'auth_token', 'value' => token)
- end
-
- def cookie_for(account)
- auth_token accounts(account).remember_token
- end
-end
diff --git a/webclient/test/functional/sessions_controller_test.rb b/webclient/test/functional/sessions_controller_test.rb
new file mode 100644
index 0000000..44fff44
--- /dev/null
+++ b/webclient/test/functional/sessions_controller_test.rb
@@ -0,0 +1,195 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+require 'mocha'
+
+require 'active_resource/http_mock'
+
+# Re-raise errors caught by the controller.
+require 'sessions_controller'
+class SessionsController; def rescue_action(e) raise e end; end
+
+# test the web client part of the login, which
+# talks to a remote rest service to get the authentication
+# token
+class SessionsControllerTest < ActionController::TestCase
+
+ # Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead
+ # Then, you can remove it from this and the units test.
+ include AuthenticatedTestHelper
+
+ fixtures :accounts
+
+ def setup
+ @login_granted = "<hash><login>granted</login></hash>"
+ @login_denied = "<hash><login>denied</login></hash>"
+ @logout_granted = "<hash><logout>Goodbye!</logout></hash>"
+
+ @hostname = "http://localhost:8000"
+ current_account = Account.new
+ auth_token = "abcdef"
+ Account.stubs(:authenticate).with("quentin","test",@hostname).returns([current_account, auth_token])
+ Account.stubs(:authenticate).with("quentin","bad password",@hostname).returns([nil,nil])
+ Account.stubs(:authenticate).with("quentin","exception","exception").raises(RuntimeError)
+ Account.stubs(:authenticate).with("quentin","bad host","bad").raises(Errno::ECONNREFUSED)
+ YaST::ServiceResource::Session.site = @hostname
+ ActiveResource::Base.site = @hostname
+ end
+
+ # new without any parameters should redirect to hosts
+ def test_new
+ get :new
+ assert_redirected_to :controller => :hosts
+ end
+
+ # new with :hostname empty
+ def test_new_with_empty_hostname
+ get :new, :hostname => ""
+ assert_redirected_to :controller => :hosts
+ assert flash[:notice]
+ end
+
+ # new with hostname, must show login
+ def test_new_shows_login
+ get :new, :hostname => @hostname
+ assert_select "form input", 4 # hostname, username, password, submit
+ end
+
+ # without a service hostname to to login, we should
+ # be redirected to web service choosing...
+ def test_create_should_redirect_to_select_hostname
+ post :create
+ assert flash[:warning]
+ assert_redirected_to :action => :new
+ end
+
+ # create with blank password
+ def test_create_with_blank_password
+ post :create, :password => "", :hostname => @hostname
+ assert flash[:warning]
+ assert_redirected_to :action => :new, :hostname => @hostname
+ end
+
+ def test_create_successful_login
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.post "/login.xml", {}, @login_granted
+ end
+ post :create, :hostname => @hostname,
+ :login => 'quentin', :password => 'test'
+ assert_nil flash[:warning]
+ assert_nil flash[:error]
+ assert session[:auth_token]
+ assert session[:user]
+ assert session[:host]
+ assert_redirected_to "/"
+ end
+
+ def test_create_with_authentication_failure
+ post :create, :login => 'quentin', :password => 'bad password', :hostname => @hostname
+ assert_nil session[:account_id]
+ assert flash[:warning]
+ assert_nil flash[:error]
+ # we should be at the login form again
+ assert_redirected_to :controller => :sessions, :action => :new, :hostname => @hostname
+ end
+
+ def test_create_with_connection_refused
+ post :create, :login => 'quentin', :password => 'bad host', :hostname => "bad"
+ assert_nil session[:account_id]
+ assert_nil flash[:warning]
+ assert flash[:error]
+ # we should be at the login form again
+ assert_redirected_to :controller => :sessions, :action => :new
+ end
+
+ def test_create_with_exception_raised
+ post :create, :login => 'quentin', :password => 'exception', :hostname => "exception"
+ assert_nil flash[:warning]
+ assert flash[:error]
+ # we should be at the login form again
+ assert_redirected_to :controller => :sessions, :action => :new, :hostname => "exception"
+ end
+
+
+ def test_should_logout
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.post "/logout.xml", {}, nil
+ end
+
+ login_as :quentin
+ delete :destroy
+ assert_nil session[:account_id]
+ assert_response :redirect
+ end
+
+ def test_should_remember_me
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.post "/login.xml", {}, @login_granted
+ end
+
+ post :create, :login => 'quentin', :password => 'test', :remember_me => "1"
+ # FIXME, not working
+ #assert_not_nil @response.session[:auth_token]
+ end
+
+ def test_should_not_remember_me
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.post "/login.xml", {}, @login_granted
+ end
+
+ post :create, :login => 'quentin', :password => 'test', :remember_me => "0"
+ assert_nil @response.session[:auth_token]
+ end
+
+ def test_should_delete_token_on_logout
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.post "/logout.xml", {}, @logout_granted
+ end
+
+ login_as :quentin
+ delete :destroy
+ assert_nil @response.cookies[:auth_token]
+ end
+
+ def test_should_login_with_cookie
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.post "/login.xml", {}, @login_granted
+ end
+
+ accounts(:quentin).remember_me
+ @request.cookies["auth_token"] = cookie_for(:quentin)
+ get :new
+ assert @controller.send(:logged_in?)
+ end
+
+ def test_should_fail_expired_cookie_login
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.post "/login.xml", {}, @login_granted
+ end
+
+ accounts(:quentin).remember_me
+ accounts(:quentin).update_attribute :remember_token_expires_at, 5.minutes.ago
+ @request.cookies["auth_token"] = cookie_for(:quentin)
+ get :new
+ assert !@controller.send(:logged_in?)
+ end
+
+ def test_should_fail_cookie_login
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.post "/login.xml", {}, @login_granted
+ end
+
+ accounts(:quentin).remember_me
+ @request.cookies[:auth_token] = auth_token('invalid_auth_token')
+ get :new
+ assert !@controller.send(:logged_in?)
+ end
+
+ protected
+ def auth_token(token)
+ CGI::Cookie.new('name' => 'auth_token', 'value' => token)
+ end
+
+ def cookie_for(account)
+ auth_token accounts(account).remember_token
+ end
+end
--
To unsubscribe, e-mail: yast-commit+unsubscribe@opensuse.org
For additional commands, e-mail: yast-commit+help@opensuse.org