Exemple #1
0
    def __call__(self, sid='', ttype='', **kwargs):
        """
        Return a token.

        :param ttype: Type of token
        :param prev: Previous token, if there is one to go from
        :param sid: Session id
        :return:
        """
        if not ttype and self.type:
            ttype = self.type
        else:
            ttype = 'A'

        if self.lifetime >= 0:
            exp = str(time_sans_frac() + self.lifetime)
        else:
            exp = '-1'  # Live for ever

        tmp = ''
        rnd = ''
        while rnd == tmp:  # Don't use the same random value again
            rnd = rndstr(32)  # Ultimate length multiple of 16

        return base64.b64encode(
            self.crypt.encrypt(lv_pack(rnd, ttype, sid,
                                       exp).encode())).decode("utf-8")
Exemple #2
0
    def authz_part2(self, user, authn_event, request, **kwargs):
        """
        After the authentication this is where you should end up

        :param user:
        :param request: The Authorization Request
        :param sid: Session key
        :param kwargs: possible other parameters
        :return: A redirect to the redirect_uri of the client
        """
        sid = setup_session(
            self.endpoint_context, request, user, authn_event=authn_event
        )

        try:
            resp_info = self.post_authentication(user, request, sid, **kwargs)
        except Exception as err:
            return self.error_response({}, "server_error", err)

        if "check_session_iframe" in self.endpoint_context.provider_info:
            ec = self.endpoint_context
            salt = rndstr()
            if ec.sdb.is_session_revoked(sid):
                pass
            else:
                authn_event = ec.sdb.get_authentication_event(
                    sid
                )  # use the last session
                _state = json.dumps({"authn_time": authn_event["authn_time"]})

                session_cookie = ec.cookie_dealer.create_cookie(
                    json.dumps(_state),
                    typ="session",
                    cookie_name=ec.cookie_name["session_management"],
                )

                opbs = session_cookie[ec.cookie_name["session_management"]]

                _session_state = compute_session_state(
                    opbs.value, salt, request["client_id"], resp_info["return_uri"]
                )

                if "cookie" in resp_info:
                    if isinstance(resp_info["cookie"], list):
                        resp_info["cookie"].append(session_cookie)
                    else:
                        append_cookie(resp_info["cookie"], session_cookie)
                else:
                    resp_info["cookie"] = session_cookie

                resp_info["response_args"]["session_state"] = _session_state

        # Mix-Up mitigation
        resp_info["response_args"]["iss"] = self.endpoint_context.issuer
        resp_info["response_args"]["client_id"] = request["client_id"]

        return resp_info
Exemple #3
0
    def add_registration_api(self, cinfo, client_id, context):
        _rat = rndstr(32)

        cinfo["registration_access_token"] = _rat
        cinfo["registration_client_uri"] = "{}?client_id={}".format(
            self.endpoint_context.endpoint["registration_api"].full_path, client_id
        )

        context.registration_access_token[_rat] = client_id
Exemple #4
0
def assertion_jwt(cli, keys, audience, algorithm, lifetime=600):
    _now = utc_time_sans_frac()

    at = AuthnToken(iss=cli.client_id,
                    sub=cli.client_id,
                    aud=audience,
                    jti=rndstr(32),
                    exp=_now + lifetime,
                    iat=_now)
    return at.to_jwt(key=keys, algorithm=algorithm)
Exemple #5
0
    def key(self, user="", areq=None):
        """
        Return a key (the session id)

        :param user: User id
        :param areq: The authorization request
        :return: An ID
        """
        csum = hashlib.new('sha224')
        csum.update(rndstr(32).encode('utf-8'))
        return csum.hexdigest()  # 56 bytes long, 224 bits
    def create_sdb(self):
        _sso_db = SSODb()
        passwd = rndstr(24)
        _th_args = {
            "code": {
                "lifetime": 600,
                "password": passwd
            },
            "token": {
                "lifetime": 3600,
                "password": passwd
            },
            "refresh": {
                "lifetime": 86400,
                "password": passwd
            },
        }

        _token_handler = token_handler.factory(None, **_th_args)
        userinfo = UserInfo(db_file=full_path("users.json"))
        self.sdb = SessionDB(InMemoryDataBase(), _token_handler, _sso_db,
                             userinfo)
