Esempio n. 1
0
def info():
    address = query_param("address")
    id = query_param("id", required=False)

    try:
        ip_network = ipaddress.ip_network(address, False)
    except ValueError:
        return {
                   "error": True,
                   "network_value": address,
                   "syntax": True,
                   "id": id
               }, 200

    _is4 = ip_network.version == 4
    prefix = ip_network.prefixlen
    if (_is4 and prefix < max_allowed_ipv4_sub_mask) or (not _is4 and prefix < max_allowed_ipv6_prefix):
        return {
                   "error": True,
                   "version": ip_network.version,
                   "max": max_allowed_ipv4_sub_mask if _is4 else max_allowed_ipv6_prefix,
                   "network_value": address,
                   "prefix": prefix,
                   "id": id
               }, 200

    return {
               "version": ip_network.version,
               "num_addresses": ip_network.num_addresses,
               "network_value": str(ip_network),
               "lower": str(ip_network[0]),
               "higher": str(ip_network[-1]),
               "id": id
           }, 200
Esempio n. 2
0
def attributes():
    uid = query_param("uid")
    service_entity_id = query_param("service_entity_id")

    def not_authorized_func(_, status):
        if status == USER_UNKNOWN:
            return {"error": f"user {uid} is unknown"}, 404
        elif status == USER_IS_SUSPENDED:
            return {"error": f"user {uid} is suspended"}, 404
        elif status == SERVICE_UNKNOWN or status == SERVICE_NOT_CONNECTED or status == COLLABORATION_NOT_ACTIVE:
            return {}, 200
        elif status == AUP_NOT_AGREED:
            return {"error": f"user {uid} has not agreed to the aup of {service_entity_id}"}, 403

    def authorized_func(user, memberships):
        # gather regular user attributes
        result = {}
        for k, v in custom_saml_mapping["attribute_saml_mapping"].items():
            val = getattr(user, k)
            if val:
                result[v] = val.split(",") if k in custom_saml_mapping["multi_value_attributes"] else [val]
        result["sshKey"] = [ssh_key.ssh_value for ssh_key in user.ssh_keys]
        membership_attribute = custom_saml_mapping['custom_attribute_saml_mapping']['memberships']
        result[membership_attribute] = memberships

        result = {k: list(set(v)) for k, v in result.items()}
        return result, 200

    return _do_attributes(uid, service_entity_id, not_authorized_func, authorized_func)
Esempio n. 3
0
def short_name_exists():
    short_name = query_param("short_name")
    existing_organisation = query_param("existing_organisation", required=False, default="")
    org = Organisation.query.options(load_only("id")) \
        .filter(func.lower(Organisation.short_name) == func.lower(short_name)) \
        .filter(func.lower(Organisation.short_name) != func.lower(existing_organisation)) \
        .first()
    return org is not None, 200
Esempio n. 4
0
File: group.py Progetto: SURFscz/SBS
def short_name_exists():
    short_name = query_param("short_name")
    collaboration_id = query_param("collaboration_id")
    existing_group = query_param("existing_group", required=False, default="")
    group = Group.query.options(load_only("id")) \
        .filter(func.lower(Group.short_name) == func.lower(short_name)) \
        .filter(func.lower(Group.short_name) != func.lower(existing_group)) \
        .filter(Group.collaboration_id == collaboration_id) \
        .first()
    return group is not None, 200
Esempio n. 5
0
def service_group_short_name_exists():
    short_name = query_param("short_name")
    service_id = query_param("service_id")
    existing_service_group = query_param("existing_service_group", required=False, default="")
    service_group = ServiceGroup.query.options(load_only("id")) \
        .filter(func.lower(ServiceGroup.short_name) == func.lower(short_name)) \
        .filter(func.lower(ServiceGroup.short_name) != func.lower(existing_service_group)) \
        .filter(ServiceGroup.service_id == service_id) \
        .first()
    return service_group is not None, 200
Esempio n. 6
0
def abbreviation_exists():
    abbreviation = query_param("abbreviation")
    existing_service = query_param("existing_service", required=False, default="")
    service = Service.query.options(load_only("id")) \
        .filter(func.lower(Service.abbreviation) == func.lower(abbreviation)) \
        .filter(func.lower(Service.abbreviation) != func.lower(existing_service)) \
        .first()
    if service:
        confirm_service_admin(service.id)
    return service is not None, 200
Esempio n. 7
0
def entity_id_exists():
    entity_id = query_param("entity_id")
    existing_service = query_param("existing_service", required=False, default="")
    service = Service.query.options(load_only("id")) \
        .filter(func.lower(Service.entity_id) == func.lower(entity_id)) \
        .filter(func.lower(Service.entity_id) != func.lower(existing_service)) \
        .first()
    if service:
        confirm_service_admin(service.id)
    return service is not None, 200
