This commit is contained in:
Untone 2021-07-14 01:25:20 +03:00
commit 4f75f199c0
20 changed files with 837 additions and 690 deletions

View File

@ -6,12 +6,20 @@ from auth.validations import User
class Identity:
@staticmethod
def identity(user_id: int, password: str) -> User:
user = global_session.query(OrmUser).filter_by(id=user_id).first()
if not user:
raise ObjectNotExist("User does not exist")
user = User(**user.dict())
if not Password.verify(password, user.password):
raise InvalidPassword("Wrong user password")
return user
@staticmethod
def identity(user_id: int, password: str) -> User:
user = global_session.query(OrmUser).filter_by(id=user_id).first()
if not user:
raise ObjectNotExist("User does not exist")
user = User(**user.dict())
if not Password.verify(password, user.password):
raise InvalidPassword("Wrong user password")
return user
@staticmethod
def identity_oauth(oauth_id, input) -> User:
user = global_session.query(OrmUser).filter_by(oauth_id=oauth_id).first()
if not user:
user = OrmUser.create(**input)
user = User(**user.dict())
return user

68
auth/oauth.py Normal file
View File

@ -0,0 +1,68 @@
from authlib.integrations.starlette_client import OAuth
from starlette.responses import PlainTextResponse
from auth.authorize import Authorize
from auth.identity import Identity
from sensitive_settings import CLIENT_ID, CLIENT_SECRET
oauth = OAuth()
oauth.register(
name='facebook',
client_id=CLIENT_ID["FACEBOOK"],
client_secret=CLIENT_SECRET["FACEBOOK"],
access_token_url='https://graph.facebook.com/v11.0/oauth/access_token',
access_token_params=None,
authorize_url='https://www.facebook.com/v11.0/dialog/oauth',
authorize_params=None,
api_base_url='https://graph.facebook.com/',
client_kwargs={'scope': 'user:email'},
)
oauth.register(
name='github',
client_id=CLIENT_ID["GITHUB"],
client_secret=CLIENT_SECRET["GITHUB"],
access_token_url='https://github.com/login/oauth/access_token',
access_token_params=None,
authorize_url='https://github.com/login/oauth/authorize',
authorize_params=None,
api_base_url='https://api.github.com/',
client_kwargs={'scope': 'user:email'},
)
oauth.register(
name='google',
client_id=CLIENT_ID["GOOGLE"],
client_secret=CLIENT_SECRET["GOOGLE"],
access_token_url='https://oauth2.googleapis.com/token',
access_token_params=None,
authorize_url='https://accounts.google.com/o/oauth2/v2/auth',
authorize_params=None,
api_base_url='https://oauth2.googleapis.com/',
client_kwargs={'scope': 'openid email profile'}
)
async def oauth_login(request):
provider = request.path_params['provider']
request.session['provider'] = provider
client = oauth.create_client(provider)
redirect_uri = request.url_for('oauth_authorize')
return await client.authorize_redirect(request, redirect_uri)
async def oauth_authorize(request):
provider = request.session['provider']
client = oauth.create_client(provider)
token = await client.authorize_access_token(request)
resp = await client.get('user', token=token)
profile = resp.json()
oauth_id = profile["id"]
user_input = {
"oauth_id" : oauth_id,
"email" : profile["email"],
"username" : profile["name"]
}
user = Identity.identity_oauth(oauth_id=oauth_id, input=user_input)
token = await Authorize.authorize(user, device="pc", auto_delete=False)
return PlainTextResponse(token)

10
create_crt.sh Normal file
View File

@ -0,0 +1,10 @@
#!/bin/bash
openssl req -newkey rsa:4096 \
-x509 \
-sha256 \
-days 3650 \
-nodes \
-out discours.crt \
-keyout discours.key \
-subj "/C=RU/ST=Moscow/L=Moscow/O=Discours/OU=Site/CN=10.0.0.187"

15
main.py
View File

@ -5,16 +5,21 @@ from ariadne.asgi import GraphQL
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.middleware.sessions import SessionMiddleware
from starlette.routing import Route
from auth.authenticate import JWTAuthenticate
from auth.oauth import oauth_login, oauth_authorize
from redis import redis
from resolvers.base import resolvers
import_module('resolvers')
schema = make_executable_schema(load_schema_from_path("schema.graphql"), resolvers)
middleware = [Middleware(AuthenticationMiddleware, backend=JWTAuthenticate())]
middleware = [
Middleware(AuthenticationMiddleware, backend=JWTAuthenticate()),
Middleware(SessionMiddleware, secret_key="!secret")
]
async def start_up():
await redis.connect()
@ -23,6 +28,10 @@ async def start_up():
async def shutdown():
await redis.disconnect()
routes = [
Route("/oauth/{provider}", endpoint=oauth_login),
Route("/authorize", endpoint=oauth_authorize)
]
app = Starlette(debug=True, on_startup=[start_up], on_shutdown=[shutdown], middleware=middleware)
app = Starlette(debug=True, on_startup=[start_up], on_shutdown=[shutdown], middleware=middleware, routes=routes)
app.mount("/", GraphQL(schema, debug=True))

View File