Exemple #7
0
 def key(self, **kwargs):
     """
     Return a key (the session id)
     """
     return rndstr(32)
Exemple #8
0
    def client_registration_setup(self, request, new_id=True, set_secret=True):
        try:
            request.verify()
        except MessageException as err:
            if "type" not in request:
                return ResponseMessage(error="invalid_type",
                                       error_description="%s" % err)
            else:
                return ResponseMessage(error="invalid_configuration_parameter",
                                       error_description="%s" % err)

        request.rm_blanks()
        try:
            self.match_client_request(request)
        except CapabilitiesMisMatch as err:
            return ResponseMessage(
                error="invalid_request",
                error_description="Don't support proposed %s" % err)

        _context = self.endpoint_context
        if new_id:
            # create new id och secret
            client_id = rndstr(12)
            while client_id in _context.cdb:
                client_id = rndstr(12)
        else:
            try:
                client_id = request['client_id']
            except KeyError:
                raise ValueError('Missing client_id')

        _rat = rndstr(32)

        _cinfo = {
            "client_id":
            client_id,
            "registration_access_token":
            _rat,
            "registration_client_uri":
            "%s?client_id=%s" % (self.endpoint_path, client_id),
            "client_salt":
            rndstr(8)
        }

        if new_id:
            _cinfo["client_id_issued_at"] = utc_time_sans_frac()

        if set_secret:
            client_secret = secret(_context.seed, client_id)
            _cinfo.update({
                "client_secret":
                client_secret,
                "client_secret_expires_at":
                client_secret_expiration_time()
            })
        else:
            client_secret = ''

        _context.cdb[client_id] = _cinfo
        _context.cdb[_rat] = client_id

        _cinfo = self.do_client_registration(
            request,
            client_id,
            ignore=["redirect_uris", "policy_uri", "logo_uri", "tos_uri"])
        if isinstance(_cinfo, ResponseMessage):
            return _cinfo

        args = dict([(k, v) for k, v in _cinfo.items()
                     if k in RegistrationResponse.c_param])

        self.comb_uri(args)
        response = RegistrationResponse(**args)

        # Add the client_secret as a symmetric key to the key jar
        if client_secret:
            _context.keyjar.add_symmetric(client_id, str(client_secret))

        _context.cdb[client_id] = _cinfo

        try:
            _context.cdb.sync()
        except AttributeError:  # Not all databases can be sync'ed
            pass

        logger.info("registration_response: %s" % sanitize(response.to_dict()))

        return response
    def __init__(
        self,
        conf,
        keyjar=None,
        client_db=None,
        session_db=None,
        cwd="",
        cookie_dealer=None,
        httpc=None,
        cookie_name=None,
        jwks_uri_path=None,
    ):
        self.conf = conf
        self.keyjar = keyjar or KeyJar()
        self.cwd = cwd

        # client database
        self.cdb = client_db or {}

        try:
            self.seed = bytes(conf["seed"], "utf-8")
        except KeyError:
            self.seed = bytes(rndstr(16), "utf-8")

        # Default values, to be changed below depending on configuration
        self.endpoint = {}
        self.issuer = ""
        self.httpc = httpc or requests
        self.verify_ssl = True
        self.jwks_uri = None
        self.sso_ttl = 14400  # 4h
        self.symkey = rndstr(24)
        self.id_token_schema = IdToken
        self.endpoint_to_authn_method = {}
        self.cookie_dealer = cookie_dealer
        self.login_hint_lookup = None

        if cookie_name:
            self.cookie_name = cookie_name
        elif "cookie_name" in conf:
            self.cookie_name = conf["cookie_name"]
        else:
            self.cookie_name = {
                "session": "oidcop",
                "register": "oidc_op_rp",
                "session_management": "sman",
            }

        for param in [
                "verify_ssl",
                "issuer",
                "sso_ttl",
                "symkey",
                "client_authn",
                "id_token_schema",
        ]:
            try:
                setattr(self, param, conf[param])
            except KeyError:
                pass

        try:
            self.template_handler = conf["template_handler"]
        except KeyError:
            try:
                loader = conf["template_loader"]
            except KeyError:
                template_dir = conf["template_dir"]
                loader = Environment(loader=FileSystemLoader(template_dir),
                                     autoescape=True)
            self.template_handler = Jinja2TemplateHandler(loader)

        self.setup = {}
        if not jwks_uri_path:
            try:
                jwks_uri_path = conf["jwks"]["uri_path"]
            except KeyError:
                pass

        try:
            if self.issuer.endswith("/"):
                self.jwks_uri = "{}{}".format(self.issuer, jwks_uri_path)
            else:
                self.jwks_uri = "{}/{}".format(self.issuer, jwks_uri_path)
        except KeyError:
            self.jwks_uri = ""

        if self.keyjar is None or self.keyjar.owners() == []:
            args = {k: v for k, v in conf["jwks"].items() if k != "uri_path"}
            self.keyjar = init_key_jar(**args)

        try:
            _conf = conf["cookie_dealer"]
        except KeyError:
            pass
        else:
            if self.cookie_dealer:  # already defined
                raise ValueError("Cookie Dealer already defined")
            self.cookie_dealer = init_service(_conf)

        try:
            _conf = conf["sub_func"]
        except KeyError:
            sub_func = None
        else:
            sub_func = {}
            for key, args in _conf.items():
                if "class" in args:
                    sub_func[key] = init_service(args)
                elif "function" in args:
                    if isinstance(args["function"], str):
                        sub_func[key] = util.importer(args["function"])
                    else:
                        sub_func[key] = args["function"]

        if session_db:
            self.sdb = session_db
        else:
            try:
                _th_args = conf["token_handler_args"]
            except KeyError:
                # create 3 keys
                keydef = [
                    {
                        "type": "oct",
                        "bytes": "24",
                        "use": ["enc"],
                        "kid": "code"
                    },
                    {
                        "type": "oct",
                        "bytes": "24",
                        "use": ["enc"],
                        "kid": "token"
                    },
                    {
                        "type": "oct",
                        "bytes": "24",
                        "use": ["enc"],
                        "kid": "refresh"
                    },
                ]

                jwks_def = {
                    "private_path": "private/token_jwks.json",
                    "key_defs": keydef,
                    "read_only": False,
                }

                _th_args = {"jwks_def": jwks_def}
                for typ, tid in [("code", 600), ("token", 3600),
                                 ("refresh", 86400)]:
                    _th_args[typ] = {"lifetime": tid}

            self.sdb = create_session_db(self,
                                         _th_args,
                                         db=None,
                                         sso_db=SSODb(),
                                         sub_func=sub_func)

        self.endpoint = build_endpoints(
            conf["endpoint"],
            endpoint_context=self,
            client_authn_method=CLIENT_AUTHN_METHOD,
            issuer=conf["issuer"],
        )
        try:
            _cap = conf["capabilities"]
        except KeyError:
            _cap = {}

        for endpoint, endpoint_instance in self.endpoint.items():
            if endpoint_instance.provider_info:
                _cap.update(endpoint_instance.provider_info)

            if endpoint in ["webfinger", "provider_info"]:
                continue

            _cap[endpoint_instance.endpoint_name] = "{}".format(
                endpoint_instance.endpoint_path)

        try:
            authz_spec = conf["authz"]
        except KeyError:
            self.authz = authz.Implicit(self)
        else:
            self.authz = init_service(authz_spec, self)

        try:
            _authn = conf["authentication"]
        except KeyError:
            self.authn_broker = None
        else:
            self.authn_broker = populate_authn_broker(_authn, self,
                                                      self.template_handler)

        try:
            _conf = conf["id_token"]
        except KeyError:
            self.idtoken = IDToken(self)
        else:
            self.idtoken = init_service(_conf, self)

        try:
            _conf = conf["userinfo"]
        except KeyError:
            pass
        else:
            self.userinfo = init_user_info(_conf, self.cwd)
            self.sdb.userinfo = self.userinfo

        try:
            _conf = conf["login_hint_lookup"]
        except KeyError:
            pass
        else:
            self.login_hint_lookup = init_service(_conf)
            if self.userinfo:
                self.login_hint_lookup.user_info = self.userinfo

        try:
            _conf = conf["login_hint2acrs"]
        except KeyError:
            self.login_hint2acrs = None
        else:
            self.login_hint2acrs = init_service(_conf)

        self.provider_info = self.create_providerinfo(_cap)

        # which signing/encryption algorithms to use in what context
        self.jwx_def = {}

        # special type of logging
        self.events = None

        # client registration access tokens
        self.registration_access_token = {}
