# -*- coding: utf8 -*-
from .interface import BUIhandler, BUIuser, BUIloader
from ..._compat import ConfigParser


class BasicLoader(BUIloader):
    """The :class:`burpui.misc.auth.basic.BasicLoader` class loads the *Basic*
    users.
    """
    def __init__(self, app=None, handler=None):
        """:func:`burpui.misc.auth.basic.BasicLoader.__init__` loads users from
        the configuration file.

        :param app: Instance of the app we are running in
        :type app: :class:`burpui.server.BUIServer`
        """
        self.app = app
        self.users = {
            'admin': 'admin'
        }
        conf = self.app.config['CFG']
        c = ConfigParser.ConfigParser()
        c.optionxform = str
        with open(conf) as fp:
            c.readfp(fp)
            if c.has_section('BASIC'):
                self.users = {}
                for opt in c.options('BASIC'):
                    if opt == 'priority':
                        # Maybe the handler argument is None, maybe the 'priority'
                        # option is missing. We don't care.
                        try:
                            handler.priority = c.getint('BASIC', opt)
                        except:
                            pass
                        continue  # pragma: no cover
                    self.users[opt] = c.get('BASIC', opt)
                    self.logger.info('Loading user: {}'.format(opt))

    def fetch(self, uid=None):
        """:func:`burpui.misc.auth.basic.BasicLoader.fetch` searches for a user
        in the configuration.

        :param uid: User to search for
        :type uid: str

        :returns: The given UID if the user exists or None
        """
        if uid in self.users:
            return uid

        return None

    def check(self, uid=None, passwd=None):
        """:func:`burpui.misc.auth.basic.BasicLoader.check` verifies if the
        given password matches the given user settings.

        :param uid: User to authenticate
        :type uid: str

        :param passwd: Password
        :type passwd: str

        :returns: True if there is a match, otherwise False
        """
        return uid in self.users and self.users[uid] == passwd


class UserHandler(BUIhandler):
    """See :class:`burpui.misc.auth.interface.BUIhandler`"""
    def __init__(self, app=None):
        """See :func:`burpui.misc.auth.interface.BUIhandler.__init__`"""
        self.basic = BasicLoader(app, self)
        self.users = {}

    def user(self, name=None):
        """See :func:`burpui.misc.auth.interface.BUIhandler.user`"""
        if name not in self.users:
            self.users[name] = BasicUser(self.basic, name)
        return self.users[name]


class BasicUser(BUIuser):
    """See :class:`burpui.misc.auth.interface.BUIuser`"""
    def __init__(self, basic=None, name=None):
        self.active = False
        self.authenticated = False
        self.basic = basic
        self.name = name
        self.id = None

        res = self.basic.fetch(self.name)

        if res:
            self.id = res
            self.active = True

    def login(self, name=None, passwd=None):
        """See :func:`burpui.misc.auth.interface.BUIuser.login`"""
        self.authenticated = self.basic.check(name, passwd)
        return self.authenticated

    def get_id(self):
        return self.id

@@ -16,16 +16,27 @@
 tf8 -*-%0A
+import re%0A%0A
 from .in
@@ -90,38 +90,76 @@
 rom 
-..._compat import ConfigParser
+werkzeug.security import check_password_hash, generate_password_hash
 %0A%0A%0Ac
@@ -685,185 +685,339 @@
 conf
-ig%5B'CFG'%5D%0A        c = ConfigParser.ConfigParser()%0A        c.optionxform = str%0A        with open(conf) as fp:%0A            c.readfp(fp)%0A            if c.has_section('BASIC'):%0A    
+%0A        if 'BASIC' in conf.options:%0A            # check passwords are salted%0A            salted = False%0A            if len(conf.options.comments%5B'BASIC'%5D) %3E 0:%0A                if re.match(%0A                        r'%5E%5Cs*#+%5Cs*@salted@',%0A                        conf.options.comments%5B'BASIC'%5D%5B-1%5D):%0A                    salted = True%0A
     
@@ -1052,20 +1052,16 @@
         
-    
 for opt 
@@ -1064,24 +1064,27 @@
 opt in c
+onf
 .options
 ('BASIC'
@@ -1075,16 +1075,20 @@
 .options
+.get
 ('BASIC'
@@ -1088,22 +1088,25 @@
 'BASIC')
-:%0A    
+.keys():%0A
         
@@ -1135,20 +1135,16 @@
 ority':%0A
-    
         
@@ -1234,20 +1234,16 @@
         
-    
 # option
@@ -1294,21 +1294,13 @@
     
-    
 try:%0A
-    
     
@@ -1343,28 +1343,41 @@
  = c
-.getint(
+onf.safe_get(opt, section=
 'BASIC'
-, opt
 )%0A  
@@ -1394,20 +1394,16 @@
         
-    
 except:%0A
@@ -1430,21 +1430,13 @@
     
-    
 pass%0A
-    
     
@@ -1500,54 +1500,226 @@
     
-    self.users%5Bopt%5D = c.get(
+pwd = conf.safe_get(opt, section='BASIC')%0A                if not salted:%0A                    pwd = generate_password_hash(pwd)%0A                    conf.options%5B
 'BASIC'
-, opt)%0A    
+%5D%5Bopt%5D = pwd%0A                self.users%5Bopt%5D = pwd%0A
     
@@ -1780,16 +1780,285 @@
 (opt))%0A%0A
+            if not salted:%0A                conf.options.comments%5B'BASIC'%5D.append(%0A                    '# Please DO NOT touch the following line'%0A                )%0A                conf.options.comments%5B'BASIC'%5D.append('# @salted@')%0A                conf.options.write()%0A%0A
     def 
@@ -2810,16 +2810,50 @@
 ers and 
+%5C%0A            check_password_hash(
 self.use
@@ -2863,18 +2863,17 @@
 uid%5D
- ==
+,
  passwd
+)
 %0A%0A%0Ac

use hashed passwords (see #130)