Пример #1
0
 def _get_token(self):
     ''' obtengo un token mediante el flujo client_credentials para poder llamar a la api de usuarios '''
     grant = ClientCredentialsGrant(self.oidc_url, self.client_id, self.client_secret, verify=self.verify_ssl)
     token = grant.get_token(grant.access_token())
     if not token:
         raise Exception()
     return token
Пример #2
0
    def login(cls, usuario, clave):

        ''' obtengo un token mediante el flujo client_credentials para poder llamar a la api de usuarios '''
        grant = ClientCredentialsGrant(cls.client_id, cls.client_secret)
        token = grant.get_token(grant.access_token())
        if not token:
            raise LoginError()


        ''' se deben cheqeuar intentos de login, y disparar : SeguridadError en el caso de que se haya alcanzado el máximo de intentos '''
        headers = {
            'Authorization': 'Bearer {}'.format(token)
        }
        r = requests.post(cls.USERS_API_URL + '/auth', verify=cls.verify, headers=headers, json={'usuario':usuario, 'clave':clave})
        if r.status_code == 200:
            clave_data = r.json()
            usuario_id = clave_data['usuario_id']
            r = requests.get(cls.USERS_API_URL + '/usuarios/{}'.format(usuario_id), headers=headers, verify=cls.verify)
            if r.status_code == 200:
                return r.json()
        logging.debug(r)
        
        if r.status_code == 403:
            raise ClaveError()

        if r.status_code == 404:
            raise UsuarioNoEncontradoError()

        raise LoginError()
Пример #3
0
 def __init__(self,
              oidc_url,
              client_id,
              client_secret,
              users_api_url,
              verify=True):
     self.cc = ClientCredentialsGrant(oidc_url, client_id, client_secret,
                                      verify)
     self.users_api_url = users_api_url
     self.verify = verify
Пример #4
0
class UsersApi:
    def __init__(self,
                 oidc_url,
                 client_id,
                 client_secret,
                 users_api_url,
                 verify=True):
        self.cc = ClientCredentialsGrant(oidc_url, client_id, client_secret,
                                         verify)
        self.users_api_url = users_api_url
        self.verify = verify

    def get_token(self):
        r = self.cc.access_token()
        tk = self.cc.get_token(r)
        return tk

    def get_auth_headers(self, tk):
        headers = {
            'Authorization': 'Bearer {}'.format(tk),
            'Accept': 'application/json'
        }
        return headers

    def obtener_usuario_por_dni(self, headers, dni):
        url = f"{self.users_api_url}/usuario_por_dni/{dni}"
        r = requests.get(url,
                         verify=self.verify,
                         allow_redirects=False,
                         headers=headers)
        return r.json()

    def obtener_usuarios(self, headers, uids=[]):
        puids = '+'.join(uids)
        url = f"{self.users_api_url}/usuarios/{puids}"
        r = requests.get(url,
                         verify=self.verify,
                         allow_redirects=False,
                         headers=headers)
        return r.json()

    def buscar_usuarios(self, headers, search=None):
        if not search:
            return []
        params = {'q': search}
        r = requests.get(self.users_api_url + f'/usuarios',
                         verify=self.verify,
                         allow_redirects=False,
                         headers=headers,
                         params=params)
        return r.json()
Пример #5
0
 def __init__(self,
              oidc_url,
              api_url,
              client_id,
              client_secret,
              verify=True,
              realm=''):
     self.verify = verify
     self.realm = realm
     self.warden_url = api_url
     self.client_id = client_id
     self.client_secret = client_secret
     self.client_credentials = ClientCredentialsGrant(
         oidc_url, self.client_id, self.client_secret, self.verify)
