def info(query_id, collection_name): def groups_permission(group_id): coll_id = Group.query.get(group_id).collaboration_id return confirm_group_member(group_id) or is_current_user_collaboration_admin( coll_id) or is_current_user_organisation_admin_or_manager(coll_id) def collaboration_permission(collaboration_id): return is_current_user_collaboration_admin(collaboration_id) or is_current_user_organisation_admin_or_manager( collaboration_id) override_func = collaboration_permission if collection_name == "collaborations" \ else groups_permission if collection_name == "groups" \ else is_organisation_admin_or_manager if collection_name == "organisations" else None confirm_read_access(query_id, override_func=override_func) audit_logs = AuditLog.query \ .filter(or_(and_(AuditLog.parent_id == query_id, AuditLog.parent_name == collection_name), and_(AuditLog.target_id == query_id, AuditLog.target_type == collection_name))) \ .order_by(desc(AuditLog.created_at)) \ .limit(150) \ .all() res = _add_references(audit_logs) return res, 200
def service_by_id(service_id): confirm_read_access(service_id, override_func=user_service) query = Service.query \ .options(selectinload(Service.service_memberships).selectinload(ServiceMembership.user)) api_call = request_context.is_authorized_api_call add_admin_info = not api_call and (is_application_admin() or is_service_admin(service_id)) if add_admin_info: query = query \ .options(selectinload(Service.collaborations).selectinload(Collaboration.organisation)) \ .options(selectinload(Service.service_memberships).selectinload(ServiceMembership.user)) \ .options(selectinload(Service.organisations)) \ .options(selectinload(Service.service_invitations)) \ .options(selectinload(Service.allowed_organisations)) \ .options(selectinload(Service.ip_networks)) \ .options(selectinload(Service.service_groups)) service = query.filter(Service.id == service_id).one() res = jsonify(service).json res["service_organisation_collaborations"] = [] # To prevent circular references value error if len(res["organisations"]) > 0: collaborations = Collaboration.query \ .options(selectinload(Collaboration.organisation)) \ .join(Collaboration.organisation) \ .join(Organisation.services) \ .filter(Service.id == service_id) \ .all() res["service_organisation_collaborations"] = jsonify(collaborations).json return res, 200 if api_call: query = query \ .options(selectinload(Service.ip_networks)) service = query.filter(Service.id == service_id).one() res = jsonify(service).json del res["logo"] return res, 200 return query.filter(Service.id == service_id).one(), 200
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
def sync(): confirm_read_access() result = {"organisations": [], "users": []} rs = db.engine.execute("SELECT name, organisation_id FROM schac_home_organisations") schac_home_organisations = [{"name": row[0], "organisation_id": row[1]} for row in rs] rs = db.engine.execute("SELECT id, name, entity_id, contact_email, uuid4, ldap_password, accepted_user_policy, " "support_email, security_email FROM services") services = [{"id": row[0], "name": row[1], "entity_id": row[2], "contact_email": row[3], "logo": logo_url("services", row[4]), "ldap_password": row[5], "accepted_user_policy": row[6], "support_email": row[7], "security_email": row[6]} for row in rs] for service in services: if not service["contact_email"]: rs = db.engine.execute(f"select u.email from users u where id in " f"(select user_id from service_memberships where service_id = {service['id']}) limit 1") for row in rs: service["contact_email"] = row[0] rs = db.engine.execute("SELECT service_id, organisation_id FROM services_organisations") services_organisations = [{"service_id": row[0], "organisation_id": row[1]} for row in rs] rs = db.engine.execute("SELECT service_id, collaboration_id FROM services_collaborations") services_collaborations = [{"service_id": row[0], "collaboration_id": row[1]} for row in rs] rs = db.engine.execute("SELECT role, user_id, organisation_id FROM organisation_memberships") organisation_memberships = [{"role": row[0], "user_id": row[1], "organisation_id": row[2]} for row in rs] rs = db.engine.execute("SELECT id, role, user_id, collaboration_id, status FROM collaboration_memberships") collaboration_memberships = [ {"id": row[0], "role": row[1], "user_id": row[2], "collaboration_id": row[3], "status": row[4]} for row in rs] rs = db.engine.execute("SELECT collaboration_membership_id, group_id FROM collaboration_memberships_groups") collaboration_memberships_groups = [{"collaboration_membership_id": row[0], "group_id": row[1]} for row in rs] rs = db.engine.execute("SELECT id, identifier, name, short_name, global_urn, organisation_id, status, description, " "uuid4, website_url from collaborations") collaborations = [ {"id": row[0], "identifier": row[1], "name": row[2], "short_name": row[3], "global_urn": row[4], 'organisation_id': row[5], "status": row[6], "description": row[7], "logo": logo_url("collaborations", row[8]), "website_url": row[9]} for row in rs] rs = db.engine.execute( "SELECT id, name, short_name, global_urn, identifier, collaboration_id, description FROM `groups`") groups = [ {"id": row[0], "name": row[1], "short_name": row[2], "global_urn": row[3], "identifier": row[4], "collaboration_id": row[5], "description": row[6]} for row in rs] for coll in collaborations: collaboration_id = coll["id"] coll["groups"] = _find_by_id(groups, "collaboration_id", collaboration_id) for group in coll["groups"]: group["collaboration_memberships"] = _find_by_id(collaboration_memberships_groups, "group_id", group["id"]) for collaboration_membership in group["collaboration_memberships"]: collaboration_membership["user_id"] = _find_user_id(collaboration_memberships, collaboration_membership[ "collaboration_membership_id"]) service_identifiers = _find_by_id(services_collaborations, "collaboration_id", collaboration_id) coll["services"] = _identifiers_only( _find_by_identifiers(services, "id", [si["service_id"] for si in service_identifiers])) coll["collaboration_memberships"] = _find_by_id(collaboration_memberships, "collaboration_id", collaboration_id) rs = db.engine.execute("SELECT id, name, identifier, short_name, uuid4 FROM organisations") for row in rs: organisation_id = row[0] service_identifiers = _find_by_id(services_organisations, "organisation_id", organisation_id) result["organisations"].append({ "id": organisation_id, "name": row[1], "identifier": row[2], "short_name": row[3], "logo": logo_url("organisations", row[4]), "schac_home_organisations": _find_by_id(schac_home_organisations, "organisation_id", organisation_id), "organisation_memberships": _find_by_id(organisation_memberships, "organisation_id", organisation_id), "collaborations": _find_by_id(collaborations, "organisation_id", organisation_id), "services": _identifiers_only( _find_by_identifiers(services, "id", [si["service_id"] for si in service_identifiers])) }) result["services"] = services rs = db.engine.execute("SELECT id, uid, name, given_name, family_name, email, scoped_affiliation, " "eduperson_principal_name, username, last_login_date FROM users") for row in rs: user_row = {"id": row[0], "uid": row[1], "name": row[2], "given_name": row[3], "family_name": row[4], "email": row[5], "scoped_affiliation": row[6], "eduperson_principal_name": row[7], "username": row[8], "last_login_date": str(row[9])} rs_ssh_keys = db.engine.execute(f"SELECT ssh_value FROM ssh_keys WHERE user_id = {row[0]}") user_row["ssh_keys"] = [r[0] for r in rs_ssh_keys] user_ip_networks = db.engine.execute(f"SELECT network_value FROM user_ip_networks WHERE user_id = {row[0]}") user_row["user_ip_networks"] = [r[0] for r in user_ip_networks] service_aups = db.engine.execute(f"SELECT aup_url, service_id, agreed_at FROM service_aups " f"WHERE user_id = {row[0]}") user_row["accepted_aups"] = [{"url": r[0], "service_id": r[1], "agreed_at": str(r[2])} for r in service_aups] result["users"].append(user_row) return result, 200
def _do_attributes(uid, service_entity_id, not_authorized_func, authorized_func, require_2fa=False, issuer_id=None): confirm_read_access() logger = ctx_logger("user_api") service = Service.query.filter(Service.entity_id == service_entity_id).first() if not service: msg = f"Returning unauthorized for user {uid} and service_entity_id {service_entity_id} " \ f"as the service is unknown" logger.error(msg) send_error_mail(tb=msg, session_exists=False) return not_authorized_func(service_entity_id, SERVICE_UNKNOWN) no_free_ride = not service.non_member_users_access_allowed user = User.query.filter(User.uid == uid).first() if not user: logger.error(f"Returning unauthorized for user {uid} and service_entity_id {service_entity_id}" f" as the user is unknown") return not_authorized_func(service.name, USER_UNKNOWN) if user.suspended and no_free_ride: logger.error(f"Returning unauthorized for user {uid} and service_entity_id {service_entity_id}" f" as the user is suspended") return not_authorized_func(service.name, USER_IS_SUSPENDED) connected_collaborations = [] memberships = [] for cm in user.collaboration_memberships: connected = list(filter(lambda s: s.id == service.id, cm.collaboration.services)) if connected or list(filter(lambda s: s.id == service.id, cm.collaboration.organisation.services)): connected_collaborations.append(cm.collaboration) memberships.append(cm) if not connected_collaborations and no_free_ride: logger.error(f"Returning unauthorized for user {uid} and service_entity_id {service_entity_id}" f" as the service is not connected to any of the user collaborations") return not_authorized_func(service.name, SERVICE_NOT_CONNECTED) if all(coll.status != STATUS_ACTIVE for coll in connected_collaborations) and no_free_ride: logger.error(f"Returning unauthorized for user {uid} and service_entity_id {service_entity_id}" f" as the service is not connected to any active collaborations") return not_authorized_func(service.name, COLLABORATION_NOT_ACTIVE) if all(m.is_expired() for m in memberships) and no_free_ride: logger.error(f"Returning unauthorized for user {uid} and service_entity_id {service_entity_id}" f" as none of the collaboration memberships are active") return not_authorized_func(service.name, MEMBERSHIP_NOT_ACTIVE) # Leave the 2FAand AUP checks as the last checks as these are the only exceptions that can be recovered from if require_2fa: idp_allowed = mfa_idp_allowed(user, user.schac_home_organisation, issuer_id) if not idp_allowed: logger.debug(f"Returning interrupt for user {uid} from issuer {issuer_id} to perform 2fa") return not_authorized_func(user, SECOND_FA_REQUIRED) if not has_agreed_with(user, service): logger.debug(f"Returning interrupt for user {uid} and service_entity_id {service_entity_id} to accept AUP") return not_authorized_func(service, AUP_NOT_AGREED) now = datetime.now() for coll in connected_collaborations: coll.last_activity_date = now db.session.merge(coll) user.last_accessed_date = now user.last_login_date = now user.suspend_notifications = [] user = db.session.merge(user) db.session.commit() all_memberships = user_memberships(user, connected_collaborations) all_attributes, http_status = authorized_func(user, all_memberships) logger.info(f"Returning attributes {all_attributes} for user {uid} and service_entity_id {service_entity_id}") return all_attributes, http_status