Esempio n. 8
0
def activity():
    limit = int(query_param("limit", False, 0))
    tables = list(filter(lambda s: s.strip(), query_param("tables", False, "").split(",")))
    query = AuditLog.query.order_by(desc(AuditLog.created_at))
    if tables:
        query = query.filter(AuditLog.target_type.in_(tables))
    if limit:
        query = query.limit(limit)
    audit_logs = query.all()
    return _add_references(audit_logs), 200
Esempio n. 9
0
def schac_home_exists():
    schac_home = query_param("schac_home")
    if not schac_home:
        return False, 200
    existing_organisation_id = query_param("existing_organisation_id", required=False)
    query = SchacHomeOrganisation.query \
        .filter(func.lower(SchacHomeOrganisation.name) == func.lower(schac_home))
    if existing_organisation_id:
        query = query \
            .filter(SchacHomeOrganisation.organisation_id != int(existing_organisation_id))
    res = query.first()
    return res.name if res else False, 200
Esempio n. 10
0
File: mfa.py Progetto: SURFscz/SBS
def sfo():
    logger = ctx_logger("oidc")

    oidc_config = current_app.app_config.oidc
    encoded_access_token = query_param("access_token")

    access_token = decode_jwt_token(encoded_access_token)

    logger.debug(f"MFA endpoint with access_token {access_token}")

    uid = access_token["sub"]
    user = User.query.filter(User.uid == uid).first()
    if not user:
        error_msg = f"Unknown user with sub {uid}"
        logger.error(error_msg)
        return redirect(f"{oidc_config.sfo_eduteams_redirect_uri}?error={quote(error_msg)}")

    if not oidc_config.second_factor_authentication_required:
        id_token = _construct_jwt(user, access_token.get("nonce", str(uuid4())), oidc_config)
        return redirect(f"{oidc_config.sfo_eduteams_redirect_uri}?id_token={id_token}")
    # need to remember this if the user response comes back
    session["in_proxy_flow"] = True

    store_user_in_session(user, False, user.has_agreed_with_aup())

    return redirect(f"{current_app.app_config.base_url}/2fa")
Esempio n. 11
0
def other():
    confirm_allow_impersonation()

    uid = query_param("uid")
    user = _user_query().filter(User.uid == uid).one()
    # avoid 2fa registration / validation
    return _user_json_response(user, True)
Esempio n. 12
0
def service_invitations_by_hash():
    hash_value = query_param("hash")
    invitation_query = _service_invitation_query()
    service_invitation = invitation_query \
        .options(selectinload(ServiceInvitation.service).selectinload(Service.service_memberships)
                 .selectinload(ServiceMembership.user)) \
        .filter(ServiceInvitation.hash == hash_value) \
        .one()
    return service_invitation, 200
Esempio n. 13
0
def identity_provider_display_name():
    user = User.query.filter(User.id == current_user_id()).one()
    schac_home_organisation = user.schac_home_organisation
    if not schac_home_organisation:
        return None, 200

    lang = query_param("lang", required=False, default="en")
    idp_name = idp_display_name(schac_home_organisation, lang)
    return {"display_name": idp_name}, 200
Esempio n. 14
0
def invitations_by_hash():
    hash_value = query_param("hash")
    invitation_query = _invitation_query()
    invitation = invitation_query \
        .options(selectinload(Invitation.groups)) \
        .options(selectinload(Invitation.collaboration).selectinload(Collaboration.collaboration_memberships)
                 .selectinload(CollaborationMembership.user)) \
        .options(selectinload(Invitation.collaboration).selectinload(Collaboration.groups)) \
        .options(selectinload(Invitation.collaboration).selectinload(Collaboration.services)) \
        .options(selectinload(Invitation.collaboration).selectinload(Collaboration.organisation)
                 .selectinload(Organisation.services)) \
        .filter(Invitation.hash == hash_value) \
        .one()
    if not query_param("expand", required=False):
        return invitation, 200

    invitation_json = jsonify(invitation).json
    service_emails = invitation.collaboration.service_emails()
    return {"invitation": invitation_json, "service_emails": service_emails}, 200
Esempio n. 15
0
def _user_activity(user_id):
    limit = int(query_param("limit", False, 1000))
    audit_logs = AuditLog.query \
        .filter((
                    (AuditLog.target_id == user_id) & (AuditLog.target_type == User.__tablename__)) | (
                    AuditLog.subject_id == user_id)) \
        .order_by(desc(AuditLog.created_at)) \
        .limit(limit) \
        .all()
    return _add_references(audit_logs), 200