class RecuperarClaveModel:

    verify = bool(int(os.environ.get('VERIFY_SSL', 0)))
    INTERNAL_DOMAINS = os.environ['INTERNAL_DOMAINS'].split(',')
    OIDC_URL = os.environ['OIDC_URL']
    OIDC_ADMIN_URL = os.environ['OIDC_ADMIN_URL']
    client_id = os.environ['OIDC_CLIENT_ID']
    client_secret = os.environ['OIDC_CLIENT_SECRET']
    REDIS_HOST = os.environ.get('REDIS_HOST', '127.0.0.1')
    REDIS_PORT = int(os.environ.get('REDIS_PORT', 6379))
    RESET_CLAVE_FROM = os.environ.get('RESET_CLAVE_FROM', '')

    USERS_API_URL = os.environ['USERS_API_URL']

    grant = ClientCredentialsGrant(OIDC_URL,
                                   client_id,
                                   client_secret,
                                   verify=verify)
    redis = redis.StrictRedis(host=REDIS_HOST,
                              port=REDIS_PORT,
                              decode_responses=True)
    """
    ////////////// MANEJO DE CACHE ////////////////////////
    """
    @classmethod
    def _obtener_usuario_api(cls, url, token=None):
        if not token:
            token = cls._obtener_token()
        headers = {'Authorization': 'Bearer {}'.format(token)}
        r = requests.get(url,
                         headers=headers,
                         verify=cls.verify,
                         allow_redirects=False)
        if not r.ok:
            logging.debug(r)
            raise Exception('error obteniendo usuario')
        usrs = r.json()
        if not usrs or len(usrs) <= 0:
            return None
        usr = None
        try:
            usr = usrs[0]
        except KeyError as e:
            usr = usrs

        assert 'id' in usr
        cls._setear_usuario_cache(usr)
        return usr

    @classmethod
    def _setear_usuario_cache(cls, usr):
        expire = 60 * 5
        uid = usr['id']
        kmails = 'r_usuario_mails_{}'.format(uid)
        cls.redis.delete(kmails)
        for m in usr['mails']:
            mid = m['id']
            k = 'r_mail_{}'.format(mid)
            cls.redis.hmset(k, m)
            cls.redis.expire(k, expire * 2)
            cls.redis.sadd(kmails, mid)
        cls.redis.expire(kmails, expire * 2)

        k = 'r_usuario_uid_{}'.format(uid)
        cls.redis.hmset(k, usr)
        cls.redis.expire(k, expire)

        k = 'r_usuario_dni_{}'.format(usr['dni'].lower().replace(' ', ''))
        cls.redis.hset(k, 'uid', uid)
        cls.redis.expire(k, expire)

    @classmethod
    def _eliminar_usuario_de_cache(cls, uid):
        k = 'r_usuario_mails_{}'.format(uid)
        cls.redis.delete(k)
        k = 'r_usuario_uid_{}'.format(uid)
        cls.redis.delete(k)

    @classmethod
    def _format_mail_from_redis(cls, m):
        for k in m.keys():
            if m[k] == 'None':
                m[k] = None
        return m

    @classmethod
    def _obtener_correo_cache(cls, mid):
        key = 'r_mail_{}'.format(mid)
        if not cls.redis.hexists(key, 'id'):
            return None
        mail = cls.redis.hgetall(key)
        fmail = cls._format_mail_from_redis(mail)
        return fmail

    @classmethod
    def _obtener_usuario_cache(cls, uid):
        assert uid is not None
        usr = cls.redis.hgetall('r_usuario_uid_{}'.format(uid))
        if len(usr.keys()) > 0:
            try:
                uid = usr['id']
                mailids = cls.redis.smembers('r_usuario_mails_{}'.format(uid))
                mails = [
                    cls._obtener_correo_cache(mid) for mid in mailids if mid
                ]
                usr['mails'] = [m for m in mails if m]
                return usr
            except Exception as e:
                logging.exception(e)
        return None

    @classmethod
    def _obtener_usuario_por_uid(cls, uid, token=None):
        usr = cls._obtener_usuario_cache(uid)
        if usr:
            return usr
        url = '{}/usuarios/{}'.format(cls.USERS_API_URL, uid)
        usr = cls._obtener_usuario_api(url, token)
        return usr

    @classmethod
    def _obtener_usuario_por_dni(cls, dni, token=None):
        key = 'r_usuario_dni_{}'.format(dni.lower().replace(' ', ''))
        if cls.redis.hexists(key, 'uid'):
            uid = cls.redis.hget(key, 'uid')
            return cls._obtener_usuario_por_uid(uid, token)

        url = '{}/usuario_por_dni/{}'.format(cls.USERS_API_URL, dni)
        usr = cls._obtener_usuario_api(url, token)
        return usr

    @classmethod
    def _obtener_token(cls):
        token = cls.grant.get_token(cls.grant.access_token())
        if not token:
            raise Exception('error obteniendo token de acceso')
        return token

    """
        //////////////////////////
    """

    @classmethod
    def _obtener_correo_alternativo(cls, usr):
        correo = None
        correos_validos = [
            m for m in usr['mails'] if not m['eliminado'] and m['confirmado']
        ]
        correo = [
            m for m in correos_validos
            if m['email'].split('@')[1] not in cls.INTERNAL_DOMAINS
        ]
        if len(correo) <= 0:
            return None
        else:
            return correo[0]

    @classmethod
    def verificar_dni(cls, dni):
        usr = cls._obtener_usuario_por_dni(dni)
        if not usr:
            return None
        c = cls._obtener_correo_alternativo(usr)
        if c:
            return {'tiene_correo': True, 'usuario': usr}
        else:
            return {'tiene_correo': False, 'usuario': usr}

    @classmethod
    def obtener_correo(cls, uid):
        usr = cls._obtener_usuario_por_uid(uid)
        if not usr:
            raise Exception('No existe ese usuario')
        correo = cls._obtener_correo_alternativo(usr)
        if not correo:
            raise Exception('No tiene correo alternativo')
        """ genero el ofuscamiento de la direccion """
        mail = correo['email']
        cs = mail.split('@')
        ayuda = None
        if len(cs[0]) <= 3:
            ayuda = mail
        else:
            l = int(len(cs[0]) / 2)
            ayuda = cs[0][:-l] + ('*' * l) + '@' + cs[1]

        r = {'correo': {'id': correo['id'], 'ayuda': ayuda}, 'usuario': usr}
        return r

    @classmethod
    def _generar_codigo(cls):
        return str(uuid.uuid4())[:8]

    @classmethod
    def _enviar_codigo_template(cls, usuario, codigo, correo):
        templ = MailsModel.obtener_template('codigo.tmpl')
        text = templ.render(usuario=usuario, codigo=codigo)
        r = MailsModel.enviar_correo(cls.RESET_CLAVE_FROM, correo,
                                     'Reseteo de Clave FCE', text)
        if r.ok:
            logging.debug('correo enviado correctamente')
        else:
            logging.debug('error enviando correo')

    @classmethod
    def enviar_codigo(cls, session, eid, correo):
        correo = correo.lower().strip()
        mail = cls._obtener_correo_cache(eid)
        if not mail:
            return None

        if correo not in mail['email'].lower().strip():
            return None

        uid = mail['usuario_id']
        usuario = cls._obtener_usuario_por_uid(uid)
        if not usuario:
            return None

        codigo = None
        intentos = session.query(ResetClave).filter(
            ResetClave.correo == correo, ResetClave.confirmado == None).all()
        for rc in intentos:
            codigo = rc.codigo
            break
        else:
            codigo = cls._generar_codigo()

        rid = str(uuid.uuid4())
        rc = ResetClave()
        rc.codigo = codigo
        rc.correo = correo
        rc.usuario_id = uid
        rc.id = rid
        session.add(rc)

        cls._enviar_codigo_template(usuario, codigo, correo)

        return rid

    @classmethod
    def detalle_recuperar_clave(cls, session):
        rc = session.query(ResetClave).all()
        return rc

    @classmethod
    def _generar_clave(cls):
        return str(uuid.uuid4())[:8]

    @classmethod
    def _enviar_clave_template(cls, usuario, clave, correo):
        templ = MailsModel.obtener_template('clave_temporal.tmpl')
        text = templ.render(usuario=usuario, clave=clave)
        r = MailsModel.enviar_correo(cls.RESET_CLAVE_FROM, correo,
                                     'Reseteo de Clave FCE', text)
        if r.ok:
            logging.debug('correo enviado correctamente')
        else:
            logging.debug('error enviando correo')

    @classmethod
    def _cambiar_clave(cls, session, usuario, clave, es_temporal=True):

        uid = usuario['id']
        dni = usuario['dni']
        cs = session.query(UsuarioClave).filter(
            UsuarioClave.usuario_id == uid).all()
        for c in cs:
            c.eliminada = datetime.datetime.now()

        uc = UsuarioClave()
        uc.usuario_id = uid
        uc.usuario = dni
        uc.clave = clave
        uc.dirty = True
        uc.debe_cambiarla = es_temporal
        if es_temporal:
            uc.expiracion = datetime.datetime.now() + datetime.timedelta(
                days=5)
        else:
            ''' a google solo se sincronizan las claves que no son temporales '''
            uc.google = True
        session.add(uc)
        session.commit()

        #try:
        #    GoogleModel.sincronizar_dirty(session)
        #except Exception as e:
        #    logging.exception(e)

    @classmethod
    def verificar_codigo(cls, session, iid, codigo):
        assert iid is not None
        assert codigo is not None

        rc = session.query(ResetClave).filter(
            ResetClave.id == iid, ResetClave.codigo == codigo).one_or_none()
        if not rc:
            return None

        uid = rc.usuario_id
        correo = rc.correo

        usuario = cls._obtener_usuario_por_uid(uid)
        if not usuario:
            raise Exception('no se pudo obtener el usuario')

        confirmado = datetime.datetime.now()
        actualizado = datetime.datetime.now()
        clave = cls._generar_clave()

        cls._cambiar_clave(session, usuario, clave, es_temporal=True)

        rcs = session.query(ResetClave).filter(
            ResetClave.codigo == codigo, ResetClave.correo == correo).all()
        for rc in rcs:
            rc.actualizado = actualizado
            rc.confirmado = confirmado
            rc.clave = clave

        session.commit()

        try:
            cls._enviar_clave_template(usuario, clave, correo)
        except Exception as e:
            logging.exception(e)

        try:
            cls._eliminar_usuario_de_cache(uid)
        except Exception as e:
            logging.exception(e)

        return clave

    @classmethod
    def cambiar_clave(cls, session, uid, clave, es_temporal=False):
        usuario = cls._obtener_usuario_por_uid(uid)
        if not usuario:
            raise Exception('no se pudo obtener el usuario')
        cls._cambiar_clave(session, usuario, clave, es_temporal)

    @classmethod
    def recuperar_cambiar_clave(cls, session, cid, clave):
        """
            Para permitir varios tipos de flujos, el verificar código genera una clave temporal
            y este método permite cambiar la clave temporal por una fija, si es que como cid se pasa la temporal.
        """
        uc = session.query(UsuarioClave).filter(
            UsuarioClave.clave == cid).one()
        uid = uc.usuario_id
        cls.cambiar_clave(session, uid, clave, es_temporal=False)

    @classmethod
    def precondiciones(cls, session, uid):
        ''' por ahora solo chequeo que no tenga clave temporal '''
        ahora = datetime.datetime.now()
        q = session.query(UsuarioClave).filter(UsuarioClave.usuario_id == uid,
                                               UsuarioClave.eliminada == None)
        #existe un problema con la zona horaria de la fecha por lo que siempre da expirada. asi que por ahora lo comento
        #q.filter(or_(UsuarioClave.debe_cambiarla == True, UsuarioClave.expiracion <= ahora))
        q = q.filter(UsuarioClave.debe_cambiarla == True)
        claves_temporales = q.count()
        return {'clave': claves_temporales <= 0}
