The following example is used to login to Roundup via a Windows NT authentication process. This extension uses Windows SSPI services to enable the authentication process. To validate a user in a Windows Domain Controller use this service along with the NTLM protocol.

You will need the pywin32 extension. Try to install a current one as the SSPI services weren't added until recently (sometime in 2005). I know that version 205 already has it.

This example is just a mix of LDAPLogin published in this wiki and the code in validate.py that's included in pywin32 library.

User profiles are also stored in Roundup database, to minimize code modifications. Also:

In your extensions directory, create a file sspilogin.py with:

  import win32security
  from sspi import ClientAuth, ServerAuth
  from roundup.cgi.actions import LoginAction
  from roundup.i18n import _

  class SSPIloginAction(LoginAction):

      # parameters for NTLM authentication.
      domain = ""
      pkg_name = "NTLM"

      def verifyLocalPassword(self, password):
          ''' Verify the password that the user has supplied '''
          stored = self.db.user.get(self.client.userid, 'password')
          if password == stored:
              return 1
          if not password and not stored:
              return 1
          return 0

      def local_login (self, password):
          ''' Local authentication '''
          # make sure the user exists
          try:
              self.client.userid = self.db.user.lookup(self.client.user)
          except KeyError:
              self.client.error_message.append(_('Unknown user "%s"')%self.client.user)
              return 0
          # verify the password
          if not self.verifyLocalPassword(password):
              self.client.error_message.append(_('Invalid password'))
              return 0
          return 1

      def sspi_login (self, password):
          ''' Authentication via Windows SSPI '''
          try:
              auth_info = self.client.user, self.domain, password
              ca = ClientAuth(self.pkg_name, auth_info = auth_info)
              sa = ServerAuth(self.pkg_name)
              data = err = None
              while err != 0:
                  err, data = ca.authorize(data)
                  err, data = sa.authorize(data)
              # If we get here without exception, we worked!
          except win32security.error, details:
              hr, func, msg = details
              self.client.error_message.append (_('Unknown SSPI account "%s"')% self.client.user)
              self.client.error_message.append (_('SSPIError = %s (%d)"') % (msg, hr))
              return 0
          return 1

      def verifyLogin(self, username, password):
          # try to login throught SSPI or with local account
          sspi_ok = None
          if not self.local_login(password):
              sspi_ok = self.sspi_login(password)
              if not sspi_ok:
                  self.client.make_user_anonymous ()
                  return
          self.client.error_message = []
          # reload user profile
          try:
              self.client.userid = self.db.user.lookup(self.client.user)
          except:
              self.client.make_user_anonymous()
              self.client.error_message.append(_("No account created without SSPI account"))
          return

  def init(instance):
      instance.registerAction('login', SSPIloginAction)

You only need to specify the Windows Domain of your users in case Roundup is not running in a server that belongs to this same domain. Otherwise leave it blank.

Regards,

-Hernan. (hfoffani@gmail.com)