Esempio n. 16
0
def organisation_invitations_by_hash():
    hash_value = query_param("hash")
    invitation_query = _organisation_invitation_query()
    organisation_invitation = invitation_query \
        .options(selectinload(OrganisationInvitation.organisation).selectinload(Organisation.organisation_memberships)
                 .selectinload(OrganisationMembership.user)) \
        .options(selectinload(OrganisationInvitation.organisation).selectinload(Organisation.services)) \
        .filter(OrganisationInvitation.hash == hash_value) \
        .one()
    return organisation_invitation, 200
Esempio n. 17
0
def attribute_aggregation():
    confirm_read_access()

    edu_person_principal_name = query_param("edu_person_principal_name")
    email = query_param("email", required=False)

    users = User.query \
        .join(User.collaboration_memberships) \
        .join(CollaborationMembership.collaboration) \
        .options(selectinload(User.collaboration_memberships)
                 .selectinload(CollaborationMembership.collaboration)) \
        .filter(or_(User.uid == edu_person_principal_name,
                    User.email == email)) \
        .all()

    # preference over edu_person_principal_name
    if len(users) == 0:
        return None, 404

    users_eppn_match = list(filter(lambda u: u.uid == edu_person_principal_name, users))
    user = users[0] if len(users) == 1 else users_eppn_match[0] if len(users_eppn_match) == 1 else users[0]

    return [cm.collaboration.name for cm in user.collaboration_memberships], 200
Esempio n. 18
0
def my_collaborations_lite():
    include_services = query_param("includeServices", False)
    user_id = current_user_id()
    query = Collaboration.query \
        .join(Collaboration.collaboration_memberships) \
        .options(selectinload(Collaboration.organisation))
    if include_services:
        query = query \
            .options(selectinload(Collaboration.services).selectinload(Service.allowed_organisations))

    collaborations = query \
        .filter(CollaborationMembership.user_id == user_id) \
        .all()
    return collaborations, 200
Esempio n. 19
0
def collaboration_by_identifier():
    identifier = query_param("identifier")

    collaboration = Collaboration.query \
        .outerjoin(Collaboration.collaboration_memberships) \
        .outerjoin(CollaborationMembership.user) \
        .options(selectinload(Collaboration.organisation).selectinload(Organisation.services)) \
        .options(selectinload(Collaboration.services)) \
        .options(selectinload(Collaboration.groups)) \
        .options(selectinload(Collaboration.collaboration_memberships)
                 .selectinload(CollaborationMembership.user)) \
        .filter(Collaboration.identifier == identifier).one()

    collaboration_json = jsonify(collaboration).json
    service_emails = collaboration.service_emails()
    return {"collaboration": collaboration_json, "service_emails": service_emails}, 200
Esempio n. 20
0
def members():
    confirm_authorized_api_call()

    identifier = query_param("identifier")
    collaboration_group = aliased(Collaboration)
    collaboration_membership = aliased(Collaboration)

    users = User.query \
        .options(load_only("uid", "name")) \
        .join(User.collaboration_memberships) \
        .join(collaboration_membership, CollaborationMembership.collaboration) \
        .join(CollaborationMembership.groups) \
        .join(collaboration_group, Group.collaboration) \
        .filter(or_(collaboration_group.identifier == identifier,
                    collaboration_membership.identifier == identifier)) \
        .all()
    return users, 200
Esempio n. 21
0
def collaboration_search():
    confirm_allow_impersonation()

    res = []
    q = query_param("q")
    if q and len(q):
        base_query = "SELECT id, name, description, organisation_id FROM collaborations "
        not_wild_card = "*" not in q
        if not_wild_card:
            q = replace_full_text_search_boolean_mode_chars(q)
            base_query += f"WHERE MATCH (name, description) AGAINST (:q IN BOOLEAN MODE) " \
                          f"AND id > 0 ORDER BY NAME LIMIT {full_text_search_autocomplete_limit}"
        sql = text(base_query if not_wild_card else base_query + " ORDER BY NAME")
        if not_wild_card:
            sql = sql.bindparams(bindparam("q", type_=String))
        result_set = db.engine.execute(sql, {"q": f"{q}*"}) if not_wild_card else db.engine.execute(sql)
        res = [{"id": row[0], "name": row[1], "description": row[2], "organisation_id": row[3]} for row in result_set]
    return res, 200
