Example #1
0
def reset_clearance(usr: User, save: bool = False):
    """
    Resets a user's clearance.

    If a user is the CEO of its corporation, then a clearance level of 2 is
    granted. If its corporation is the executor of the alliance, then a level
    of 4 is granted instead. If the user is root or has a clearance level of
    10, then a level of 10 is applied (so that superusers are preserved no
    matter what). Otherwise, the user's clearance level is set to 0.
    """
    if usr.clearance_level >= 9:
        return
    if usr.character_id == 0:
        usr.clearance_level = 10
    elif usr.is_ceo_of_alliance():
        usr.clearance_level = 4
    elif usr.is_ceo_of_corporation():
        usr.clearance_level = 2
    elif usr.clearance_level >= 0:
        usr.clearance_level = 0
    if save:
        logging.debug(
            "Reset clearance level of %s to %d",
            usr.character_name,
            usr.clearance_level,
        )
        usr.save()
Example #2
0
def complete_authentication_challenge(connection: TS3Connection, usr: User):
    """
    Complete an authentication challenge, see
    :meth:`sni.teamspeak.new_authentication_challenge`.
    """
    challenge: TeamspeakAuthenticationChallenge = TeamspeakAuthenticationChallenge.objects.get(
        user=usr)
    client = find_client(connection, nickname=challenge.challenge_nickname)
    usr.teamspeak_cldbid = client.client_database_id
    usr.save()
    auth_group = ensure_autogroup(conf.teamspeak.auth_group_name)
    auth_group.modify(add_to_set__members=usr)
    challenge.delete()
    logging.info("Completed authentication challenge for %s",
                 usr.character_name)
Example #3
0
def index_users_skillpoints():
    """
    Measures all users skillpoints
    """
    for usr in User.objects():
        if has_esi_scope(usr, EsiScope.ESI_SKILLS_READ_SKILLS_V1):
            scheduler.add_job(index_user_skillpoints, args=(usr, ))
Example #4
0
def token_has_enough_scopes(access_token: DecodedAccessToken,
                            usr: User) -> bool:
    """
    Tells wether the access token has all the cropes that are required for a
    given user.
    """
    return usr.cumulated_mandatory_esi_scopes() <= set(access_token.scp)
Example #5
0
def index_users_wallets():
    """
    Indexes user wallet balance
    """
    for usr in User.objects():
        if has_esi_scope(usr, EsiScope.ESI_WALLET_READ_CHARACTER_WALLET_V1):
            scheduler.add_job(index_user_wallets, args=(usr, ))
Example #6
0
def index_users_mails():
    """
    Index all user emails.
    """
    for usr in User.objects():
        if has_esi_scope(usr, EsiScope.ESI_MAIL_READ_MAIL_V1):
            scheduler.add_job(index_user_mails, args=(usr, ))
Example #7
0
def index_users_location():
    """
    Indexes all user's location
    """
    for usr in User.objects():
        if (has_esi_scope(usr, EsiScope.ESI_LOCATION_READ_LOCATION_V1)
                and has_esi_scope(usr, EsiScope.ESI_LOCATION_READ_ONLINE_V1)
                and has_esi_scope(usr,
                                  EsiScope.ESI_LOCATION_READ_SHIP_TYPE_V1)):
            scheduler.add_job(index_user_location, args=(usr, ))
Example #8
0
async def update_discord_users():
    """
    Updates all discord users.
    """
    for usr in User.objects(discord_user_id__ne=None):
        await utils.catch_all_async(
            update_discord_user,
            f"Could not update Discord properties of user {usr.character_name}",
            args=[usr],
        )
Example #9
0
def get_user(tkn: Token = Depends(from_authotization_header_nondyn)):
    """
    Returns the list of all user names. Requires a clearance level of 0 or
    more.
    """
    assert_has_clearance(tkn.owner, "sni.read_user")
    return [
        GetUserShortOut.from_record(usr) for usr in User.objects(
            clearance_level__gte=0).order_by("character_name")
    ]
Example #10
0
def tracking_status(usr: User) -> TrackingStatus:
    """
    Reports the tracking status of this user, see
    :class:`sni.esi.token.TrackingStatus`
    """
    query_set = EsiRefreshToken.objects(owner=usr)
    if query_set.count() == 0:
        return TrackingStatus.HAS_NO_REFRESH_TOKEN
    cumulated_mandatory_esi_scopes = usr.cumulated_mandatory_esi_scopes()
    for refresh_token in query_set:
        if cumulated_mandatory_esi_scopes <= set(refresh_token.scopes):
            return TrackingStatus.HAS_A_VALID_REFRESH_TOKEN
    return TrackingStatus.ONLY_HAS_INVALID_REFRESH_TOKEN
