Skip to main content

Manipulate your databases as if you never leaved Python!

Project description

yuno (originally: saki)

An account and database management framework, completing Nasse

Manipulate your databases as if you never leaved Python

PyPI version Downloads PyPI - Downloads PyPI - Python Version PyPI - Status GitHub - License GitHub top language CodeQL Checks Badge Pytest Code Size Repo Size Issues

Getting Started

These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.

Prerequisites

Python

You will need Python 3 to use this module

# vermin output
Minimum required versions: 3.8
Incompatible versions:     2

According to Vermin (--backport typing), Python 3.8 is needed for typing.Literal

MongoDB

I haven't checked the minimum requirements for the MongoDB features used in this library yet but you should always use the latest versions for maximum security.

Also this framework has been tested on MongoDB v4.4.5 (locally), MongoDB v4.4.12 (on Atlas) and the latest version of MongoDB on the CI tests.

Always check if your Python version works with yuno before using it in production.

Installing

Option 1: From PyPI

pip install --upgrade yuno

Option 2: From Git

pip install --upgrade git+https://github.com/Animenosekai/yuno

You can check if you successfully installed it by printing out its version:

$ python -c "import yuno; print(yuno.__version__)"
# output:
yuno v1.0

Purpose

This framework aims to bring effortless database management and database schemas in Python, in order to make everything predictable and easy to debug.

You will never need to learn and search for obscure MongoDB features again because everything is made to behave like normal Python objects.

Usage

Running a MongoDB (mongod) process from Python

If you have a MongoDB database already running (for example: using MongoDB Atlas, or on a separate server), you can skip this part.

Starting a process

To start a process you will need to create a new MongoDB object with the configurations (refer to Configuration) and start it from there.

Example

from yuno import MongoDB

server = MongoDB()
server.start() # this will launch MongoDB and wait for it to run
...
server.kill() # this will kill the MongoDB process