Esempio n. 22
0
def service_by_uuid4():
    uuid4 = urllib.parse.unquote(query_param("uuid4"))
    user = User.query.get(current_user_id())
    service = Service.query.filter(Service.uuid4 == uuid4).one()

    if not is_application_admin() and not user_service(service.id):
        raise Forbidden()

    service_emails = {}
    if service.contact_email:
        service_emails[service.id] = [service.contact_email]
    else:
        service_emails[service.id] = [membership.user.email for membership in service.service_memberships]

    collaborations = []
    for cm in user.collaboration_memberships:
        if service.id in [s.id for s in cm.collaboration.services]:
            collaborations.append(cm.collaboration)

    return {"service": service, "collaborations": collaborations, "service_emails": service_emails}, 200
Esempio n. 23
0
def organisation_search():
    confirm_write_access(override_func=is_service_admin)

    res = []
    q = query_param("q")
    if q and len(q):
        base_query = "SELECT id, name, description, category, logo, short_name FROM organisations "
        not_wild_card = "*" not in q
        if not_wild_card:
            q = replace_full_text_search_boolean_mode_chars(q)
            base_query += f"WHERE MATCH (name, description) AGAINST (:q IN BOOLEAN MODE) " \
                          f"AND id > 0  ORDER BY NAME LIMIT {full_text_search_autocomplete_limit}"
        sql = text(base_query if not_wild_card else base_query + " ORDER BY NAME")
        if not_wild_card:
            sql = sql.bindparams(bindparam("q", type_=String))
        result_set = db.engine.execute(sql, {"q": f"{q}*"}) if not_wild_card else db.engine.execute(sql)

        res = [{"id": row[0], "name": row[1], "description": row[2], "category": row[3], "logo": row[4],
                "short_name": row[5]} for row in result_set]
    return res, 200
Esempio n. 24
0
def short_name_exists():
    name = query_param("short_name")
    organisation_id = int(query_param("organisation_id"))
    existing_collaboration = query_param("existing_collaboration", required=False, default="")
    res = _do_short_name_exists(name, organisation_id, existing_collaboration)
    return res, 200
Esempio n. 25
0
def service_by_entity_id():
    entity_id = urllib.parse.unquote(query_param("entity_id"))
    return Service.query \
               .options(selectinload(Service.allowed_organisations)) \
               .filter(Service.entity_id == entity_id) \
               .one(), 200
Esempio n. 26
0
def all_services():
    include_counts = query_param("include_counts", required=False)
    return _do_get_services(include_counts=include_counts)
Esempio n. 27
0
def user_search():
    confirm_allow_impersonation()

    q = query_param("q")
    organisation_id = query_param("organisation_id", required=False)
    collaboration_id = query_param("collaboration_id", required=False)
    organisation_admins = query_param("organisation_admins", required=False)
    collaboration_admins = query_param("collaboration_admins", required=False)

    base_query = "SELECT u.id, u.uid, u.name, u.email, o.name, om.role, c.name, cm.role  FROM users u "

    organisation_join = " INNER " if organisation_id or organisation_admins else "LEFT "
    base_query += f"{organisation_join} JOIN organisation_memberships om ON om.user_id = u.id " \
                  f"{organisation_join} JOIN organisations o ON o.id = om.organisation_id "

    collaboration_join = " INNER " if collaboration_id or collaboration_admins else "LEFT "
    base_query += f"{collaboration_join} JOIN collaboration_memberships cm ON cm.user_id = u.id " \
                  f"{collaboration_join} JOIN collaborations c ON c.id = cm.collaboration_id "

    base_query += " WHERE 1=1 "
    not_wild_card = q != "*"
    if not_wild_card:
        q = replace_full_text_search_boolean_mode_chars(q)
        base_query += "AND MATCH (u.name, u.email) AGAINST (:q IN BOOLEAN MODE) " \
                      "AND u.id > 0 "

    if organisation_id:
        base_query += f"AND om.organisation_id = {int(organisation_id)} "

    if collaboration_id:
        base_query += f"AND cm.collaboration_id = {int(collaboration_id)} "

    if organisation_admins:
        base_query += "AND om.role = 'admin'"

    if collaboration_admins:
        base_query += "AND cm.role = 'admin'"

    base_query += f" ORDER BY u.name  LIMIT {full_text_search_autocomplete_limit}"

    sql = text(base_query)

    if not_wild_card:
        sql = sql.bindparams(bindparam("q", type_=String))
    result_set = db.engine.execute(sql, {"q": f"{q}*"}) if not_wild_card else db.engine.execute(sql)
    data = [{"id": row[0], "uid": row[1], "name": row[2], "email": row[3], "organisation_name": row[4],
             "organisation_role": row[5], "collaboration_name": row[6],
             "collaboration_role": row[7]} for row in result_set]

    res = []
    for key, group in itertools.groupby(data, lambda u: u["id"]):
        user_info = {"id": key, "organisations": [], "collaborations": []}
        for g in group:
            user_info["uid"] = g["uid"]
            user_info["name"] = g["name"]
            user_info["email"] = g["email"]
            user_info["admin"] = is_admin_user(g)
            if g["organisation_name"] is not None and g["organisation_name"] not in [item["name"] for item in
                                                                                     user_info["organisations"]]:
                user_info["organisations"].append({"name": g["organisation_name"], "role": g["organisation_role"]})
            if g["collaboration_name"] is not None and g["collaboration_name"] not in [item["name"] for item in
                                                                                       user_info["collaborations"]]:
                user_info["collaborations"].append({"name": g["collaboration_name"], "role": g["collaboration_role"]})
        res.append(user_info)
    return res, 200