@ -11,10 +11,12 @@ class User(Base):
email: str = Column(String, nullable=False)
username: str = Column(String, nullable=False, comment="Name")
password: str = Column(String, nullable=False, comment="Password")
password: str = Column(String, nullable=True, comment="Password")
role_id: int = Column(ForeignKey("role.id"), nullable=True, comment="Role")
oauth_id: str = Column(String, nullable=True)
@classmethod
def get_permission(cls, user_id):
perms: List[Permission] = cls.session.query(Permission).join(User, User.role_id == Permission.role_id).filter(

View File

@ -13,34 +13,34 @@ from settings import JWT_AUTH_HEADER
@mutation.field("registerUser")
async def register(*_, input: dict = None) -> User:
create_user = CreateUser(**input)
create_user.password = Password.encode(create_user.password)
return User.create(**create_user.dict())
create_user = CreateUser(**input)
create_user.password = Password.encode(create_user.password)
return User.create(**create_user.dict())
@query.field("signIn")
async def sign_in(_, info: GraphQLResolveInfo, id: int, password: str):
try:
device = info.context["request"].headers['device']
except KeyError:
device = "pc"
auto_delete = False if device == "mobile" else True
user = Identity.identity(user_id=id, password=password)
token = await Authorize.authorize(user, device=device, auto_delete=auto_delete)
return {"status" : True, "token" : token}
try:
device = info.context["request"].headers['device']
except KeyError:
device = "pc"
auto_delete = False if device == "mobile" else True
user = Identity.identity(user_id=id, password=password)
token = await Authorize.authorize(user, device=device, auto_delete=auto_delete)
return {"status" : True, "token" : token}
@query.field("signOut")
@login_required
async def sign_out(_, info: GraphQLResolveInfo):
token = info.context["request"].headers[JWT_AUTH_HEADER]
status = await Authorize.revoke(token)
return {"status" : status}
token = info.context["request"].headers[JWT_AUTH_HEADER]
status = await Authorize.revoke(token)
return {"status" : status}
#@query.field("getUser")
#@login_required
async def get_user(*_, id: int):
return global_session.query(User).filter(User.id == id).first()
return global_session.query(User).filter(User.id == id).first()

View File

@ -1,6 +1,15 @@
from ariadne import MutationType, QueryType
from ariadne import MutationType, QueryType, SubscriptionType, ScalarType
query = QueryType()
mutation = MutationType()
subscription = SubscriptionType()
resolvers = [query, mutation]
datetime_scalar = ScalarType("DateTime")
@datetime_scalar.serializer
def serialize_datetime(value):
return value.isoformat()
resolvers = [query, mutation, subscription, datetime_scalar]

View File

@ -1,10 +1,20 @@
from orm import Message, User
from orm.base import global_session
from resolvers.base import mutation, query
from resolvers.base import mutation, query, subscription
from auth.authenticate import login_required
import asyncio
class MessageQueue:
new_message = asyncio.Queue()
updated_message = asyncio.Queue()
deleted_message = asyncio.Queue()
@mutation.field("createMessage")
@login_required
async def create_message(_, info, input):
@ -17,6 +27,8 @@ async def create_message(_, info, input):
replyTo = input.get("replyTo")
)
MessageQueue.new_message.put_nowait(new_message)
return {
"status": True,
"message" : new_message
@ -61,6 +73,8 @@ async def update_message(_, info, input):
message.body = input["body"]
global_session.commit()
MessageQueue.updated_message.put_nowait(message)
return {
"status" : True,
"message" : message
@ -83,6 +97,33 @@ async def delete_message(_, info, id):
global_session.delete(message)
global_session.commit()
MessageQueue.deleted_message.put_nowait(message)
return {
"status" : True
}
@subscription.source("messageCreated")
async def new_message_generator(obj, info):
while True:
new_message = await MessageQueue.new_message.get()
yield new_message
@subscription.source("messageUpdated")
async def updated_message_generator(obj, info):
while True:
message = await MessageQueue.updated_message.get()
yield message
@subscription.source("messageDeleted")
async def deleted_message_generator(obj, info):
while True:
message = await MessageQueue.deleted_message.get()
yield new_message
@subscription.field("messageCreated")
@subscription.field("messageUpdated")
@subscription.field("messageDeleted")
def message_resolver(message, info):
return message

View File

@ -64,8 +64,6 @@ type Mutation {
requestEmailConfirmation: User!
requestPasswordReset(email: String!): Boolean!
resetPassword(password: String!, token: String!): Token!
signIn(email: String!, password: String!): Token!
# signUp(email: String!, password: String!, username: String): User!
registerUser(input: registerUserInput!): User!
# shout
@ -136,7 +134,9 @@ type Proposal {
type Subscription {
messageCreated: Message!
messageUpdated: Message!
messageDeleted: Message!
onlineUpdated: [User!]!
shoutUpdated: Shout!
userUpdated: User!

View File

@ -2,4 +2,4 @@ import uvicorn
from settings import PORT
if __name__ == '__main__':
uvicorn.run("main:app", host="0.0.0.0", port=PORT, reload=True)
uvicorn.run("main:app", host="0.0.0.0", port=PORT, ssl_keyfile="discours.key", ssl_certfile="discours.crt", reload=True)

View File

@ -1,6 +1,6 @@
from pathlib import Path
PORT = 24579
PORT = 8081
SQLITE_URI = Path(__file__).parent / "database.sqlite3"
JWT_ALGORITHM = "HS256"