# -*- coding: utf-8 -*-
"""
w2lapp.secinfo.py: Display (SSL) connection data

web2ldap - a web-based LDAP Client,
see http://www.web2ldap.de for details

(c) by Michael Stroeder <michael@stroeder.com>

This module is distributed under the terms of the
GPL (GNU GENERAL PUBLIC LICENSE) Version 2
(see http://www.gnu.org/copyleft/gpl.html)
"""

import time,ldap,ldapsession,utctime,w2lapp.cnf,pyweblib.sslenv,ldaputil.base,w2lapp.core,w2lapp.gui,msgzip

from w2lapp.session import session

##############################################################################
# Connection info
##############################################################################

def w2l_ConnInfo(sid,outf,command,form,ls,dn,env):

  protocol_version = ls.l.get_option(ldap.OPT_PROTOCOL_VERSION)

  conninfo_flushcaches = int(form.getInputValue('conninfo_flushcaches',['0'])[0])
  if conninfo_flushcaches:
    ls.flushCache()

  context_menu_list = []

  # List of candidate DNs for probing configuration information
  config_dn_list = []

  monitored_info = None
  if ls.rootDSE.has_key('monitorContext'):
    context_menu_list.append(form.applAnchor('read','Monitor',sid,[('dn',ls.rootDSE['monitorContext'][0])]))
    try:
      monitor_context_dn = ls.rootDSE['monitorContext'][0]
    except KeyError:
      pass
    else:
      try:
        monitored_info = ls.readEntry(monitor_context_dn,['monitoredInfo'])[0][1]['monitoredInfo']
      except (ldap.LDAPError,KeyError):
        pass
      else:
        context_menu_list.append(form.applAnchor(
          'search','My connections',sid,
          [
            ('dn',monitor_context_dn),
            ('filterstr','(&(objectClass=monitorConnection)(monitorConnectionAuthzDN=%s))' % (ldap.filter.escape_filter_chars(ls.who or ''))),
            ('scope',str(ldap.SCOPE_SUBTREE)),
          ],
          title=u'Find own connections in Monitor database',
        ))
  else:
    config_dn_list.append(('CN=MONITOR','Monitor'))

  if ls.rootDSE.has_key('changelog'):
    # OpenLDAP 2.3+
    context_menu_list.append(form.applAnchor('read','Change log',sid,[('dn',ls.rootDSE['changelog'][0])]))
  else:
    config_dn_list.append(('cn=changelog','Change log'))

  if ls.rootDSE.has_key('configContext'):
    # OpenLDAP 2.3+
    context_menu_list.append(form.applAnchor('read','Config',sid,[('dn',ls.rootDSE['configContext'][0])]))
  elif ls.rootDSE.has_key('configurationNamingContext'):
    # MS AD
    context_menu_list.append(form.applAnchor('read','AD Configuration',sid,[('dn',ls.rootDSE['configurationNamingContext'][0])]))
  elif ls.rootDSE.has_key('ibm-configurationnamingcontext'):
    # IBM Directory Server
    context_menu_list.append(form.applAnchor('read','IBM DS Configuration',sid,[('dn',ls.rootDSE['ibm-configurationnamingcontext'][0])]))
  else:
    config_dn_list.extend([
      ('CN=CONFIG','Config'),
      ('CN=Configuration','Configuration'),
      ('cn=ldbm','LDBM Database'),
      ('ou=system','System'),
    ])

  current_audit_context = ls.getAuditContext(ls.currentSearchRoot)
  if not current_audit_context is None:
    context_menu_list.extend([
      form.applAnchor('read','Audit DB',sid,[('dn',current_audit_context)]),
      form.applAnchor(
        'search','Audit my access',sid,
        [
          ('dn',current_audit_context),
          ('filterstr','(&(objectClass=auditObject)(reqAuthzID=%s))' % (ldap.filter.escape_filter_chars(ls.who or ''))),
          ('scope',str(ldap.SCOPE_ONELEVEL)),
        ],
        title=u'Complete audit trail for currently bound identity',
      ),
      form.applAnchor(
        'search','Audit my writes',sid,
        [
          ('dn',current_audit_context),
          ('filterstr','(&(objectClass=auditWriteObject)(reqAuthzID=%s))' % (ldap.filter.escape_filter_chars(ls.who or ''))),
          ('scope',str(ldap.SCOPE_ONELEVEL)),
        ],
        title=u'Audit trail of write access by currently bound identity',
      ),
      form.applAnchor(
        'search','Last logins',sid,
        [
          ('dn',current_audit_context),
          ('filterstr','(&(objectClass=auditBind)(reqDN=%s))' % (ldap.filter.escape_filter_chars(ls.who or ''))),
          ('scope',str(ldap.SCOPE_ONELEVEL)),
        ],
        title=u'Audit trail of last logins (binds) by currently bound identity',
      ),
    ])

  for config_dn,txt in config_dn_list:
    try:
      entry_exists = ls.existingEntry(config_dn,suppress_referrals=1)
    except ldap.LDAPError:
      pass
    else:
      if entry_exists:
        context_menu_list.append(form.applAnchor('read',txt,sid,[('dn',config_dn)]))

  if ls.rootDSE.has_key('schemaNamingContext'):
    # MS AD schema configuration
    context_menu_list.append(form.applAnchor('read','AD Schema Configuration',sid,[('dn',ls.rootDSE['schemaNamingContext'][0])]))

  w2lapp.gui.TopSection(
    sid,outf,form,ls,dn,
    'Connection Info',
    w2lapp.gui.MainMenu(sid,form,ls,dn),
    context_menu_list=context_menu_list
  )

  if ls.who:
    who_html = ' | '.join([
        w2lapp.gui.DisplayDN(sid,form,ls,ls.who,commandbutton=0),
        form.applAnchor('read','Read',sid,[('dn',ls.who)]),
        form.applAnchor('passwd','Password',sid,[('dn',ls.who),('passwd_who',ls.who)]),
      ])
  else:
    who_html = 'anonymous'

  try:
    whoami_result = '&quot;%s&quot;' % (form.utf2display(unicode(str(ls.l.whoami_s()),ls.charset)))
  except ldap.LDAPError,e:
    whoami_result = '<strong>Failed:</strong> %s' % (w2lapp.gui.LDAPError2ErrMsg(e,form,ls.charset))

  if ls.saslAuth:
    sasl_mech = u'SASL/%s' % (ls.saslAuth.mech)
    sasl_auth_info = '<table>%s</table>' % '\n'.join([
        '<tr><td>%s</td><td>%s</td></tr>' % (
          form.utf2display(unicode(ldaputil.base.LDAP_OPT_NAMES_DICT.get(k,str(k)),'ascii')),
          form.utf2display(unicode(repr(v),ls.charset))
        )
        for k,v in ls.saslAuth.cb_value_dict.items()
        if v
      ])
  else:
    sasl_mech = u'simple'
    sasl_auth_info = 'SASL not used'

  if ldap.SASL_AVAIL:
    try:
      sasl_ssf = unicode(ls.l.get_option(ldap.OPT_X_SASL_SSF))
    except ldap.LDAPError,e:
      sasl_ssf = u'error reading option: %s' % (w2lapp.gui.LDAPError2ErrMsg(e,form,ls.charset))
    except ValueError:
      sasl_ssf = u'option not available'
  else:
    sasl_ssf = u'no SASL support in python-ldap'

  vendor_name = unicode(
    (
      ls.rootDSE.get('vendorName','') or \
      monitored_info or \
      [{True:'OpenLDAP',False:''}['OpenLDAProotDSE' in ls.rootDSE.get('objectClass',[])]] or \
      ['unknown']
    )[0],
    ls.charset
  )

  outf.write("""
    <div id="Message" class="Main">

    <h1>LDAP Connection Parameters</h1>
    <h2>LDAP connection</h2>
    <table summary="LDAP connection">
      <tr>
        <td>LDAPv%d connection to:</td>
        <td>%s (%s, %s)</td>
      </tr>
      <tr>
        <td>Connection start time:</td>
        <td>%s</td>
      </tr>
      <tr>
        <td>Connection duration:</td>
        <td>%d secs</td>
      </tr>
      <tr>
        <td>Server vendor info:</td>
        <td>%s %s</td>
      </tr>
      <tr>
        <td>Bound as:</td>
        <td>%s</td>
      </tr>
      <tr>
        <td>Result <em>Who am I?</em>:</td>
        <td>%s</td>
      </tr>
      <tr>
        <td>Bind mechanism used:</td>
        <td>%s</td>
      </tr>
      <tr>
        <td>SASL auth info:</td>
        <td>%s</td>
      </tr>
      <tr>
        <td>SASL SSF info:</td>
        <td>%s</td>
      </tr>
    </table>
    """ % (
      protocol_version,
      ls.uri.encode('ascii'),
      ls.charset.upper(),
      {0:'not secured',1:'secured'}[ls.secureConn],
      utctime.strftimeiso8601(time.gmtime(ls.connStartTime)),
      time.time()-ls.connStartTime,
      form.utf2display(vendor_name),
      form.utf2display(
        unicode(
          (ls.rootDSE.get('vendorVersion','') or [''])[0],
          ls.charset
        ),
      ),
      who_html,
      whoami_result,
      form.utf2display(sasl_mech),
      sasl_auth_info,
      form.utf2display(sasl_ssf),
    )
  )

  outf.write("""
    <h3>LDAP cache information</h3>
    <p>%s</p>
    <table id="LDAPCacheTable" summary="LDAP cache information">
      <tr>
        <td>Cached searches:</td>
        <td>%d</td>
      </tr>
      <tr>
        <td>Cached subschema DN mappings:</td>
        <td>%d</td>
      </tr>
      <tr>
        <td>Cached subschema subentries:</td>
        <td>%d</td>
      </tr>
      <tr>
        <td>Cache hit ratio:</td>
        <td>%d %%</td>
      </tr>
    </table>
    """ % (
      form.applAnchor(
        'conninfo','Flush all caches',sid,
        [
          ('dn',dn),
          ('conninfo_flushcaches','1'),
        ],
        title=u'Flush all cached information for this LDAP connection'
      ),
      len(ls.l._cache),
      len(ls.schema_dn_cache),
      len(ls.schema_cache),
      round(ls.l.get_cache_hit_ratio() or 0.0),
    )
  )

  cross_check_vars = session.sessiondict['__session_checkvars__'+sid].items()
  cross_check_vars.sort()
  cross_check_vars_html = '\n'.join([
    '<tr><td>%s</td><td>%s</td></tr>' % (
      form.utf2display(unicode(k,form.accept_charset)),
      form.utf2display(unicode(v,form.accept_charset)),
    )
    for k,v in cross_check_vars
  ])

  if isinstance(outf,msgzip.GzipFile):
    compresslevel = outf.compresslevel
  else:
    compresslevel = None
  outf.write("""
    <h2>HTTP connection</h2>
    <table summary="HTTP connection">
      <tr><td>Your IP address:</td><td>%s</td></tr>
      <tr><td>direct remote address/port:</td><td>%s:%s</td></tr>
      <tr><td>Server signature:</td><td>%s</td></tr>
      <tr><td>Character set/encoding:</td><td>%s</td></tr>
      <tr><td>GZIP compression level:</td><td>%s</td></tr>
      <tr>
        <td>Cross-check vars in use:</td>
        <td>
          <table summary="Cross-check vars">
            %s
          </table>
        </td>
      </tr>
      <tr><td>User-Agent header:</td><td>%s</td></tr>
      <tr><td>Browser detected:</td><td>%s %s</td></tr>
    </table>
    <h3>SSL</h3>
    """ % (
      w2lapp.core.guessClientAddr(env),
      form.utf2display(unicode(form.env.get('REMOTE_ADDR',''))),
      form.utf2display(unicode(form.env.get('REMOTE_PORT',''))),
      form.env.get('SERVER_SIGNATURE',''),
      form.utf2display(unicode(form.accept_charset.upper())),
      str(compresslevel),
      cross_check_vars_html,
      form.utf2display(unicode(form.env.get('HTTP_USER_AGENT',''),form.accept_charset)),
      form.browser_type or '',
      form.browser_version or '',
    )
  )
  ssl_valid_dn = w2lapp.cnf.GetParam(ls,'ssl_valid_dn','')
  ssl_valid_idn = w2lapp.cnf.GetParam(ls,'ssl_valid_idn','')
  pyweblib.sslenv.PrintSecInfo(env,w2lapp.cnf.misc.sec_sslacceptedciphers,ssl_valid_dn,ssl_valid_idn,outf)
  outf.write('</div>')
  w2lapp.gui.PrintFooter(outf,form)