Esempio n. 28
0
def authorization():
    state = query_param("state", required=False, default=None)
    authorization_endpoint = _get_authorization_url(state)
    return {"authorization_endpoint": authorization_endpoint}, 200
Esempio n. 29
0
def resume_session():
    logger = ctx_logger("oidc")

    cfg = current_app.app_config
    oidc_config = cfg.oidc
    code = query_param("code", required=False, default=None)
    if not code:
        # This means that we are not in the redirect callback, but at the redirect from eduTeams
        logger.debug("Redirect to login in resume-session to start OIDC flow")
        authorization_endpoint = _get_authorization_url()
        return redirect(authorization_endpoint)

    scopes = " ".join(oidc_config.scopes)
    payload = {
        "code": code,
        "grant_type": "authorization_code",
        "scope": scopes,
        "redirect_uri": oidc_config.redirect_uri
    }
    headers = {
        "Content-Type": "application/x-www-form-urlencoded",
        "Cache-Control": "no-cache",
        "Accept": "application/json, application/json;charset=UTF-8"
    }
    response = requests.post(oidc_config.token_endpoint, data=urllib.parse.urlencode(payload),
                             headers=headers, auth=(oidc_config.client_id, oidc_config.client_secret))
    if response.status_code != 200:
        return _redirect_with_error(logger, f"Server error: Token endpoint error (http {response.status_code}")

    token_json = response.json()
    access_token = token_json["access_token"]

    headers = {
        "Accept": "application/json, application/json;charset=UTF-8",
        "Authorization": f"Bearer {access_token}"
    }

    response = requests.get(oidc_config.userinfo_endpoint, headers=headers)
    if response.status_code != 200:
        return _redirect_with_error(logger, f"Server error: User info endpoint error (http {response.status_code}")

    logger = ctx_logger("user")
    user_info_json = response.json()

    logger.debug(f"Userinfo endpoint results {user_info_json}")

    uid = user_info_json["sub"]
    user = User.query.filter(User.uid == uid).first()
    if not user:
        user = User(uid=uid, created_by="system", updated_by="system")
        add_user_claims(user_info_json, uid, user)

        # last_login_date is set later in this method
        user.last_accessed_date = datetime.datetime.now()
        logger.info(f"Provisioning new user {user.uid}")
    else:
        logger.info(f"Updating user {user.uid} with new claims / updated at")
        add_user_claims(user_info_json, uid, user)

    encoded_id_token = token_json["id_token"]
    id_token = decode_jwt_token(encoded_id_token)

    no_mfa_required = not oidc_config.second_factor_authentication_required
    idp_mfa = id_token.get("acr") == ACR_VALUES

    idp_allowed = mfa_idp_allowed(user, user.schac_home_organisation, None)

    second_factor_confirmed = no_mfa_required or idp_mfa or idp_allowed
    if second_factor_confirmed:
        user.last_login_date = datetime.datetime.now()

    user = db.session.merge(user)
    db.session.commit()

    user_accepted_aup = user.has_agreed_with_aup()
    store_user_in_session(user, second_factor_confirmed, user_accepted_aup)

    if not user_accepted_aup:
        location = f"{cfg.base_url}/aup"
    elif not second_factor_confirmed:
        location = f"{cfg.base_url}/2fa"
    else:
        location = session.get("original_destination", cfg.base_url)

    return redirect(location)
Esempio n. 30
0
def find_by_id():
    confirm_write_access()
    return _user_query() \
               .options(joinedload(User.service_aups)
                        .subqueryload(ServiceAup.service)) \
               .filter(User.id == query_param("id")).one(), 200