Exemple #10
0
    def client_registration_setup(self, request, new_id=True, set_secret=True):
        try:
            request.verify()
        except (MessageException, ValueError) as err:
            return ResponseMessage(
                error="invalid_configuration_request", error_description="%s" % err
            )

        request.rm_blanks()
        try:
            self.match_client_request(request)
        except CapabilitiesMisMatch as err:
            return ResponseMessage(
                error="invalid_request",
                error_description="Don't support proposed %s" % err,
            )

        _context = self.endpoint_context
        if new_id:
            # create new id och secret
            client_id = rndstr(12)
            while client_id in _context.cdb:
                client_id = rndstr(12)
        else:
            try:
                client_id = request["client_id"]
            except KeyError:
                raise ValueError("Missing client_id")

        _cinfo = {"client_id": client_id, "client_salt": rndstr(8)}

        if "registration_api" in self.endpoint_context.endpoint:
            self.add_registration_api(_cinfo, client_id, _context)

        if new_id:
            _cinfo["client_id_issued_at"] = utc_time_sans_frac()

        if set_secret:
            client_secret = self.add_client_secret(_cinfo, client_id, _context)
        else:
            client_secret = ""

        _context.cdb[client_id] = _cinfo

        _cinfo = self.do_client_registration(
            request,
            client_id,
            ignore=["redirect_uris", "policy_uri", "logo_uri", "tos_uri"],
        )
        if isinstance(_cinfo, ResponseMessage):
            return _cinfo

        args = dict(
            [(k, v) for k, v in _cinfo.items() if k in RegistrationResponse.c_param]
        )

        comb_uri(args)
        response = RegistrationResponse(**args)

        # Add the client_secret as a symmetric key to the key jar
        if client_secret:
            _context.keyjar.add_symmetric(client_id, str(client_secret))

        _context.cdb[client_id] = _cinfo

        try:
            _context.cdb.sync()
        except AttributeError:  # Not all databases can be sync'ed
            pass

        logger.info("registration_response: %s" % sanitize(response.to_dict()))

        return response