The start method can get extra parameters that will modify the way MongoDB is launched:

  • executable: The path to the MongoDB executable.
  • wait: When you have set fork to False, this is the number of seconds to wait for the process to start. (I was planning to use the output from the process to determine when it is listening for connections but mongod doesn't seem to "flush" its output so it was just hanging waiting for new output to come until the process was killed)
  • keep_alive: Whether to keep the process alive or not (fork will be enabled) after Python quits (it will not register self.kill at cleanup).

Configuration

MongoDB configuration is made easy with Yuno!

You can just pass configuration values into MongoDB to configure it!

You can configure a lot of important values natively for now and you can add more obscure ones when launching the process.

List of supported configuration values
  • host: The host of the MongoDB server (net.bindIp)
  • port: The port of the MongoDB server (net.port)
  • db_path: The path of the MongoDB database (storage.dbPath)
  • fork: Whether to fork the MongoDB process or not (processManagement.fork)
  • log_config: The MongoDB logging configuration (systemLog)
  • max_connections: The maximum number of connections allowed (net.maxIncomingConnections)
  • json_validation: Whether to enable the JSON validation or not (net.wireObjectCheck)
  • ipv6: Whether to enable IPv6 or not (net.ipv6)
  • monitoring: Whether to enable the free MongoDB monitoring or not (cloud.monitoring.free.state)
About LogConfig

The content of the log_config parameter should be a LogConfig object (or a dictionary containing data to instantiate a LogConfig class).

This parameter is a class on its own because of its complexity.

Parameters

  • verbosity: The verbosity level of the MongoDB logging system
  • path: The path of the MongoDB log file. It can also be TERMINAL or SYSLOG (the systemLog.destination value will be set automatically according to this value)
  • append: Whether to append to the log file or not
  • timezone: The timezone of the timestamps format
  • debug: Whether to enable the debug mode or not (this will enable max verbosity and --traceExceptions)

Example

from yuno import MongoDB, LogConfig
from yuno.launcher import Timezone

server = MongoDB(db_path="./db/test", fork=True, monitoring=False, log_config=LogConfig(verbosity=2, path="./logs/db/test.log", timezone=Timezone.UTC))

server.start()

Loading and Dumping the configuration

You can also load the config from a file or dump it into a file using the built in methods (available on LogConfig and MongoDB):

  • to_cli_args will dump the configuration to a list of CLI arguments to give to the MongoDB (mongod) process
  • loads will load the configuration from a YAML string or a dictionary of values
  • load will load the configuration from a YAML file
  • dumps will dump the configuration to a YAML string
  • dump will dump the configuration to a YAML file
  • to_dict (or dict(configuration_object)) will dump the configuration to a dictionary (you have a camelCase parameter to make the keys camelCased)

This is especially useful when you already have a configuration file or want to share the configuration to another user not using Yuno.

Example

from yuno import MongoDB

server = MongoDB() # will load all of the default configurations
server.load("./db/mongo.conf") # will reinitialize the object with the config file, keeping the default values for values that aren't specified
server.dump("./db/mongo_new.conf") # will dump the configurations to a new file (to share or use later)

# will show the configuration
print("MongoDB configuration")
print("-" * 10)
for key, val in server.to_dict(camelCase=True):
    print(key, "=", val)

Connecting to a MongoDB process

Wether you use the built-in MongoDB object to launch a MongoDB process or just host it on the cloud/on another server, you will need to connect to it using YunoClient.

Here are the parameters used to connect to a server:

  • host: The host or list of hostnames to connect to. You can use host to pass in a URI string or a MongoDB object (in which case you won't need to use port).
  • port: The port to connect to.
  • tz_aware: Whether to use timezone aware datetimes or not.
  • connect: Whether to connect before making any operation to the server or not.
  • kwargs: Options to pass to the PyMongo client. (you don't need to set kwargs manually, just pass the parameters as normal parameters)

Example

# Using the "MongoDB" object
from yuno import MongoDB
from yuno import YunoClient

server = MongoDB()
server.start()
client = YunoClient(server) # you are connecting to the server here

Using the client

To access databases from the client, all you need to do is access its name as an attribute or an item:

client.database_name
# or
client["database_name"]

In both case, this will return the same YunoDatabase object.

The client["database_name"] syntax is especially useful if you use a database with the name of a method overwritten by YunoClient (for example watch, server_info, address, close, etc.).

You can find a list of overwritten attributes under the __overwritten__ attribute.

YunoClient can be used to establish the database schema.

This basically means that you can already type hint databases by creating your own clients.

from yuno import YunoClient
from yuno import YunoDatabase

class MyClient(YunoClient):
    production: YunoDatabase # you can here use your custom databases (refer to the "Using databases" section)
    scores: YunoDatabase

client = MyClient("mongodb+srv://anise:password@yunotest.host.mongodb.net/production")
client.production # will return the `production` database
# YunoDatabase('production')

This helps with establishing a schema and helps your code editor guide you when writing code, resulting in less time searching for types, available databases and looking back at your code.

It has serveral other methods, which are picked and adapted from the original PyMongo MongoClient.

Examples

  • close is used to close the connection to the server
  • database_names is used to retrieve the list of databases created.
  • server_info will return some info about the current MongoDB server in a special object called BuildInfo (yuno.client.BuildInfo).
  • watch returns a cursor to watch the cluster for changes and events.
  • on will register a callback function which will be called when the specified operation/event is performed on the server.

You can also use pythonic syntax to make some operations:

  • Dropping a database
del client.test
# or
del client["test"]
# will drop (delete) the "test" database

Using databases

A "database" is what holds collections in MongoDB.

To access collections from the client, all you need to do is access its name as an attribute or an item:

database.collection_name
# or
database["collection_name"]

In both case, this will return the same YunoCollection object.

The database["collection_name"] syntax is especially useful if you use a database with the name of a method overwritten by YunoDatabase (for example watch, on, command, aggregate, etc.).

You can find a list of overwritten attributes under the __overwritten__ attribute.

YunoCollection is the class returned by YunoDatabase when querying for one.

# using the client variable defined before

accounts = production_database.accounts
# this is YunoCollection object

You can define your own databases to type hint them (for the same reasons as before)

from yuno import YunoClient, YunoDatabase, YunoCollection

class MyDatabase(YunoDatabase):
    accounts: YunoCollection # you will also be able to create your own collections

class MyClient(YunoClient):
    production: MyDatabase

client = MyClient("mongodb+srv://anise:password@yunotest.host.mongodb.net/production")
production_database = client.production # will return the custom MyDatabase object and code editors will help you write code
# MyDatabase('production')
production_database.accounts # this is a YunoCollection object

It has serveral other methods, which are picked and adapted from the original PyMongo MongoClient.

Examples

  • aggregate is used to perform an aggregation on the database
  • create_collection is used to create a new collection
  • list_collection_names returns a list of collections available
  • watch returns a cursor to watch the database for changes and events.
  • on will register a callback function which will be called when the specified operation/event is performed on the database.

You can also use pythonic syntax to make some operations:

  • Dropping a collection
del database.account
# or
del database["account"]
# will drop (delete) the "account" collection

Using collections

A "collection" is what holds documents in MongoDB.

To access documents from the collection, all you need to do is access its name as an attribute or an item:

collection.document_id
# or
collection["document_id"]

In both case, this will return the same YunoCollection object.

The collection["document_id"] syntax is especially useful if you use a document with the _id of a method overwritten by YunoCollection (for example watch, on, find, aggregate, etc.).

You can find a list of overwritten attributes under the __overwritten__ attribute.

YunoCollection is the class returned by YunoDatabase when querying for one.

# using the client variable defined before

accounts = production_database.accounts
# this is YunoCollection object

You can define your own collections to type hint them (for the same reasons as before)

from bson import ObjectId
from yuno import YunoClient, YunoDatabase, YunoCollection, YunoDict

class MyCollection(YunoCollection):
    special_document: YunoDict # you will also be able to create your own documents

class MyDatabase(YunoDatabase):
    accounts: MyCollection

class MyClient(YunoClient):
    production: MyDatabase

client = MyClient("mongodb+srv://anise:password@yunotest.host.mongodb.net/production")
production_database = client.production
accounts = production_database.accounts
accounts.special_document # this is a YunoDict object

There is a special __type__ attribute which is used to define the default type of the documents in the collection.

This is especially useful if all of the documents in the collection share the same schema.

You can use type hints to define the schema of special documents (a document which gives global information about the collection for example).

It has serveral other methods, which are picked and adapted from the original PyMongo MongoClient.

Examples

  • count is used to count the number of documents matching the given filter
  • find is used to find documents matching the given filter
  • index creates a new index on the collection
  • aggregate returns an aggregation of documents following the given pipeline
  • watch returns a cursor to watch the database for changes and events.
  • on will register a callback function which will be called when the specified operation/event is performed on the database.

You can also use pythonic syntax to make some operations:

  • Deleting a document
del collection.special_document
# or
del collection["special_document"]
# will delete the "special_document" document
  • Set a document (create or replace)
collection.special_document = {"_id": "special_document", "name": "Special document"}
# or
collection["special_document"] = {"_id": "special_document", "name": "Special document"}
# will replace the "special_document" document with the one defined above
  • Verify if the given document exists
"special_document" in collection
# will return True if the document (with _id == "special_document") exists

Using objects

An "object" is what represents any object in a document, or even the document itself.

To access objects from another object (the highest hierarchy parent object being the document), all you need to do is access its name as an attribute or an item:

document.object_name
# or
document["object_name"]

In both case, this will return the same YunoObject object.

The document["object_name"] syntax is especially useful if you use an object with its name being of a method overwritten by YunoObject (for example watch, on, reload, delete, etc.).

You can find a list of overwritten attributes under the __overwritten__ attribute.

YunoObject is the class returned by YunoCollection when querying for one.

# using the client variable defined before

document = accounts.special_document
# this is YunoObject object

You can define your own objects to type hint them (for the same reasons as before)

from yuno import YunoClient, YunoDatabase, YunoCollection, YunoDict



class CustomObject(YunoDict):
    hello: str = "world"
    do_not_exist: str = "this does not exist"  # its default value if not found in db


class CustomDocument(YunoDict):
    __lazy__ = ["hello"]  # lazy loaded attribute

    hello: str
    world: str = "heyhey"
    a: CustomObject  # nested object ^^


class SpecialDocument(YunoDict):
    __lazy__ = ["specialAttributes"]

    specialAttributes: list[str]
    version: str


class CustomCollection(YunoCollection):
    __type__ = CustomDocument  # the default type of document in the collection

    special: SpecialDocument  # a special document type


class CustomDatabase(YunoDatabase):
    __yuno_test__: CustomCollection


class CustomClient(YunoClient):
    test_database: CustomDatabase


client = CustomClient("mongodb+srv://anise:password@yunotest.host.mongodb.net/production")
test_database = client.test_database
test_collection = test_database.__yuno_test__
special_doc = test_collection.special
special_doc # this is a SpecialDocument object
document = test_collection.any_document
document # this is a CustomDocument object
document.hello # this is a str object, but lazy loaded (not loaded until needed)
document.a # this is CustomObject object
document.a.hello # this is a str
document.a.do_not_exist # this is a str (and if it's not found in the db, it will be the value given by default "this does not exist")

There is a special __lazy__ attribute which is used to define attributes which won't be loaded until needed.

This is especially useful for attributes which are expensive to load or not needed in normal circumstances.

You can use type hints to define the schema of some attributes.

Objects acts as regular python objects.

For example, a YunoDict object can be used as a regular python dict:

object # this is a YunoDict object
object.get("key") # this is a str
object.pop("key") # remove the key from the object
object.items() # this is a list of tuples
object.keys() # this is a list of str
...
for key, value in object.items():
    print(key, value)
del object["key"]

and a YunoList object can be used as a regular python list:

object # this is a YunoList object
object.append("value") # add a value to the list
object.pop() # remove the last value from the list
object.extend(["value1", "value2"]) # add multiple values to the list
object.index("value") # return the index of the value in the list
object.insert(0, "value") # insert a value at the beginning of the list
object.remove("value") # remove the first value from the list
object.reverse() # reverse the list
object.sort() # sort the list
...
for value in object:
    print(value)

Some methods don't come from regular Python data types, but are specific to YunoObjects:

Examples

  • delete deletes the current object from the database
  • reload reloads the current object from the database (replaces the current object with the one from the database)
  • watch returns a cursor to watch the database for changes and events.
  • on will register a callback function which will be called when the specified operation/event is performed on the object.

Instead of the reload method, you also have a __realtime__ attribute which you can set to True if you want the object to follow the updates on the database (you will have an object which is always up to date).

document # this is a YunoObject (YunoDict, YunoList, etc.) object
document.__realtime__ = True
# this will make the object follow the updates on the database

class CustomObject(YunoDict):
    __realtime__ = True

    hello: str = "world"

class CustomCollection(YunoCollection):
    __type__ = CustomObject

# any object coming from the CustomCollection collection will be a CustomObject object and will be a "realtime" object, following the updates on the database

You can also use pythonic syntax to make some operations:

  • Deleting a document
del object.key
# or
del object["key"]
# will delete the "key" attribute from the object
  • Set an attribute (create or replace)
object.key = {"hello": "world"}
# or
object["key"] = {"hello": "world"}
# will replace the "key" attribute with the one defined above
  • Verify if the given document exists
"key" in object
# will return True if the attribute exists

Security package

Yuno being a framework, it also provides a set of tools to help you secure your data and logins for your users.

They are all located under yuno.security.

Encrypt

Encrypt is a module to encrypt your data using AES.

The key can be managed by the database and the token is made recognizable by the given prefix.

Usage

Encrypting
>>> from yuno.security.encrypt import AES
>>> encryption = AES(<your key>)
>>> encryption.encrypt("hello world")
'yuno+312e302e30,1f1d5944d37904e6fe0d129efc11207c,cbb99af1cfb54e08b31d4e,f4876580de57a521791bc0cc527c85cc'

You can also use your database to automatically create and store the key.

>>> client = YunoClient(...)
>>> encryption = AES(key=client)
# the key will be randomly generated and stored in the database to be retrieved automatically when needed
>>> encryption.encrypt("hello world")
'yuno+312e302e30,1f1d5944d37904e6fe0d129efc11207c,cbb99af1cfb54e08b31d4e,f4876580de57a521791bc0cc527c85cc'
Decrypting
>>> encryption.decrypt("yuno+312e302e30,1f1d5944d37904e6fe0d129efc11207c,cbb99af1cfb54e08b31d4e,f4876580de57a521791bc0cc527c85cc")
'hello world'

Encryption Schema

Yuno AES Encryption Schema

Hash

You can also hash data and passwords using Yuno

This is all located inside yuno.security.hash.

Data

The data hasher will use SHA-256 to hash your data.

Create a Hasher instance.

Four methods are available:

  • hash: Which will automatically detect which method to use.
  • hash_string: To hash a string
  • hash_bytes: To hash a bytes object
  • hash_buffer: To hash an IO buffer (file for example).

Password

Yuno uses Argon2id, one of the strongest password hashing algorithms, to hash passwords.

You can use it by creating an instance of PasswordHasher.

When initializing the PasswordHasher, you will need to pass a pepper, a kind of system key, to alter the given passwords and make them unique to the server.

The pepper parameter can also be a database object, in which case a random key will be generated and stored inside the database.

Usage

Hashing
>>> from yuno.security.hash import PasswordHasher
>>> hasher = PasswordHasher(pepper="my_pepper")
>>> hasher.hash("my_password")
'$argon2id$v=19$m=102400,t=2,p=8$YKj47J7lty47hXrsgIwq0A$XWtTO+CtuSvEWrVph9ZrNQ'

The hashed password follows the following schema:

 PEPPER + PASSWORD + SALT(*)

(*) If provided when hashing, it's a per-password bias to make the password unique to an account for example.

hasher.hash("my_password", salt="my_salt")

the SALT can be for example an account ID

Verifying

Two methods are provided to check if the given password is correct:

>>> hasher.verify(password="my_password", hashed="$argon2id$v=19$m=102400,t=2,p=8$YKj47J7lty47hXrsgIwq0A$XWtTO+CtuSvEWrVph9ZrNQ", salt="my_salt")
'$argon2id$v=19$m=102400,t=2,p=8$YKj47J7lty47hXrsgIwq0A$XWtTO+CtuSvEWrVph9ZrNQ'

This function checks if the given password matches the given hashed password and returns the hashed password if it matches.

This returned password can also be slightly different from the original hash because it will sometimes rehash it so make sure to update the hash password with the new one in the database.

>>> hasher.is_equal(password="my_password", hashed="$argon2id$v=19$m=102400,t=2,p=8$YKj47J7lty47hXrsgIwq0A$XWtTO+CtuSvEWrVph9ZrNQ", salt="my_salt")
True

Does basically the same thing as before but will do no verification, rehashing, whatsoever and will return True or False instead of raising an exception if the password don't match.

Tokens

Finally, Yuno can also manage JWT Tokens.

A JWT is basically a JSON object encoded which is base64 encoded and signed with a secret key.

This way, anyone can read its content but nobody can modify it without the key.

This is why Yuno also gives you the option to pass a yuno.security.encrypt.AES instance to encrypt the tokens with AES and make them unreadable to anyone without the key.

Usage

When initializing the TokenManager, you will need to pass a key which is the secret key used to sign the tokens.

This can also be a database object, in which case the key will be generated and stored inside the database.

You can also provide a sign parameter, which will further sign the token from within the its content by adding to each token a random signature.

Generating
>>> from yuno.security.token import TokenManager
>>> token_manager = TokenManager(key="my_secret_key")
>>> token_manager.generate(user="id-123", username="username-123", roles=["admin", "user"]) # and any extra data
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2NDYwNDEwNDcsImV4cCI6MTY0NjEyNzQ0NywidXNlciI6ImlkLTEyMyIsInVzZXJuYW1lIjoidXNlcm5hbWUtMTIzIiwicm9sZXMiOlsiYWRtaW4iLCJ1c2VyIl19.2ddAuZU7ozW9_F2dz9JKZbxdsNZlFbkjm5WYiuJVuUo'

with encryption

>>> from yuno.security.encrypt import AES
>>> aes = AES()
>>> token_manager.generate(user="id-123", encryption=aes)
'yuno+312e302e30,667dfc91d461177e673582969809d4a8,fdf3e9a34532ca63389a15bcae8d7c0373221d12e617b56a9136b1d9288f1f2da3020390c5657451357ab73607fc2040a87f4194b1abd84e8b6e9767e2ffc04ae5f70e2869c60138f8a0922f2e2f9b31f6e0913bb6b2b3e7e7b6679e55c865a4a9598016fc713e6eb48353cc61bc9954c897794ff4f29f50e0d2282a930e26b7ee7bb547428bbedef74e88a4f827eeab5564c883b7,82beee1f51fc1c1de408cc4a49c15f13'
Decoding
>>> token_manager.decode("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2NDYwNDEwNDcsImV4cCI6MTY0NjEyNzQ0NywidXNlciI6ImlkLTEyMyIsInVzZXJuYW1lIjoidXNlcm5hbWUtMTIzIiwicm9sZXMiOlsiYWRtaW4iLCJ1c2VyIl19.2ddAuZU7ozW9_F2dz9JKZbxdsNZlFbkjm5WYiuJVuUo")
{'iat': 1646037825, 'exp': 1646124194, 'user': 'id-123', 'username': 'username-123', 'roles': ['admin', 'user']}

with encryption

>>> token_manager.decode("yuno+312e302e30,667dfc91d461177e673582969809d4a8,fdf3e9a34532ca63389a15bcae8d7c0373221d12e617b56a9136b1d9288f1f2da3020390c5657451357ab73607fc2040a87f4194b1abd84e8b6e9767e2ffc04ae5f70e2869c60138f8a0922f2e2f9b31f6e0913bb6b2b3e7e7b6679e55c865a4a9598016fc713e6eb48353cc61bc9954c897794ff4f29f50e0d2282a930e26b7ee7bb547428bbedef74e88a4f827eeab5564c883b7,82beee1f51fc1c1de408cc4a49c15f13")
{'iat': 1646041175, 'exp': 1646127575, 'user': 'id-123'}

When using the sign parameter, an extra signature will be added to the token.

>>> token_manager = TokenManager("my_key", sign="my_sign")
>>> token_manager.generate(sub="id-123", username="username-123", roles=["admin", "user"])
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2NDYwNDMwOTgsImV4cCI6MTY0NjEyOTQ5OCwidXNlciI6ImlkLTEyMyIsInJhbmQiOiI1N2IyZmVhODNlNjE1M2ZjIiwic2lnbiI6Ijg5MjQyZTdlOWNhMzAwMGU4M2NiZWUxMTQxZWQ4MTFhNGQ3M2NkNTliYTdkODE5ZjVkOTg2YzEzYzg5OGNjMzIiLCJ1c2VybmFtZSI6InVzZXJuYW1lLTEyMyIsInJvbGVzIjpbImFkbWluIiwidXNlciJdfQ.t0bL6_cqpZOeY2oJoBURSMhvagjRT3b_KlCBTFjFduI'
>>> token_manager.decode('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2NDYwNDMwOTgsImV4cCI6MTY0NjEyOTQ5OCwidXNlciI6ImlkLTEyMyIsInJhbmQiOiI1N2IyZmVhODNlNjE1M2ZjIiwic2lnbiI6Ijg5MjQyZTdlOWNhMzAwMGU4M2NiZWUxMTQxZWQ4MTFhNGQ3M2NkNTliYTdkODE5ZjVkOTg2YzEzYzg5OGNjMzIiLCJ1c2VybmFtZSI6InVzZXJuYW1lLTEyMyIsInJvbGVzIjpbImFkbWluIiwidXNlciJdfQ.t0bL6_cqpZOeY2oJoBURSMhvagjRT3b_KlCBTFjFduI')
{'iat': 1646043098, 'exp': 1646129498, 'sub': 'id-123', 'rand': '57b2fea83e6153fc', 'sign': '89242e7e9ca3000e83cbee1141ed811a4d73cd59ba7d819f5d986c13c898cc32', 'username': 'username-123', 'roles': ['admin', 'user']}

Notice that a rand and sign fields are added to the token.

The rand field is random data as hex generated to be used in the sign field.

The sign field is the signature of the token.

It is a SHA-256 hashed concatenation of the rand field and the sign provided to the token manager.

Considerations

The goal here is to increase the number of keys and biases to make cracking the tokens and hashes way harder to any attacker.

Yuno Security Layers

As you can see, the JWT has the most layers because it is exposed to the public.

You should protect your databases and APIs and try to use some key rotation mechanism for maximum security.

How it works

Yuno works on top of PyMongo to make all of the operations to MongoDB.

It also uses PyJWT for the JWT encoding and decoding, Argon2 for the hashing and encryption of the passwords, and PyCryptodome for the AES encryption.

Deployment

This module is currently in development and might contain bugs.

Feel free to use it in production if you feel like it is suitable for your production even if you may encounter issues.

Contributing

Pull requests are welcome. For major changes, please open a discussion first to discuss what you would like to change.

Please make sure to update the tests as appropriate.

Built With

  • PyMongo - To connect to MongoDB databases and make operations
  • psutil - For cross-platform process management
  • PyYAML - To parse YAML files (MongoDB configuration files)
  • argon2-cffi - To hash and encrypt passwords with Argon2id
  • PyJWT - To encode and decode JWT tokens
  • PyCryptodome - To AES encrypt and decrypt data

Authors

License

This project is licensed under the MIT License - see the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

yuno-1.1.tar.gz (59.0 kB view details)

Uploaded Source

Built Distribution

yuno-1.1-py3-none-any.whl (52.6 kB view details)

Uploaded Python 3

File details

Details for the file yuno-1.1.tar.gz.

File metadata

  • Download URL: yuno-1.1.tar.gz
  • Upload date:
  • Size: 59.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/32.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.63.0 importlib-metadata/4.11.2 keyring/23.5.0 rfc3986/2.0.0 colorama/0.4.4 CPython/3.9.10

File hashes

Hashes for yuno-1.1.tar.gz
Algorithm Hash digest
SHA256 a3ad99e71aeec5ec1d7e4bbd93112b32a048d5d0309428198d2ee7b7b457056c
MD5 e323410f03a0435b3c8c0240dba1d278
BLAKE2b-256 e77f0124c822a373b810f1a6ad5a1dc45ef576fabbb6980a3f023588497a6ba0

See more details on using hashes here.

File details

Details for the file yuno-1.1-py3-none-any.whl.

File metadata

  • Download URL: yuno-1.1-py3-none-any.whl
  • Upload date:
  • Size: 52.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/32.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.63.0 importlib-metadata/4.11.2 keyring/23.5.0 rfc3986/2.0.0 colorama/0.4.4 CPython/3.9.10

File hashes

Hashes for yuno-1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 035910fcc939763fef5a1e6b4878f432059e9a7a14ea470e054bcfaf972d7ff1
MD5 890d2346a15dc43bf85c0df8c60167ce
BLAKE2b-256 f7891fe9fba028f401cc57dd810aaeb780532a6770227c3561327fd0b4425b74

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page