def create_user_token(app_token: Token, owner: User) -> Token: """ Derives a new user token from an existing app token, and set the owner to be the user given in argument. """ if app_token.token_type == Token.TokenType.dyn: new_token = Token( # nosec created_on=utils.now(), expires_on=utils.now_plus(days=1), owner=owner, parent=app_token, token_type="use", uuid=str(uuid4()), ) elif app_token.token_type == Token.TokenType.per: new_token = Token( # nosec created_on=utils.now(), owner=owner, parent=app_token, token_type="use", uuid=str(uuid4()), ) else: raise ValueError("Expected an app token") new_token.save() logging.info("Created user token %s for app %s", new_token.uuid, app_token.uuid) return new_token
def get_access_token(character_id: int, scope: EsiScope = EsiScope.PUBLICDATA) -> EsiAccessToken: """ Returns an access token, refreshes if needed Todo: Support multiple scopes. """ scope_str: str = scope.value owner: User = User.objects.get(character_id=character_id) esi_access_token: EsiAccessToken = EsiAccessToken.objects( owner=owner, scopes=scope_str, expires_on__gt=utils.now(), ).first() if not esi_access_token: esi_refresh_token: EsiRefreshToken = EsiRefreshToken.objects( owner=owner, scopes=scope_str, valid=True).first() if not esi_refresh_token: logging.error( "Could not find refresh token for user %s with scope %s", owner.character_name, scope_str, ) raise LookupError( (f"Could not find access token with scope {scope_str} " f"for user {character_id}")) esi_access_token = save_esi_tokens( refresh_access_token(esi_refresh_token.refresh_token)) return esi_access_token
def create_state_code( app_token: Optional[Token], *, inviting_corporation: Optional[Corporation] = None, code_prefix: Optional[str] = None, ) -> StateCode: """ Creates a new state code. See also: :class:`sni.uac.token.StateCode` """ code = str(uuid4()) if code_prefix is not None: code = code_prefix + ":" + code state_code = StateCode( app_token=app_token, created_on=utils.now(), inviting_corporation=inviting_corporation, uuid=code, ) state_code.save() logging.info( "Created state code %s deriving from app token %s", state_code.uuid, app_token.uuid if app_token is not None else "N/A", ) return state_code
def new_authentication_challenge(usr: User) -> str: """ Initiates an authentication challenge. The challenge proceeds as follows: 1. A user (:class:`sni.user`) asks to start a challenge by calling this method. 2. This methods returns a UUID, and the user has 60 seconds to change its teamspeak nickname to that UUID. 3. The user notifies SNI that (s)he has done so. 4. The server checks (see :meth:`sni.teamspeak.complete_authentication_challenge`), and if sucessful, the corresponding teamspeak client is registered in the database and bound to that user. The nickname is also automatically assigned. """ logging.info("Starting authentication challenge for %s", usr.character_name) challenge_nickname = utils.random_code(20) TeamspeakAuthenticationChallenge.objects(user=usr).update( set__challenge_nickname=challenge_nickname, set__created_on=utils.now(), set__user=usr, upsert=True, ) return challenge_nickname
def update_user_from_esi(usr: User): """ Updates a user's information from the ESI """ data = esi_get(f"latest/characters/{usr.character_id}").data old_corporation = usr.corporation usr.corporation = ensure_corporation(int(data["corporation_id"])) usr.updated_on = utils.now() if usr.corporation != old_corporation: logging.debug("Corporation of user %s changed", usr.character_name) reset_clearance(usr) usr.save()
def esi_request( http_method: str, path: str, *, kwargs: dict = {}, token: Optional[str] = None, ) -> EsiResponse: """ Makes an HTTP request to the ESI, and returns the response object. """ kwargs["headers"] = { "Accept-Encoding": "gzip", "accept": "application/json", "User-Agent": "SeAT Navy Issue @ " + conf.general.root_url, **kwargs.get("headers", {}), } if token: kwargs["headers"]["Authorization"] = "Bearer " + token if http_method.upper() != "GET": raw = request(http_method, ESI_BASE + path, **kwargs) raw.raise_for_status() return EsiResponse.from_response(raw) key = ("esi", [path, token, sorted(kwargs.get("params", {}).items())]) response = cache_get(key) if response is not None: return EsiResponse(**response) raw = request(http_method, ESI_BASE + path, **kwargs) raw.raise_for_status() response = EsiResponse.from_response(raw) ttl = 60 if "Expires" in response.headers: try: ttl = int((parser.parse(response.headers["Expires"]) - utils.now()).total_seconds()) except ValueError as error: logging.warning("Could not determine ESI TTL: %s", str(error)) if ttl > 0: cache_set(key, response.dict(), ttl) return response
def create_permanent_app_token( owner: User, *, callback: Optional[str] = None, comments: Optional[str] = None, parent: Optional[Token] = None, ) -> Token: """ Creates a new permanent app token for that user. Warning: Does not check that the user is allowed to actually do that. """ new_token = Token( callback=callback, comments=comments, created_on=utils.now(), owner=owner, parent=parent, token_type=Token.TokenType.per, uuid=str(uuid4()), ) new_token.save() logging.info("Created permanent app token %s", new_token.uuid) return new_token
def on_pre_save(_sender: Any, document: me.Document): """ If the document has a `updated_on`, sets it to the current datetime. """ if "updated_on" in document: document.updated_on = utils.now()