def register_models(oso: Oso, base): """Register all models in model base class ``base`` with oso as classes.""" # TODO (dhatch): Not sure this is legit b/c it uses an internal interface? for name, model in base._decl_class_registry.items(): if name == "_sa_module_registry": continue oso.register_class(model)
def test_oso(): oso = Oso() oso.register_class(Actor, name="test_oso::Actor") oso.register_class(Widget, name="test_oso::Widget") oso.register_class(Company, name="test_oso::Company") oso.load_file(Path(__file__).parent / "test_oso.polar") return oso
def test_quickstart_policy_4(): oso = Oso() oso.register_class(Expense) oso.load_file("../polar/expenses-04.polar") assert oso.is_allowed("*****@*****.**", "GET", EXPENSES[1]) assert not oso.is_allowed("*****@*****.**", "GET", EXPENSES[3]) assert not oso.is_allowed("*****@*****.**", "GET", EXPENSES[1]) assert oso.is_allowed("*****@*****.**", "GET", EXPENSES[3])
def init_oso(app): base_oso = Oso() oso = FlaskOso(base_oso) register_models(base_oso, Base) set_get_session(base_oso, lambda: g.session) base_oso.load_file("app/authorization.polar") app.oso = oso
def init_oso(db: Session): oso = Oso() register_models(oso, Base) set_get_session(oso, lambda: db) oso.load_file("app/authorization/rules/role_basics.polar") oso.load_file("app/authorization/rules/organization_permissions.polar") oso.load_file("app/authorization/rules/team_permissions.polar") oso.load_file("app/authorization/rules/dataroom_permissions.polar") enable_roles(oso) return oso
def test_quickstart_policy_2(): oso = Oso() alice = "*****@*****.**" expense = EXPENSES[1] assert not oso.is_allowed(alice, "GET", expense) oso.register_class(Expense) oso.load_file("../polar/expenses-02.polar") assert oso.is_allowed(alice, "GET", expense) assert not oso.is_allowed("*****@*****.**", "GET", expense)
def test_quickstart_policy_3(): oso = Oso() oso.register_class(Expense) oso.load_file("../polar/expenses-03-py.polar") expense = EXPENSES[1] assert oso.is_allowed("*****@*****.**", "GET", expense) assert not oso.is_allowed("*****@*****.**", "GET", expense)
def test_multi(): oso = Oso() oso.load_str("allow(x, y) if x == y;") tp = ThreadPoolExecutor(max_workers=8) futures = [] for _ in range(32): futures.append(tp.submit(torch_oso, oso)) for i, future in enumerate(concurrent.futures.as_completed(futures)): future.result() # If we got here none of these crashed. assert True
def update_team_role( org_id: UUID, team_id: UUID, role_id: UUID, req: schemas.TeamRoleRequest, current_user: models.User = Depends(deps.get_current_active_user), db: Session = Depends(deps.get_db), auth: Oso = Depends(deps.get_oso), ): team = crud.team.get(db, team_id) if team is None or team.organization_fk != org_id: raise HTTPException(status_code=404) if not auth.is_allowed(current_user, "DELETE_ROLE", team): raise HTTPException(status_code=403) role = db.query(models.TeamRole).filter_by(id=role_id).first() if role is None: raise HTTPException(status_code=404) if role.user_id != req.user_id: raise HTTPException(status_code=404) user = crud.user.get(db, id=role.user_id) if user is None: raise HTTPException(status_code=404) crud.team_roles.update_user_role(db, user=user, resource=team, role_name=req.user_role) return "OK"
def create_team_role( org_id: UUID, team_id: UUID, req: schemas.TeamRoleRequest, current_user: models.User = Depends(deps.get_current_active_user), db: Session = Depends(deps.get_db), auth: Oso = Depends(deps.get_oso), ): team = crud.team.get(db, team_id) if team is None or team.organization_fk != org_id: raise HTTPException(status_code=404) if not auth.is_allowed(current_user, "CREATE_ROLE", team): raise HTTPException(status_code=403) user_to_add = crud.user.get(db, id=req.user_id) if user_to_add is None: raise HTTPException(status_code=404) crud.team_roles.add_user_role(db, user=user_to_add, resource=team, role_name=req.user_role) return "OK"
def create_team( org_id: UUID, req: schemas.TeamCreateRequest, current_user: models.User = Depends(deps.get_current_active_user), db: Session = Depends(deps.get_db), auth: Oso = Depends(deps.get_oso), ): org = crud.org.get(db, org_id) if org is None: raise HTTPException(status_code=404) # check if user is allowed to create a new team create_team = schemas.TeamCreate( creator=current_user, organization=org, name=req.name, description=req.description, is_active=True, ) new_team = crud.team.create_model(create_team) if not auth.is_allowed(current_user, "CREATE", new_team): raise HTTPException(status_code=403) # add the newly created model to db created_team = crud.team.add_model_to_db(db, model=new_team) # add the user as the team owner crud.team_roles.add_user_role(db, current_user, created_team, "OWNER") return created_team
def delete_dataroom_team_role( org_id: UUID, dataroom_id: UUID, role_id: UUID, current_user: models.User = Depends(deps.get_current_active_user), db: Session = Depends(deps.get_db), auth: Oso = Depends(deps.get_oso), ): dataroom = crud.dataroom.get(db, dataroom_id) if dataroom is None or dataroom.organization_fk != org_id: raise HTTPException(status_code=404) if not auth.is_allowed(current_user, "DELETE_ROLE", dataroom): raise HTTPException(status_code=403) role = db.query(models.DataRoomRole).filter_by(id=role_id).first() if role is None: raise HTTPException(status_code=404) if role.team_id != role.team_id: raise HTTPException(status_code=404) crud.room_roles.delete_team_role(db, team_role_id=role.id) return "OK"
def create_dataroom_team_role( org_id: UUID, dataroom_id: UUID, req: schemas.DataRoomTeamRoleRequest, current_user: models.User = Depends(deps.get_current_active_user), db: Session = Depends(deps.get_db), auth: Oso = Depends(deps.get_oso), ): dataroom = crud.dataroom.get(db, dataroom_id) if dataroom is None or dataroom.organization_fk != org_id: raise HTTPException(status_code=404) if not auth.is_allowed(current_user, "CREATE_ROLE", dataroom): raise HTTPException(status_code=403) team_to_add = crud.team.get(db, id=req.team_id) if team_to_add is None: raise HTTPException(status_code=404) crud.room_roles.add_team_role(db, team=team_to_add, room=dataroom, role_name=req.team_role) return "OK"
def set_get_session(oso: Oso, get_session_func): """Set the function that oso uses to expose a SQLAlchemy session to the policy :param oso: The Oso instance used to evaluate the policy. :type oso: Oso :param get_session_func: A function that returns a SQLAlchemy session :type get_session_func: lambda The session can be accessed from polar via the OsoSession constant. E.g., .. code-block:: polar OsoSession.get().query(...) """ _OsoSession.set_get_session(get_session_func) oso.register_constant(_OsoSession, "OsoSession")
def load_oso(): """Loads and returns the oso policy""" oso = Oso() policy_path = Path(__file__).resolve().parent.parent / "expenses" ## Policy Data oso.load_file(policy_path / "data.polar") ## Role definitions oso.load_file(policy_path / "roles.polar") ## ABAC policy oso.load_file(policy_path / "abac.polar") return oso
class Oso(models.AbstractModel): _name = "oso" _description = "global oso state" oso = Oso() def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) policy = get_resource_path("oso_auth", "security", "base.polar") self.oso.load_file(policy)
def create_invite_to_room( dataroom_id: UUID, org_id: UUID, req: schemas.InviteAPIRequest, current_user: models.User = Depends(deps.get_current_active_user), db: Session = Depends(deps.get_db), auth: Oso = Depends(deps.get_oso), ): # check whether the path is correct so that dataroom belongs to org # and dataroom actually exists room = crud.dataroom.get(db, id=dataroom_id) if not room: raise HTTPException(status_code=400) if not room.organization_fk == org_id: raise HTTPException(status_code=400) # check whether the user is allowed to create invites if not auth.is_allowed(current_user, "INVITE_GUESTS", room): raise HTTPException(status_code=404, detail="Not enough privileges") # check whether the invitee already exists in db, if not create him/her create_user = schemas.UserCreate(email=req.email, password=security.random_password()) invitee = crud.user.create_if_not_exists(db, obj_in=create_user) # add user to the specific role crud.room_roles.add_user_role(db, invitee, room, req.user_role) # generate access url access_token_expires = timedelta(days=req.expires_in) token = security.create_access_token(invitee.id, expires_delta=access_token_expires) # generate invitation entry in db create_invite = schemas.InviteCreate( invitee=invitee, jwt_token=token, dataroom=room, creator=current_user, expires_in=security.calculate_expires_in_days(req.expires_in), ) crud.invite.create(db, obj_in=create_invite) # generate access link access_link = f"{settings.DOMAIN_NAME}/access?token={token}" # generate email and send invite to the user send_access_email( email_to=req.email, access_link=access_link, invitor_name=f"{current_user.first_name} {current_user.last_name}", ) return "OK"
def test_set_get_session(): from sqlalchemy_oso.session import set_get_session from oso import Oso def get_session(): engine = create_engine("sqlite://") Base.metadata.create_all(engine) Session = sessionmaker(bind=engine) session = Session() load_fixture_data(session) return session oso = Oso() set_get_session(oso, get_session) register_models(oso, Base) test_str = """get_repo(name: String) if session = OsoSession.get() and repo = session.query(Repository).filter_by(name: name).first() and repo.name = name; """ oso.load_str(test_str) results = oso.query_rule("get_repo", "Abbey Road") assert next(results) results = oso.query_rule("get_repo", "Abbey Road") assert next(results)
def rmdir(path): import shutil import getpass from oso import Oso oso = Oso() oso.register_class(PathAttributes) oso.load_files(["rmdir.polar"]) path_attributes = get_path_attributes(path) user_id = getpass.getuser() if oso.is_allowed(user_id, "can_remove", path_attributes): shutil.rmtree(path) else: raise PermissionError(f"You cannot delete {path}")
def load_oso(): """Loads and returns the oso policy""" oso = Oso() policy_path = Path(__file__).resolve().parent.parent / "policies" # Role definitions oso.load_file(policy_path / "rbac.polar") # ABAC policy oso.load_file(policy_path / "abac.polar") return oso
def list_documents( dataroom_id: UUID, org_id: UUID, db: Session = Depends(deps.get_db), auth: Oso = Depends(deps.get_oso), current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: # check whether the path is correct and dataroom belongs to org room = crud.dataroom.get(db, id=dataroom_id) if not room.organization_fk == org_id: raise HTTPException(status_code=400) # make sure user is allowed to list documents if not auth.is_allowed(current_user, "LIST_DOCUMENTS", room): raise HTTPException(status_code=404) documents = crud.document.get_multi_by_room(db, dataroom_id) return documents
def get_datarooms( org_id: UUID, db: Session = Depends(deps.get_db), auth: Oso = Depends(deps.get_oso), current_user: models.User = Depends(deps.get_current_active_user), ): org = crud.org.get(db, org_id) if org is None: raise HTTPException(status_code=404) # first find out if the user is allowed to list rooms in a given org if not auth.is_allowed(current_user, "LIST_ROOMS", org): raise HTTPException(status_code=400, detail="Not enough privileges") rooms = crud.dataroom.get_multi_by_org(db, org) return rooms
def authorize_model_filter(oso: Oso, actor, action, session: Session, model): """Return SQLAlchemy expression that applies the policy to ``model``. Executing this query will return only authorized objects. If the request is not authorized, a query that always contains no result will be returned. :param oso: The oso class to use for evaluating the policy. :param actor: The actor to authorize. :param action: The action to authorize. :param session: The SQLAlchemy session. :param model: The model to authorize, must be a SQLAlchemy model. """ try: mapped_class = inspect(model, raiseerr=True).class_ except AttributeError: raise TypeError(f"Expected a model; received: {model}") partial_resource = Partial("resource", TypeConstraint(polar_model_name(mapped_class))) results = oso.query_rule("allow", actor, action, partial_resource) combined_filter = None has_result = False for result in results: has_result = True resource_partial = result["bindings"]["resource"] filter = partial_to_filter(resource_partial, session, model, get_model=oso.get_class) if combined_filter is None: combined_filter = filter else: combined_filter = combined_filter | filter if not has_result: return sql.false() return combined_filter
def update_invitation( dataroom_id: UUID, org_id: UUID, invite_id: UUID, req: schemas.InviteAPIRequest, current_user: models.User = Depends(deps.get_current_active_user), db: Session = Depends(deps.get_db), auth: Oso = Depends(deps.get_oso), ): # check whether the path is correct so that dataroom belongs to org # and dataroom actually exists room = crud.dataroom.get(db, id=dataroom_id) if not room: raise HTTPException(status_code=400) if not room.organization_fk == org_id: raise HTTPException(status_code=400) # check whether the user is allowed to create invites if not auth.is_allowed(current_user, "INVITE_GUESTS", room): raise HTTPException(status_code=404, detail="Not enough privileges") invitation = crud.invite.get(db, invite_id) if not invitation: raise HTTPException(status_code=404) if not invitation.invitee.email == req.email: raise HTTPException(status_code=400) crud.room_roles.update_user_role(db, invitation.invitee, room, req.user_role) expires_in_timestamp = security.calculate_expires_in_days(req.expires_in) invitation_update_schema = schemas.InviteUpdate( expires_in=expires_in_timestamp) crud.invite.update(db, db_obj=invitation, obj_in=invitation_update_schema) return "OK"
def update_team( org_id: UUID, team_id: UUID, req: schemas.TeamCreateRequest, current_user: models.User = Depends(deps.get_current_active_user), db: Session = Depends(deps.get_db), auth: Oso = Depends(deps.get_oso), ): team = crud.team.get(db, team_id) if team is None or team.organization_fk != org_id: raise HTTPException(status_code=404) if not auth.is_allowed(current_user, "UPDATE", team): raise HTTPException(status_code=403) update_team = schemas.TeamUpdate(name=req.name, description=req.description) updated_team = crud.team.update(db, db_obj=team, obj_in=update_team) return updated_team
def test_team_dataroom_roles(db: Session, data: Data, oso: Oso): # make sure that a user that does not have direct # room roles can still manage room because of a team # role user_roles_in_room_1: List = oso_roles.get_user_roles( db, data.member_team_1, Dataroom, data.room_1.id) # user does not have direct access to the room count_of_roles = len(user_roles_in_room_1) assert count_of_roles == 0 # but still gets it via his team role (which is a members role) assert oso.is_allowed(data.member_team_1, "READ", data.room_1) is True assert oso.is_allowed(data.member_team_1, "LIST_DOCUMENTS", data.room_1) is True assert oso.is_allowed(data.member_team_1, "DELETE", data.room_1) is False assert oso.is_allowed(data.member_team_1, "UPDATE", data.room_1) is False assert oso.is_allowed(data.member_team_1, "INVITE_GUESTS", data.room_1) is False # does not have access to other rooms assert oso.is_allowed(data.member_team_1, "READ", data.room_2) is False assert oso.is_allowed(data.member_team_1, "LIST_DOCUMENTS", data.room_2) is False # is allowed to create his own documents in a room new_document_team_member_1 = Document( name="New Doc Team Member 1", description="Test", file_name="new_doc_team_member_1", extension="txt", md5_sum=UUID("87a6909ab71ec463f013325dbf9f3543"), mime_type="text/plain", size=50, creator=data.member_team_1, dataroom=data.room_1, ) assert (oso.is_allowed(data.member_team_1, "CREATE", new_document_team_member_1) is True)
def create_dataroom( org_id: UUID, dataroom_request: schemas.DataRoomCreateRequest, db: Session = Depends(deps.get_db), auth: Oso = Depends(deps.get_oso), current_user: models.User = Depends(deps.get_current_active_user), ): org = crud.org.get(db, org_id) if org is None: raise HTTPException(status_code=404) # create the room model in order to verify if user is allowed to create it create_room_schema = schemas.DataRoomCreate(creator=current_user, organization=org, name=dataroom_request.name) new_room = crud.dataroom.create_model(create_room_schema) # first find out if the user is allowed to create rooms in a given org if not auth.is_allowed(current_user, "CREATE", new_room): raise HTTPException(status_code=403, detail="Not enough privileges") new_room = crud.dataroom.add_model_to_db(db, model=new_room) return new_room
def oso_with_session(test_db_session): oso = Oso() set_get_session(oso, lambda: test_db_session) register_models(oso, Base) return oso
def oso(): return Oso()
import math import os from polar.exceptions import UnrecognizedEOF from oso import Oso, OsoException, Variable oso = Oso() # Application class with default kwargs constructor, registered with the # decorator. class A: def __init__(self, x): self.x = x def foo(self): return -1 oso.register_class(A) # Test inheritance; doesn't need to be registered. class D(A): pass # Namespaced application class (to be aliased) with custom # constructor. class B: class C: def __init__(self, y):