Exemple #11
0
    def __init__(self,
                 conf,
                 keyjar=None,
                 client_db=None,
                 session_db=None,
                 cwd='',
                 cookie_dealer=None):
        self.conf = conf
        self.keyjar = keyjar or KeyJar()
        self.cwd = cwd

        if session_db:
            self.sdb = session_db
        else:
            self.sdb = create_session_db(
                conf['password'],
                db=None,
                token_expires_in=conf['token_expires_in'],
                grant_expires_in=conf['grant_expires_in'],
                refresh_token_expires_in=conf['refresh_token_expires_in'],
                sso_db=SSODb())

        # client database
        self.cdb = client_db or {}

        try:
            self.seed = bytes(conf['seed'], 'utf-8')
        except KeyError:
            self.seed = bytes(rndstr(16), 'utf-8')

        # Default values, to be changed below depending on configuration
        self.endpoint = {}
        self.issuer = ''
        self.verify_ssl = True
        self.jwks_uri = None
        self.sso_ttl = 14400  # 4h
        self.symkey = rndstr(24)
        self.id_token_schema = IdToken
        self.endpoint_to_authn_method = {}
        self.cookie_dealer = cookie_dealer

        for param in [
                'verify_ssl', 'issuer', 'sso_ttl', 'symkey', 'client_authn',
                'id_token_schema'
        ]:
            try:
                setattr(self, param, conf[param])
            except KeyError:
                pass

        template_dir = conf["template_dir"]
        jinja_env = Environment(loader=FileSystemLoader(template_dir))

        self.setup = {}
        try:
            self.jwks_uri = '{}/{}'.format(self.issuer,
                                           conf['jwks']['public_path'])
        except KeyError:
            self.jwks_uri = ''

        self.endpoint = build_endpoints(
            conf['endpoint'],
            endpoint_context=self,
            client_authn_method=CLIENT_AUTHN_METHOD,
            issuer=conf['issuer'])
        try:
            _cap = conf['capabilities']
        except KeyError:
            _cap = {}

        for endpoint in ['authorization', 'token', 'userinfo', 'registration']:
            try:
                endpoint_spec = self.endpoint[endpoint]
            except KeyError:
                pass
            else:
                _cap[endpoint_spec.endpoint_name] = '{}'.format(
                    self.endpoint[endpoint].endpoint_path)

        try:
            authz_spec = conf['authz']
        except KeyError:
            self.authz = authz.Implicit(self)
        else:
            if 'args' in authz_spec:
                self.authz = authz.factory(authz_spec['name'],
                                           **authz_spec['args'])
            else:
                self.authz = authz.factory(self, authz_spec['name'])

        try:
            _authn = conf['authentication']
        except KeyError:
            self.authn_broker = None
        else:
            self.authn_broker = AuthnBroker()

            for authn_spec in _authn:
                try:
                    _args = authn_spec['kwargs']
                except KeyError:
                    _args = {}

                if 'template' in _args:
                    _args['template_env'] = jinja_env

                _args['endpoint_context'] = self
                authn_method = user.factory(authn_spec['name'], **_args)
                args = {
                    k: authn_spec[k]
                    for k in ['acr', 'level', 'authn_authority']
                    if k in authn_spec
                }

                self.authn_broker.add(method=authn_method, **args)
                self.endpoint_to_authn_method[
                    authn_method.url_endpoint] = authn_method

        try:
            _conf = conf['userinfo']
        except KeyError:
            pass
        else:
            try:
                kwargs = _conf['kwargs']
            except KeyError:
                kwargs = {}

            if 'db_file' in kwargs:
                kwargs['db_file'] = os.path.join(self.cwd, kwargs['db_file'])
            self.userinfo = _conf['class'](**kwargs)

        self.provider_info = self.create_providerinfo(_cap)

        # which signing/encryption algorithms to use in what context
        self.jwx_def = {}

        # special type of logging
        self.events = None
    def __init__(
        self,
        conf,
        keyjar=None,
        client_db=None,
        session_db=None,
        sso_db=None,
        cwd="",
        cookie_dealer=None,
        httpc=None,
        cookie_name=None,
        jwks_uri_path=None,
        jti_db=None,
    ):
        self.conf = conf
        self.keyjar = keyjar or KeyJar()
        self.cwd = cwd

        if self.keyjar is None or self.keyjar.owners() == []:
            args = {k: v for k, v in conf["jwks"].items() if k != "uri_path"}
            self.keyjar = init_key_jar(**args)

        try:
            self.seed = bytes(conf["seed"], "utf-8")
        except KeyError:
            self.seed = bytes(rndstr(16), "utf-8")

        # Default values, to be changed below depending on configuration
        self.endpoint = {}
        self.issuer = ""
        self.httpc = httpc or requests
        self.jwks_uri = None
        self.sso_ttl = 14400  # 4h
        self.symkey = rndstr(24)
        self.id_token_schema = IdToken
        self.idtoken = None
        self.authn_broker = None
        self.authz = None
        self.endpoint_to_authn_method = {}
        self.cookie_dealer = cookie_dealer
        self.login_hint_lookup = None
        self.login_hint2acrs = None
        self.userinfo = None
        self.scope2claims = SCOPE2CLAIMS
        # arguments for endpoints add-ons
        self.args = {}
        self.par_db = {}
        self.dev_auth_db = {}

        for param in [
                "issuer",
                "sso_ttl",
                "symkey",
                "client_authn",
                "id_token_schema",
        ]:
            try:
                setattr(self, param, conf[param])
            except KeyError:
                pass

        self.th_args = get_token_handlers(conf)

        # client database
        self.set_client_db()

        # session db
        self._sub_func = {}
        self.do_sub_func()

        # set self.sdb
        if session_db:
            self.set_session_db(sso_db, db=session_db)
        else:
            self.set_session_db(sso_db)

        if jti_db:
            self.set_jti_db(db=jti_db)
        else:
            self.set_jti_db()

        if cookie_name:
            self.cookie_name = cookie_name
        elif "cookie_name" in conf:
            self.cookie_name = conf["cookie_name"]
        else:
            self.cookie_name = {
                "session": "oidcop",
                "register": "oidc_op_rp",
                "session_management": "sman",
            }

        try:
            self.template_handler = conf["template_handler"]
        except KeyError:
            try:
                loader = conf["template_loader"]
            except KeyError:
                template_dir = conf["template_dir"]
                loader = Environment(loader=FileSystemLoader(template_dir),
                                     autoescape=True)
            self.template_handler = Jinja2TemplateHandler(loader)

        self.setup = {}
        if not jwks_uri_path:
            try:
                jwks_uri_path = conf["jwks"]["uri_path"]
            except KeyError:
                pass

        try:
            if self.issuer.endswith("/"):
                self.jwks_uri = "{}{}".format(self.issuer, jwks_uri_path)
            else:
                self.jwks_uri = "{}/{}".format(self.issuer, jwks_uri_path)
        except KeyError:
            self.jwks_uri = ""

        for item in [
                "cookie_dealer",
                "authz",
                "authentication",
                "id_token",
                "scope2claims",
        ]:
            _func = getattr(self, "do_{}".format(item), None)
            if _func:
                _func()

        _cap = self.do_endpoints()

        for item in [
                "userinfo", "login_hint_lookup", "login_hint2acrs", "add_on"
        ]:
            _func = getattr(self, "do_{}".format(item), None)
            if _func:
                _func()

        self.provider_info = self.create_providerinfo(_cap)

        # which signing/encryption algorithms to use in what context
        self.jwx_def = {}

        # special type of logging
        self.events = None

        # client registration access tokens
        self.registration_access_token = {}

        # The HTTP clients request arguments
        _cnf = conf.get("http_params")
        if _cnf:
            self.httpc_params = get_http_params(_cnf)
        else:  # Backward compatibility
            self.httpc_params = {"verify": conf.get("verify_ssl")}