Example #11
0
def is_authorized_to_login(usr: User) -> bool:
    """
    Tells wether a user is authorized to login or not. A user is authorized to
    login if the following conditions are satisfied:

    * its ``authorized_to_login`` field is ``True`` or if the user belongs to
      **at least one** group, corporation, alliance, or coalition whose
      ``authorized_to_login`` field is ``True``;

    * the user's ``authorized_to_login`` field is not ``False``, and none of
      the groups, corporation, alliance, or coalitions the user belongs to has
      a ``authorized_to_login`` field set ot ``False``.

    In addition, the root user is always allowed to login, regardless of the
    status of the group he's part of.
    """
    if usr.character_id == 0:
        return True

    if usr.authorized_to_login is False:
        return False

    authorized_to_login: Optional[bool] = usr.authorized_to_login

    if usr.corporation is not None:
        if usr.corporation.authorized_to_login is False:
            return False
        # Note that in Python, (None or True) == True
        authorized_to_login = (authorized_to_login
                               or usr.corporation.authorized_to_login)

    if usr.alliance is not None:
        if usr.alliance.authorized_to_login is False:
            return False
        authorized_to_login = (authorized_to_login
                               or usr.alliance.authorized_to_login)

    for coa in usr.coalitions():
        if coa.authorized_to_login is False:
            return False
        authorized_to_login = authorized_to_login or coa.authorized_to_login

    for grp in Group.objects(members=usr):
        if grp.authorized_to_login is False:
            return False
        authorized_to_login = authorized_to_login or grp.authorized_to_login

    # If authorized_to_login is still None, then the result is False
    return bool(authorized_to_login)
Example #12
0
 def from_record(usr: User) -> "GetUserOut":
     """
     Populates a new :class:`sni.routers.user.GetUserOut` with the information
     contained in a user database record.
     """
     return GetUserOut(
         alliance=(usr.alliance.alliance_id
                   if usr.alliance is not None else None),
         authorized_to_login=usr.authorized_to_login,
         available_esi_scopes=list(available_esi_scopes(usr)),
         character_id=usr.character_id,
         character_name=usr.character_name,
         clearance_level=usr.clearance_level,
         coalitions=[str(coalition.pk) for coalition in usr.coalitions()],
         corporation=(usr.corporation.corporation_id
                      if usr.corporation is not None else None),
         created_on=usr.created_on,
         cumulated_mandatory_esi_scopes=list(
             usr.cumulated_mandatory_esi_scopes()),
         is_ceo_of_alliance=usr.is_ceo_of_alliance(),
         is_ceo_of_corporation=usr.is_ceo_of_corporation(),
         tickered_name=usr.tickered_name,
         updated_on=usr.updated_on,
     )
Example #13
0
def post_history_character_location_now(
    character_id: int, tkn: Token = Depends(from_authotization_header_nondyn),
):
    """
    Get and save the current location of a character. Requires having clearance
    to access the ESI scopes ``esi-location.read_location.v1``,
    ``esi-location.read_online.v1``, and ``esi-location.read_ship_type.v1``, of
    the character.
    """
    usr: User = User.objects(character_id=character_id).get()
    assert_has_clearance(tkn.owner, "esi-location.read_location.v1", usr)
    assert_has_clearance(tkn.owner, "esi-location.read_online.v1", usr)
    assert_has_clearance(tkn.owner, "esi-location.read_ship_type.v1", usr)
    location = get_user_location(usr)
    location.save()
    return GetCharacterLocationOut.from_record(location)
Example #14
0
def get_history_character_wallet(
    character_id: int,
    response: Response,
    page: pdt.PositiveInt = Header(1),
    tkn: Token = Depends(from_authotization_header_nondyn),
):
    """
    Get the wallet balance history of a character. Requires having clearance to
    access the ESI scope ``esi-wallet.read_character_wallet.v1`` of the
    character. The results are sorted by most to least recent, and paginated by
    pages of 50 items. The page count in returned in the ``X-Pages`` header.
    """
    usr: User = User.objects(character_id=character_id).get()
    assert_has_clearance(tkn.owner, "esi-wallet.read_character_wallet.v1", usr)
    query_set = EsiWalletBalance.objects(user=usr).order_by("-timestamp")
    return [
        GetCharacterWalletBalanceOut.from_record(document)
        for document in paginate(query_set, 50, page, response)
    ]
Example #15
0
def delete_corporation_guest(
    corporation_id: int,
    character_id: int,
    tkn: Token = Depends(from_authotization_header_nondyn),
):
    """
    Deletes a corporation guest
    """
    corporation: Corporation = Corporation.objects(
        corporation_id=corporation_id
    ).get()
    assert_has_clearance(
        tkn.owner, "sni.delete_corporation_guest", corporation.ceo
    )
    guest: User = User.objects(
        character_id=character_id,
        clearance_level__lt=0,
        corporation=corporation,
    ).get()
    guest.delete()
