diff --git a/api/kiwi_vpn_api/db/models.py b/api/kiwi_vpn_api/db/models.py index f9b6afa..d67f4aa 100644 --- a/api/kiwi_vpn_api/db/models.py +++ b/api/kiwi_vpn_api/db/models.py @@ -14,34 +14,6 @@ from sqlalchemy.orm import Session, relationship ORMBaseModel = declarative_base() -class User(ORMBaseModel): - __tablename__ = "users" - - name = Column(String, primary_key=True, index=True) - password = Column(String, nullable=False) - - capabilities: list[UserCapability] = relationship( - "UserCapability", lazy="joined", cascade="all, delete-orphan" - ) - certificates: list[Certificate] = relationship( - "Certificate", lazy="select", back_populates="owner" - ) - distinguished_names: list[DistinguishedName] = relationship( - "DistinguishedName", lazy="select", back_populates="owner" - ) - - @classmethod - def load(cls, db: Session, name: str) -> User | None: - """ - Load user from database by name. - """ - - return (db - .query(User) - .filter(User.name == name) - .first()) - - class UserCapability(ORMBaseModel): __tablename__ = "user_capabilities" @@ -54,53 +26,43 @@ class UserCapability(ORMBaseModel): capability = Column(String, primary_key=True) -class DistinguishedName(ORMBaseModel): - __tablename__ = "distinguished_names" +class User(ORMBaseModel): + __tablename__ = "users" - id = Column(Integer, primary_key=True, autoincrement=True) + name = Column(String, primary_key=True, index=True) + password = Column(String, nullable=False) - owner_name = Column(String, ForeignKey("users.name")) - cn_only = Column(Boolean, default=True, nullable=False) country = Column(String(2)) state = Column(String) city = Column(String) organization = Column(String) organizational_unit = Column(String) + email = Column(String) - common_name = Column(String, nullable=False) + + capabilities: list[UserCapability] = relationship( + "UserCapability", lazy="joined", cascade="all, delete-orphan" + ) + devices: list[Device] = relationship( + "Device", lazy="select", back_populates="owner" + ) + + +class Device(ORMBaseModel): + __tablename__ = "devices" + + id = Column(Integer, primary_key=True, autoincrement=True) + + owner_name = Column(String, ForeignKey("users.name")) + name = Column(String) + type = Column(String) + expiry = Column(DateTime, default=datetime.datetime.now) owner: User = relationship( "User", lazy="joined", back_populates="distinguished_names" ) UniqueConstraint( - country, - state, - city, - organization, - organizational_unit, - email, - common_name, - ) - - -class Certificate(ORMBaseModel): - __tablename__ = "certificates" - - id = Column(Integer, primary_key=True, autoincrement=True) - - owner_name = Column(String, ForeignKey("users.name")) - dn_id = Column( - Integer, - ForeignKey("distinguished_names.id"), - nullable=False, - ) - expiry = Column(DateTime, default=datetime.datetime.now) - - distinguished_name: DistinguishedName = relationship( - "DistinguishedName", lazy="joined" - ) - - owner: User = relationship( - "User", lazy="joined", back_populates="certificates" + owner_name, + name, ) diff --git a/api/kiwi_vpn_api/db/schemas.py b/api/kiwi_vpn_api/db/schemas.py index 132aab1..c20d226 100644 --- a/api/kiwi_vpn_api/db/schemas.py +++ b/api/kiwi_vpn_api/db/schemas.py @@ -79,6 +79,36 @@ class DistinguishedName(DistinguishedNameBase): # distinguished name already existed pass + def delete( + self, + db: Session, + ) -> bool: + """ + Delete this distinguished name from the database. + """ + + db_dn = models.DistinguishedName(**dict(self)) + db.refresh(db_dn) + + # .load( + # db=db, + # country=self.country, + # state=self.state, + # city=self.city, + # organization=self.organization, + # organizational_unit=self.organizational_unit, + # email=self.email, + # common_name=self.common_name, + # ) + + if db_dn is None: + # nonexistent user + return False + + db.delete(db_dn) + db.commit() + return True + ########## # table: certificates ########## diff --git a/api/kiwi_vpn_api/routers/dn.py b/api/kiwi_vpn_api/routers/dn.py index ae1eae2..bdebb9c 100644 --- a/api/kiwi_vpn_api/routers/dn.py +++ b/api/kiwi_vpn_api/routers/dn.py @@ -56,3 +56,33 @@ async def add_distinguished_name( # return the created user on success return new_dn + + +# @router.delete( +# "", +# responses={ +# status.HTTP_200_OK: Responses.OK, +# status.HTTP_400_BAD_REQUEST: Responses.NOT_INSTALLED, +# status.HTTP_401_UNAUTHORIZED: Responses.NEEDS_USER, +# status.HTTP_403_FORBIDDEN: Responses.NEEDS_ADMIN, +# status.HTTP_404_NOT_FOUND: Responses.ENTRY_DOESNT_EXIST, +# }, +# ) +# async def remove_distinguished_name( +# user_name: str, +# _: User = Depends(get_current_user_if_admin), +# db: Session | None = Depends(Connection.get), +# ): +# """ +# DELETE ./{user_name}: Remove a user from the database. +# """ + +# # get the user +# user = User.from_db( +# db=db, +# name=user_name, +# ) + +# # fail if deletion was unsuccessful +# if user is None or not user.delete(db): +# raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) diff --git a/api/plan.md b/api/plan.md new file mode 100644 index 0000000..fc15cf3 --- /dev/null +++ b/api/plan.md @@ -0,0 +1,26 @@ +## Server props +- default DN parts: country, state, city, org, OU +- "customizable" flags for DN parts +- flag: use client-to-client +- force cipher, tls-cipher, auth params +- server name +- default certification length +- default certificate algo + +## User props +- username +- password +- custom DN parts: country, state, city, org, OU +- email + +## User caps +- admin: administrator +- login: can log into the web interface +- certify: can certify own devices without approval +- renew: can renew certificates for own devices + +## Device props +- name +- type (icon) +- is active +- certified until