finally:
    con.close()
"""
    comienza la parte de ejecución sobre los sistemas actuales
"""

import requests

oidc_url = os.environ['OIDC_URL']
client_id = os.environ['OIDC_CLIENT_ID']
client_secret = os.environ['OIDC_CLIENT_SECRET']
verify = False
users_api_url = os.environ['USERS_API_URL']

from oidc.oidc import ClientCredentialsGrant
cc = ClientCredentialsGrant(oidc_url, client_id, client_secret, verify)


def _get_token(cc):
    r = cc.access_token()
    tk = cc.get_token(r)
    return tk


def _get_auth_headers(tk):
    headers = {
        'Authorization': 'Bearer {}'.format(tk),
        'Accept': 'application/json'
    }
    return headers
Пример #8
0
class LoginModel:

    verify = VERIFY_SSL
    OIDC_URL = OIDC_URL
    OIDC_ADMIN_URL = OIDC_ADMIN_URL
    client_id = CLIENT_ID
    client_secret = CLIENT_SECRET

    api = API(url=OIDC_URL, client_id=CLIENT_ID, client_secret=CLIENT_SECRET, verify_ssl=VERIFY_SSL)
    hydra = HydraModel(OIDC_ADMIN_URL, verify)
    grant = ClientCredentialsGrant(OIDC_URL, client_id, client_secret, verify=verify)
    
    @classmethod
    def _obtener_token(cls):
        token = cls.grant.get_token(cls.grant.access_token())
        if not token:
            raise Exception('error obteniendo token de acceso')
        return token

    @classmethod
    def _obtener_token_sesion(cls, sid):
        d = str(datetime.datetime.now())
        h = '{}-{}'.format(sid,d).encode('utf-8')
        return hashlib.sha1(h).hexdigest()


    """ 
        -----------------
        El flujo de login 
        -----------------
    """

    @classmethod
    def init_login_flow(cls, challenge):
        lc = cls.hydra.obtener_login_challenge(challenge)
        r = {
            'redirect_to': None
        }
        if lc['skip']:
            r = cls.aceptar_login_challenge(challenge,lc)
        return r

    @classmethod
    def login(cls, session, usuario, clave, challenge):
        ahora = datetime.datetime.now()
        q = session.query(UsuarioClave).filter(UsuarioClave.usuario == usuario, UsuarioClave.clave == clave, UsuarioClave.eliminada == None)
        q = q.filter(or_(UsuarioClave.expiracion == None, UsuarioClave.expiracion > ahora))
        c = q.one_or_none()
        if not c:
            r = cls.denegar_login_challenge(challenge, error='unknown_user', descripcion='Usuario o clave incorrectos')

            ''' logueo el intento '''
            ll = LoginLog()
            ll.id = str(uuid.uuid4())
            ll.created = datetime.datetime.utcnow()
            ll.usuario = usuario
            ll.clave = clave
            ll.status = False
            ll.challenge = challenge
            session.add(ll)

        else:
            """
            #TODO: hace falta chequear el debe cambiarla para loguearlo y redirigirlo a usuarios.
            """            
            r = cls.aceptar_login_challenge(challenge, uid=c.usuario_id)

            ''' logueo el intento '''
            ll = LoginLog()
            ll.id = str(uuid.uuid4())
            ll.created = datetime.datetime.utcnow()
            ll.usuario = usuario
            ll.status = True
            ll.challenge = challenge
            session.add(ll)

        return r

    @classmethod
    def aceptar_login_challenge(cls, challenge, lc=None, uid=None, recordar=True, timeout=3600):
        if not lc:
            lc = cls.hydra.obtener_login_challenge(challenge)
        data = {
            'subject': lc['subject'] if lc['skip'] else uid,
            'remember': False if lc['skip'] else recordar,
            'remember_for': timeout,
            'acr':''
        }
        return cls.hydra.aceptar_login_challenge(challenge, data)

    @classmethod
    def denegar_login_challenge(cls, challenge, error='', descripcion=''):
        data = {
            'error': error,
            'error_description': descripcion
        }
        return cls.hydra.denegar_login_challenge(challenge, data)


    """ 
        ---------------------
        El flujo de consent 
        ---------------------
    """

    @classmethod
    def init_consent_flow(cls, challenge):
        cc = cls.hydra.obtener_consent_challenge(challenge)
        r = {
            'redirect_to': None
        }
        if cc['skip']:
            r = cls.aceptar_consent_challenge(challenge,cc)
        else:
            """
                En nuestro caso siempre aceptamos los consent ya que son apps internas
                Para cumplir con la especificación habría que mostrar una pantalla con el consent de los scopes
            """
            r = cls.aceptar_consent_challenge(challenge,cc)
        return r

    """
        https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims
    """
    @classmethod
    def aceptar_consent_challenge(cls, challenge, cc=None, recordar=True, timeout=3600):
        if not cc:
            cc = cls.hydra.obtener_consent_challenge(challenge)

        """
            /////////////////
            TODO: cambiar este hack horrible a la api de usuarios!!!
            lo mejor a hacer es usar eventsourcing.
        """
        uid = cc['subject']
        tk = cls.api._get_token()

        def _get_user_uuid(api, uuid, token=None):
            query = '{}/usuarios/{}'.format(USERS_API_URL, uuid)
            r = api.get(query, token=token)
            if not r.ok:
                return None
            usr = r.json()
            if len(usr) > 0:
                return usr[0]
            return None

        def _get_primary_email(usr):
            mails = sorted([m for m in usr['mails'] if m['confirmado'] and not m['eliminado']], key=lambda m: m['email'])
            if len(mails) > 0:
                return mails[0]
            return None

        usr = _get_user_uuid(cls.api, uid, tk)
        if not usr:
            raise Exception('error obteniendo datos de usuario')

        """
            //////////////////////////
        """

        atk = {}
        if 'profile' in cc['requested_scope']:
            atk['name'] = usr['nombre']
            atk['family_name'] = usr['apellido']
            atk['given_name'] = usr['nombre']
            atk['middle_name'] = usr['nombre'].split(' ')[1] if len(usr['nombre'].split(' ')) > 1 else ''
            atk['nickname'] = ''
            atk['preferred_username'] = usr['dni']
            atk['profile'] = ''
            atk['picture'] = ''
            atk['website'] = ''
            atk['gender'] = usr['genero']
            atk['birthdate'] = usr['nacimiento']
            atk['zoneinfo'] = 'America/Argentina/Buenos_Aires'
            atk['locale'] = 'es-ES'

            updated_at = None
            try:
                updated_at = parse(usr['actualizado']).timestamp()
            except Exception as ex:
                try:
                    updated_at = parse(usr['creado']).timestamp()
                except Exception as exx:
                    pass
            atk['updated_at'] = updated_at

        if 'email' in cc['requested_scope']:
            em = _get_primary_email(usr)
            if em:
                atk['email'] = em['email']
                atk['email_verified'] = True if em['confirmado'] else False

        if 'address' in cc['requested_scope']:
            atk['address'] = {
                'street_address':usr['direccion'],
                'locality':usr['ciudad'],
                'country':usr['pais']
            }

        """
        if 'phone' in cc['requested_scope']:
            atk['phone_number'] = ''
            atk['phone_number_verified'] = ''
        """

        data = {
            'grant_scope': cc['requested_scope'],
            'remember': False if cc['skip'] else recordar,
            'remember_for': timeout,
            'session':{
                'access_token':atk,
                'id_token':atk
            }
        }            
        return cls.hydra.aceptar_consent_challenge(challenge, data)


    """
        métodos utilitarios para la administración 
    """

    @classmethod
    def logout_hydra(cls, client_id, uid):
        logging.debug('deslogueando a  {} en {}'.format(uid, client_id))
        c = cls.hydra.obtener_cliente(client_id)
        logging.debug(c)
        ''' https://www.ory.sh/docs/api/hydra/?version=latest '''
        url = c['client_uri']
        r = {
            'redirect_to': url
        }        
        cls.hydra.eliminar_sesion_login_usuario(uid)
        logging.debug(r)
        return r

    @classmethod
    def obtener_sesiones_usuario(cls, uid):
        return cls.hydra.obtener_consent_sesiones(uid)

    @classmethod
    def eliminar_sesiones_usuario(cls, uid):
        cls.hydra.eliminar_sesion_login_usuario(uid)
        cls.hydra.eliminar_sesiones_usuario(uid)
        
    @classmethod
    def eliminar_sesiones_usuario_cliente(cls, uid, cid):
        cls.hydra.eliminar_sesiones_cliente_usuario(cid, uid)