Example #16
0
def get_history_character_mails(
    character_id: int,
    response: Response,
    page: pdt.PositiveInt = Header(1),
    tkn: Token = Depends(from_authotization_header_nondyn),
):
    """
    Get the email history of a character. Requires having clearance to access
    the ESI scope ``esi-mail.read_mail.v1`` of the character. The results are
    sorted by most to least recent, and paginated by pages of 50 items. The
    page count in returned in the ``X-Pages`` header.

    Todo:
        Only show email sent by the specified user...
    """
    usr: User = User.objects(character_id=character_id).get()
    assert_has_clearance(tkn.owner, "esi-mail.read_mail.v1", usr)
    query_set = EsiMail.objects(from_id=character_id).order_by("-timestamp")
    return [
        GetCharacterMailShortOut.from_record(mail)
        for mail in paginate(query_set, 50, page, response)
    ]
Example #17
0
def message_registered_clients_with_wrong_name():
    """
    Iterates through all users that are registered in Teamspeak, and checks
    their nickname. If it doesn't match, sends a message.
    """
    connection = new_teamspeak_connection()
    for ts_client in client_list(connection):
        if ts_client.client_nickname.startswith(conf.teamspeak.bot_name):
            # TS client is probably the bot
            continue
        usr: User = User.objects(
            teamspeak_cldbid=ts_client.client_database_id).first()
        if usr is None:
            # TS client is unknown
            continue
        tickered_name = usr.tickered_name
        if ts_client.client_nickname != tickered_name:
            logging.debug(
                "Wrong nickname found %s; should be %s",
                ts_client.client_nickname,
                tickered_name,
            )
            message = (
                f"Hello {ts_client.client_nickname}. " +
                "Please change your Teamspeak nickname to " +
                f'"{tickered_name}" (without the quotes). Thank you :)' +
                " -- SeAT Navy Issue Teamspeak Bot; This is an automated" +
                " message, please do not respond.")
            utils.catch_all(
                connection.sendtextmessage,
                f"Failed to notify teamspeak user {ts_client.client_nickname}",
                kwargs={
                    "targetmode": 1,
                    "target": ts_client.clid,
                    "msg": message,
                },
            )
    close_teamspeak_connection(connection)
Example #18
0
def get_history_character_location(
    character_id: int,
    response: Response,
    page: pdt.PositiveInt = Header(1),
    tkn: Token = Depends(from_authotization_header_nondyn),
):
    """
    Get the location history of a character. Requires having clearance to
    access the ESI scopes ``esi-location.read_location.v1``,
    ``esi-location.read_online.v1``, and ``esi-location.read_ship_type.v1``, of
    the character. The results are sorted by most to least recent, and
    paginated by pages of 50 items. The page count in returned in the
    ``X-Pages`` header.
    """
    usr: User = User.objects(character_id=character_id).get()
    assert_has_clearance(tkn.owner, "esi-location.read_location.v1", usr)
    assert_has_clearance(tkn.owner, "esi-location.read_online.v1", usr)
    assert_has_clearance(tkn.owner, "esi-location.read_ship_type.v1", usr)
    query_set = EsiCharacterLocation.objects(user=usr).order_by("-timestamp")
    return [
        GetCharacterLocationOut.from_record(location)
        for location in paginate(query_set, 50, page, response)
    ]
Example #19
0
async def auth(ctx: Context, code: str):
    """
    Starts a Discord authentication challenge.
    """
    if ctx.channel.id != conf.discord.auth_channel_id:
        return
    member: Member = ctx.author
    try:
        complete_authentication_challenge(member, code)
        usr: User = User.objects(discord_user_id=member.id).get()
        await update_discord_user(usr)
        logging.info(
            "Successfully authenticated user %s (%d)",
            ctx.author.name,
            ctx.author.id,
        )
        await ctx.channel.send(
            f"Hi <@{ctx.author.id}> :wave:, you are now authenticated. "
            "Welcome aboard!")
        await log(
            f":white_check_mark: Successfully authenticated <@{ctx.author.id}>"
        )
    except Exception as error:
        logging.error(
            "Could not authenticate Discord user %s (%d): %s",
            ctx.author.name,
            ctx.author.id,
            str(error),
        )
        await ctx.channel.send(
            f":x: Sorry <@{ctx.author.id}>, I could not authenticate you. "
            "Please try again with a different code. If the problem persists, "
            "contact your Discord administrator.")
        await log(
            f":x: Failed to authenticate <@{ctx.author.id}>: {str(error)}")
    await ctx.message.delete()