Exemple #13
0
    def client_registration_setup(self, request, new_id=True, set_secret=True):
        try:
            request.verify()
        except (MessageException, ValueError) as err:
            return ResponseMessage(error="invalid_configuration_request",
                                   error_description="%s" % err)

        request.rm_blanks()
        try:
            self.match_client_request(request)
        except CapabilitiesMisMatch as err:
            return ResponseMessage(
                error="invalid_request",
                error_description="Don't support proposed %s" % err,
            )

        _context = self.endpoint_context
        if new_id:
            # create new id och secret
            client_id = rndstr(12)
            # cdb client_id MUST be unique!
            while client_id in _context.cdb:
                client_id = rndstr(12)
            if "client_id" in request:
                del request["client_id"]
        else:
            client_id = request.get("client_id")
            if not client_id:
                raise ValueError("Missing client_id")

        _cinfo = {"client_id": client_id, "client_salt": rndstr(8)}

        if "registration_read" in self.endpoint_context.endpoint:
            self.add_registration_api(_cinfo, client_id, _context)

        if new_id:
            _cinfo["client_id_issued_at"] = utc_time_sans_frac()

        client_secret = ""
        if set_secret:
            client_secret = self.add_client_secret(_cinfo, client_id, _context)

        logger.debug(
            "Stored client info in CDB under cid={}".format(client_id))

        _context.cdb[client_id] = _cinfo
        _cinfo = self.do_client_registration(
            request,
            client_id,
            ignore=["redirect_uris", "policy_uri", "logo_uri", "tos_uri"],
        )
        if isinstance(_cinfo, ResponseMessage):
            return _cinfo

        args = dict([(k, v) for k, v in _cinfo.items()
                     if k in RegistrationResponse.c_param])

        comb_uri(args)
        response = RegistrationResponse(**args)

        # Add the client_secret as a symmetric key to the key jar
        if client_secret:
            _context.keyjar.add_symmetric(client_id, str(client_secret))

        logger.debug(
            "Stored updated client info in CDB under cid={}".format(client_id))
        logger.debug("ClientInfo: {}".format(_cinfo))
        _context.cdb[client_id] = _cinfo

        # Not all databases can be sync'ed
        if hasattr(_context.cdb, "sync") and callable(_context.cdb.sync):
            _context.cdb.sync()

        msg = "registration_response: {}"
        logger.info(msg.format(sanitize(response.to_dict())))

        return response