Пример #9
0
class Warden:
    def __init__(self,
                 oidc_url,
                 api_url,
                 client_id,
                 client_secret,
                 verify=True,
                 realm=''):
        self.verify = verify
        self.realm = realm
        self.warden_url = api_url
        self.client_id = client_id
        self.client_secret = client_secret
        self.client_credentials = ClientCredentialsGrant(
            oidc_url, self.client_id, self.client_secret, self.verify)

    def _get_auth_headers(self):
        r = self.client_credentials.access_token(scopes=['warden'])
        tk = self.client_credentials.get_token(r)
        headers = {
            'Authorization': 'Bearer {}'.format(tk),
            'Accept': 'application/json'
        }
        return headers

    def check_access(self, token, resource, action):
        headers = self._get_auth_headers()
        data = {'token': token, 'action': action, 'resource': resource}
        r = requests.post(self.warden_url + '/allowed',
                          verify=self.verify,
                          allow_redirects=False,
                          headers=headers,
                          data=data)
        if r.ok:
            js = r.json()
            if js['allowed'] == True:
                return js
        return None

    def _has_profiles(self, token, profiles, op):
        headers = self._get_auth_headers()
        data = {'token': token, 'profiles': profiles, 'op': op}
        r = requests.post(self.warden_url + '/profile',
                          verify=self.verify,
                          allow_redirects=False,
                          headers=headers,
                          json=data)
        if r.ok:
            js = r.json()
            return js
        return None

    def has_one_profile(self, token, profiles=[]):
        return self._has_profiles(token, profiles, op='OR')

    def has_all_profiles(self, token, profiles=[]):
        return self._has_profiles(token, profiles, op='AND')

    """
        esquema nuevo de permisos
        el token de consulta a warden se realiza usando el mismo token de usuario
    """

    def has_permissions(self, token, permisos=[]):
        assert token is not None
        headers = {
            'Authorization': 'Bearer {}'.format(token),
            'Accept': 'application/json'
        }
        request = {'permissions': permisos}
        r = requests.post(self.warden_url + '/has_permissions',
                          verify=self.verify,
                          allow_redirects=False,
                          headers=headers,
                          json=request)
        if r.ok:
            js = r.json()
            return js['result']
        return None

    def register_system_perms(self, token, system, permisos=[]):
        assert token is not None
        headers = {
            'Authorization': 'Bearer {}'.format(token),
            'Accept': 'application/json'
        }
        request = {'system': system, 'permissions': permisos}
        r = requests.post(self.warden_url + '/registrar_permisos',
                          verify=self.verify,
                          allow_redirects=False,
                          headers=headers,
                          json=request)
        if r.ok:
            return r.json()
        return None

    def _get_request_token(self):
        return self._bearer_token(flask.request.headers)

    """
        introspección del token
    """

    def _verify_valid_token(self, token):
        '''
            Recupera y chequea el token por validez
        '''
        if not token:
            return None
        headers = self._get_auth_headers()
        data = {'token': token}
        r = requests.post(self.warden_url + '/introspect',
                          verify=self.verify,
                          allow_redirects=False,
                          headers=headers,
                          json=data)
        if r.ok:
            js = r.json()
            if js['token']:
                return js['token']

        return None

    def _require_valid_token(self):
        original_token = self._get_request_token()
        tk = self._verify_valid_token(original_token)
        if not tk:
            return original_token, None
        return original_token, tk

    def require_valid_token(self, f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            original_token, tk = self._require_valid_token()
            if not tk:
                return self._invalid_request()
            kwargs['token'] = tk
            #kwargs['original_token'] = original_token
            return f(*args, **kwargs)

        return decorated_function

    def require_valid_token2(self, f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            original_token, tk = self._require_valid_token()
            if not tk:
                return self._invalid_request()
            kwargs['token'] = tk
            kwargs['original_token'] = original_token
            return f(*args, **kwargs)

        return decorated_function

    def _bearer_token(self, headers):
        if 'Authorization' in headers:
            auth = headers['Authorization'].split(' ')
            if auth[0].lower() == 'bearer':
                return auth[1]
        return None

    def _invalid_request(self):
        return self._require_auth(text='Bad Request',
                                  error='invalid_request',
                                  status=400)

    def _invalid_token(self):
        return self._require_auth(text='Unauthorized',
                                  error='invalid_token',
                                  status=401)

    def _insufficient_scope(self):
        return self._require_auth(text='Forbidden',
                                  error='insufficient_scope',
                                  status=403)

    def _require_auth(self,
                      text='Unauthorized',
                      error=None,
                      status=401,
                      error_description=''):
        headers = None
        if error:
            headers = {
                'WWW-Authenticate':
                'Basic realm=\"{}\", error=\"{}\", error_description:\"{}\"'.
                format(self.realm, error, error_description)
            }
        else:
            headers = {
                'WWW-Authenticate': 'Basic realm=\"{}\"'.format(self.realm)
            }
        return (text, status, headers)