feat(grpc): add auth service transport
This commit is contained in:
63
proto/gapido_auth/generated/auth.proto
Normal file
63
proto/gapido_auth/generated/auth.proto
Normal file
@@ -0,0 +1,63 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package gapido.auth.v1;
|
||||
|
||||
service AuthService {
|
||||
rpc RequestOtp (RequestOtpRequest) returns (RequestOtpResponse);
|
||||
rpc VerifyOtp (VerifyOtpRequest) returns (TokenResponse);
|
||||
rpc RefreshToken (RefreshTokenRequest) returns (TokenResponse);
|
||||
rpc RevokeRefreshToken (RevokeRefreshTokenRequest) returns (RevokeRefreshTokenResponse);
|
||||
rpc PublicPing (PingRequest) returns (PingResponse);
|
||||
rpc UserOnly (ProtectedRequest) returns (ProtectedResponse);
|
||||
rpc AdminOnly (ProtectedRequest) returns (ProtectedResponse);
|
||||
}
|
||||
|
||||
message RequestOtpRequest {
|
||||
string mobile = 1;
|
||||
string purpose = 2;
|
||||
}
|
||||
|
||||
message RequestOtpResponse {
|
||||
bool accepted = 1;
|
||||
}
|
||||
|
||||
message VerifyOtpRequest {
|
||||
string mobile = 1;
|
||||
string code = 2;
|
||||
string purpose = 3;
|
||||
}
|
||||
|
||||
message RefreshTokenRequest {
|
||||
string refresh_token = 1;
|
||||
}
|
||||
|
||||
message RevokeRefreshTokenRequest {
|
||||
string refresh_token = 1;
|
||||
}
|
||||
|
||||
message RevokeRefreshTokenResponse {
|
||||
bool revoked = 1;
|
||||
}
|
||||
|
||||
message TokenResponse {
|
||||
string access_token = 1;
|
||||
string refresh_token = 2;
|
||||
string token_type = 3;
|
||||
int32 expires_in = 4;
|
||||
string role = 5;
|
||||
}
|
||||
|
||||
message PingRequest {}
|
||||
|
||||
message PingResponse {
|
||||
string message = 1;
|
||||
}
|
||||
|
||||
message ProtectedRequest {}
|
||||
|
||||
message ProtectedResponse {
|
||||
string user_id = 1;
|
||||
string role = 2;
|
||||
string message = 3;
|
||||
}
|
||||
|
||||
2
src/gapido_auth/generated/__init__.py
Normal file
2
src/gapido_auth/generated/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
"""Generated protobuf modules are written here by gapido_auth.tools.generate_proto."""
|
||||
|
||||
58
src/gapido_auth/generated/auth_pb2.py
Normal file
58
src/gapido_auth/generated/auth_pb2.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# NO CHECKED-IN PROTOBUF GENCODE
|
||||
# source: gapido_auth/generated/auth.proto
|
||||
# Protobuf Python Version: 5.28.1
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import runtime_version as _runtime_version
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf.internal import builder as _builder
|
||||
_runtime_version.ValidateProtobufRuntimeVersion(
|
||||
_runtime_version.Domain.PUBLIC,
|
||||
5,
|
||||
28,
|
||||
1,
|
||||
'',
|
||||
'gapido_auth/generated/auth.proto'
|
||||
)
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n gapido_auth/generated/auth.proto\x12\x0egapido.auth.v1\"4\n\x11RequestOtpRequest\x12\x0e\n\x06mobile\x18\x01 \x01(\t\x12\x0f\n\x07purpose\x18\x02 \x01(\t\"&\n\x12RequestOtpResponse\x12\x10\n\x08\x61\x63\x63\x65pted\x18\x01 \x01(\x08\"A\n\x10VerifyOtpRequest\x12\x0e\n\x06mobile\x18\x01 \x01(\t\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\x12\x0f\n\x07purpose\x18\x03 \x01(\t\",\n\x13RefreshTokenRequest\x12\x15\n\rrefresh_token\x18\x01 \x01(\t\"2\n\x19RevokeRefreshTokenRequest\x12\x15\n\rrefresh_token\x18\x01 \x01(\t\"-\n\x1aRevokeRefreshTokenResponse\x12\x0f\n\x07revoked\x18\x01 \x01(\x08\"r\n\rTokenResponse\x12\x14\n\x0c\x61\x63\x63\x65ss_token\x18\x01 \x01(\t\x12\x15\n\rrefresh_token\x18\x02 \x01(\t\x12\x12\n\ntoken_type\x18\x03 \x01(\t\x12\x12\n\nexpires_in\x18\x04 \x01(\x05\x12\x0c\n\x04role\x18\x05 \x01(\t\"\r\n\x0bPingRequest\"\x1f\n\x0cPingResponse\x12\x0f\n\x07message\x18\x01 \x01(\t\"\x12\n\x10ProtectedRequest\"C\n\x11ProtectedResponse\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x0c\n\x04role\x18\x02 \x01(\t\x12\x0f\n\x07message\x18\x03 \x01(\t2\xdd\x04\n\x0b\x41uthService\x12S\n\nRequestOtp\x12!.gapido.auth.v1.RequestOtpRequest\x1a\".gapido.auth.v1.RequestOtpResponse\x12L\n\tVerifyOtp\x12 .gapido.auth.v1.VerifyOtpRequest\x1a\x1d.gapido.auth.v1.TokenResponse\x12R\n\x0cRefreshToken\x12#.gapido.auth.v1.RefreshTokenRequest\x1a\x1d.gapido.auth.v1.TokenResponse\x12k\n\x12RevokeRefreshToken\x12).gapido.auth.v1.RevokeRefreshTokenRequest\x1a*.gapido.auth.v1.RevokeRefreshTokenResponse\x12G\n\nPublicPing\x12\x1b.gapido.auth.v1.PingRequest\x1a\x1c.gapido.auth.v1.PingResponse\x12O\n\x08UserOnly\x12 .gapido.auth.v1.ProtectedRequest\x1a!.gapido.auth.v1.ProtectedResponse\x12P\n\tAdminOnly\x12 .gapido.auth.v1.ProtectedRequest\x1a!.gapido.auth.v1.ProtectedResponseb\x06proto3')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'gapido_auth.generated.auth_pb2', _globals)
|
||||
if not _descriptor._USE_C_DESCRIPTORS:
|
||||
DESCRIPTOR._loaded_options = None
|
||||
_globals['_REQUESTOTPREQUEST']._serialized_start=52
|
||||
_globals['_REQUESTOTPREQUEST']._serialized_end=104
|
||||
_globals['_REQUESTOTPRESPONSE']._serialized_start=106
|
||||
_globals['_REQUESTOTPRESPONSE']._serialized_end=144
|
||||
_globals['_VERIFYOTPREQUEST']._serialized_start=146
|
||||
_globals['_VERIFYOTPREQUEST']._serialized_end=211
|
||||
_globals['_REFRESHTOKENREQUEST']._serialized_start=213
|
||||
_globals['_REFRESHTOKENREQUEST']._serialized_end=257
|
||||
_globals['_REVOKEREFRESHTOKENREQUEST']._serialized_start=259
|
||||
_globals['_REVOKEREFRESHTOKENREQUEST']._serialized_end=309
|
||||
_globals['_REVOKEREFRESHTOKENRESPONSE']._serialized_start=311
|
||||
_globals['_REVOKEREFRESHTOKENRESPONSE']._serialized_end=356
|
||||
_globals['_TOKENRESPONSE']._serialized_start=358
|
||||
_globals['_TOKENRESPONSE']._serialized_end=472
|
||||
_globals['_PINGREQUEST']._serialized_start=474
|
||||
_globals['_PINGREQUEST']._serialized_end=487
|
||||
_globals['_PINGRESPONSE']._serialized_start=489
|
||||
_globals['_PINGRESPONSE']._serialized_end=520
|
||||
_globals['_PROTECTEDREQUEST']._serialized_start=522
|
||||
_globals['_PROTECTEDREQUEST']._serialized_end=540
|
||||
_globals['_PROTECTEDRESPONSE']._serialized_start=542
|
||||
_globals['_PROTECTEDRESPONSE']._serialized_end=609
|
||||
_globals['_AUTHSERVICE']._serialized_start=612
|
||||
_globals['_AUTHSERVICE']._serialized_end=1217
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
355
src/gapido_auth/generated/auth_pb2_grpc.py
Normal file
355
src/gapido_auth/generated/auth_pb2_grpc.py
Normal file
@@ -0,0 +1,355 @@
|
||||
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
||||
"""Client and server classes corresponding to protobuf-defined services."""
|
||||
import grpc
|
||||
import warnings
|
||||
|
||||
from gapido_auth.generated import auth_pb2 as gapido__auth_dot_generated_dot_auth__pb2
|
||||
|
||||
GRPC_GENERATED_VERSION = '1.68.1'
|
||||
GRPC_VERSION = grpc.__version__
|
||||
_version_not_supported = False
|
||||
|
||||
try:
|
||||
from grpc._utilities import first_version_is_lower
|
||||
_version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
|
||||
except ImportError:
|
||||
_version_not_supported = True
|
||||
|
||||
if _version_not_supported:
|
||||
raise RuntimeError(
|
||||
f'The grpc package installed is at version {GRPC_VERSION},'
|
||||
+ f' but the generated code in gapido_auth/generated/auth_pb2_grpc.py depends on'
|
||||
+ f' grpcio>={GRPC_GENERATED_VERSION}.'
|
||||
+ f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
|
||||
+ f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
|
||||
)
|
||||
|
||||
|
||||
class AuthServiceStub(object):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
|
||||
def __init__(self, channel):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
channel: A grpc.Channel.
|
||||
"""
|
||||
self.RequestOtp = channel.unary_unary(
|
||||
'/gapido.auth.v1.AuthService/RequestOtp',
|
||||
request_serializer=gapido__auth_dot_generated_dot_auth__pb2.RequestOtpRequest.SerializeToString,
|
||||
response_deserializer=gapido__auth_dot_generated_dot_auth__pb2.RequestOtpResponse.FromString,
|
||||
_registered_method=True)
|
||||
self.VerifyOtp = channel.unary_unary(
|
||||
'/gapido.auth.v1.AuthService/VerifyOtp',
|
||||
request_serializer=gapido__auth_dot_generated_dot_auth__pb2.VerifyOtpRequest.SerializeToString,
|
||||
response_deserializer=gapido__auth_dot_generated_dot_auth__pb2.TokenResponse.FromString,
|
||||
_registered_method=True)
|
||||
self.RefreshToken = channel.unary_unary(
|
||||
'/gapido.auth.v1.AuthService/RefreshToken',
|
||||
request_serializer=gapido__auth_dot_generated_dot_auth__pb2.RefreshTokenRequest.SerializeToString,
|
||||
response_deserializer=gapido__auth_dot_generated_dot_auth__pb2.TokenResponse.FromString,
|
||||
_registered_method=True)
|
||||
self.RevokeRefreshToken = channel.unary_unary(
|
||||
'/gapido.auth.v1.AuthService/RevokeRefreshToken',
|
||||
request_serializer=gapido__auth_dot_generated_dot_auth__pb2.RevokeRefreshTokenRequest.SerializeToString,
|
||||
response_deserializer=gapido__auth_dot_generated_dot_auth__pb2.RevokeRefreshTokenResponse.FromString,
|
||||
_registered_method=True)
|
||||
self.PublicPing = channel.unary_unary(
|
||||
'/gapido.auth.v1.AuthService/PublicPing',
|
||||
request_serializer=gapido__auth_dot_generated_dot_auth__pb2.PingRequest.SerializeToString,
|
||||
response_deserializer=gapido__auth_dot_generated_dot_auth__pb2.PingResponse.FromString,
|
||||
_registered_method=True)
|
||||
self.UserOnly = channel.unary_unary(
|
||||
'/gapido.auth.v1.AuthService/UserOnly',
|
||||
request_serializer=gapido__auth_dot_generated_dot_auth__pb2.ProtectedRequest.SerializeToString,
|
||||
response_deserializer=gapido__auth_dot_generated_dot_auth__pb2.ProtectedResponse.FromString,
|
||||
_registered_method=True)
|
||||
self.AdminOnly = channel.unary_unary(
|
||||
'/gapido.auth.v1.AuthService/AdminOnly',
|
||||
request_serializer=gapido__auth_dot_generated_dot_auth__pb2.ProtectedRequest.SerializeToString,
|
||||
response_deserializer=gapido__auth_dot_generated_dot_auth__pb2.ProtectedResponse.FromString,
|
||||
_registered_method=True)
|
||||
|
||||
|
||||
class AuthServiceServicer(object):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
|
||||
def RequestOtp(self, request, context):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def VerifyOtp(self, request, context):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def RefreshToken(self, request, context):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def RevokeRefreshToken(self, request, context):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def PublicPing(self, request, context):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def UserOnly(self, request, context):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def AdminOnly(self, request, context):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
|
||||
def add_AuthServiceServicer_to_server(servicer, server):
|
||||
rpc_method_handlers = {
|
||||
'RequestOtp': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.RequestOtp,
|
||||
request_deserializer=gapido__auth_dot_generated_dot_auth__pb2.RequestOtpRequest.FromString,
|
||||
response_serializer=gapido__auth_dot_generated_dot_auth__pb2.RequestOtpResponse.SerializeToString,
|
||||
),
|
||||
'VerifyOtp': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.VerifyOtp,
|
||||
request_deserializer=gapido__auth_dot_generated_dot_auth__pb2.VerifyOtpRequest.FromString,
|
||||
response_serializer=gapido__auth_dot_generated_dot_auth__pb2.TokenResponse.SerializeToString,
|
||||
),
|
||||
'RefreshToken': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.RefreshToken,
|
||||
request_deserializer=gapido__auth_dot_generated_dot_auth__pb2.RefreshTokenRequest.FromString,
|
||||
response_serializer=gapido__auth_dot_generated_dot_auth__pb2.TokenResponse.SerializeToString,
|
||||
),
|
||||
'RevokeRefreshToken': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.RevokeRefreshToken,
|
||||
request_deserializer=gapido__auth_dot_generated_dot_auth__pb2.RevokeRefreshTokenRequest.FromString,
|
||||
response_serializer=gapido__auth_dot_generated_dot_auth__pb2.RevokeRefreshTokenResponse.SerializeToString,
|
||||
),
|
||||
'PublicPing': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.PublicPing,
|
||||
request_deserializer=gapido__auth_dot_generated_dot_auth__pb2.PingRequest.FromString,
|
||||
response_serializer=gapido__auth_dot_generated_dot_auth__pb2.PingResponse.SerializeToString,
|
||||
),
|
||||
'UserOnly': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.UserOnly,
|
||||
request_deserializer=gapido__auth_dot_generated_dot_auth__pb2.ProtectedRequest.FromString,
|
||||
response_serializer=gapido__auth_dot_generated_dot_auth__pb2.ProtectedResponse.SerializeToString,
|
||||
),
|
||||
'AdminOnly': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.AdminOnly,
|
||||
request_deserializer=gapido__auth_dot_generated_dot_auth__pb2.ProtectedRequest.FromString,
|
||||
response_serializer=gapido__auth_dot_generated_dot_auth__pb2.ProtectedResponse.SerializeToString,
|
||||
),
|
||||
}
|
||||
generic_handler = grpc.method_handlers_generic_handler(
|
||||
'gapido.auth.v1.AuthService', rpc_method_handlers)
|
||||
server.add_generic_rpc_handlers((generic_handler,))
|
||||
server.add_registered_method_handlers('gapido.auth.v1.AuthService', rpc_method_handlers)
|
||||
|
||||
|
||||
# This class is part of an EXPERIMENTAL API.
|
||||
class AuthService(object):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
|
||||
@staticmethod
|
||||
def RequestOtp(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(
|
||||
request,
|
||||
target,
|
||||
'/gapido.auth.v1.AuthService/RequestOtp',
|
||||
gapido__auth_dot_generated_dot_auth__pb2.RequestOtpRequest.SerializeToString,
|
||||
gapido__auth_dot_generated_dot_auth__pb2.RequestOtpResponse.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)
|
||||
|
||||
@staticmethod
|
||||
def VerifyOtp(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(
|
||||
request,
|
||||
target,
|
||||
'/gapido.auth.v1.AuthService/VerifyOtp',
|
||||
gapido__auth_dot_generated_dot_auth__pb2.VerifyOtpRequest.SerializeToString,
|
||||
gapido__auth_dot_generated_dot_auth__pb2.TokenResponse.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)
|
||||
|
||||
@staticmethod
|
||||
def RefreshToken(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(
|
||||
request,
|
||||
target,
|
||||
'/gapido.auth.v1.AuthService/RefreshToken',
|
||||
gapido__auth_dot_generated_dot_auth__pb2.RefreshTokenRequest.SerializeToString,
|
||||
gapido__auth_dot_generated_dot_auth__pb2.TokenResponse.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)
|
||||
|
||||
@staticmethod
|
||||
def RevokeRefreshToken(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(
|
||||
request,
|
||||
target,
|
||||
'/gapido.auth.v1.AuthService/RevokeRefreshToken',
|
||||
gapido__auth_dot_generated_dot_auth__pb2.RevokeRefreshTokenRequest.SerializeToString,
|
||||
gapido__auth_dot_generated_dot_auth__pb2.RevokeRefreshTokenResponse.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)
|
||||
|
||||
@staticmethod
|
||||
def PublicPing(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(
|
||||
request,
|
||||
target,
|
||||
'/gapido.auth.v1.AuthService/PublicPing',
|
||||
gapido__auth_dot_generated_dot_auth__pb2.PingRequest.SerializeToString,
|
||||
gapido__auth_dot_generated_dot_auth__pb2.PingResponse.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)
|
||||
|
||||
@staticmethod
|
||||
def UserOnly(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(
|
||||
request,
|
||||
target,
|
||||
'/gapido.auth.v1.AuthService/UserOnly',
|
||||
gapido__auth_dot_generated_dot_auth__pb2.ProtectedRequest.SerializeToString,
|
||||
gapido__auth_dot_generated_dot_auth__pb2.ProtectedResponse.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)
|
||||
|
||||
@staticmethod
|
||||
def AdminOnly(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(
|
||||
request,
|
||||
target,
|
||||
'/gapido.auth.v1.AuthService/AdminOnly',
|
||||
gapido__auth_dot_generated_dot_auth__pb2.ProtectedRequest.SerializeToString,
|
||||
gapido__auth_dot_generated_dot_auth__pb2.ProtectedResponse.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)
|
||||
2
src/gapido_auth/tools/__init__.py
Normal file
2
src/gapido_auth/tools/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
"""Developer tooling."""
|
||||
|
||||
26
src/gapido_auth/tools/generate_proto.py
Normal file
26
src/gapido_auth/tools/generate_proto.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from pathlib import Path
|
||||
|
||||
from grpc_tools import protoc
|
||||
|
||||
|
||||
def main() -> None:
|
||||
root = Path(__file__).resolve().parents[3]
|
||||
proto_root = root / "proto"
|
||||
src_root = root / "src"
|
||||
proto_file = proto_root / "gapido_auth" / "generated" / "auth.proto"
|
||||
result = protoc.main(
|
||||
[
|
||||
"grpc_tools.protoc",
|
||||
f"-I{proto_root}",
|
||||
f"--python_out={src_root}",
|
||||
f"--grpc_python_out={src_root}",
|
||||
str(proto_file),
|
||||
]
|
||||
)
|
||||
if result != 0:
|
||||
raise SystemExit(result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
2
src/gapido_auth/transport/__init__.py
Normal file
2
src/gapido_auth/transport/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
"""Transport adapters."""
|
||||
|
||||
2
src/gapido_auth/transport/grpc/__init__.py
Normal file
2
src/gapido_auth/transport/grpc/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
"""gRPC transport."""
|
||||
|
||||
122
src/gapido_auth/transport/grpc/auth_servicer.py
Normal file
122
src/gapido_auth/transport/grpc/auth_servicer.py
Normal file
@@ -0,0 +1,122 @@
|
||||
# mypy: disable-error-code="attr-defined"
|
||||
import logging
|
||||
from collections.abc import Sequence
|
||||
|
||||
import grpc
|
||||
|
||||
from gapido_auth.application.auth_service import AuthService
|
||||
from gapido_auth.domain.entities import Role, TokenPair, User
|
||||
from gapido_auth.domain.errors import (
|
||||
AppError,
|
||||
AuthenticationError,
|
||||
InactiveUser,
|
||||
InvalidOtp,
|
||||
OtpAttemptsExceeded,
|
||||
OtpExpired,
|
||||
PermissionDenied,
|
||||
RateLimitExceeded,
|
||||
ValidationError,
|
||||
)
|
||||
from gapido_auth.generated import auth_pb2, auth_pb2_grpc
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AuthGrpcServicer(auth_pb2_grpc.AuthServiceServicer):
|
||||
def __init__(self, auth_service: AuthService) -> None:
|
||||
self._auth_service = auth_service
|
||||
|
||||
async def RequestOtp(self, request, context): # type: ignore[no-untyped-def]
|
||||
try:
|
||||
await self._auth_service.request_otp(
|
||||
mobile=request.mobile,
|
||||
purpose=request.purpose or "login",
|
||||
client_key=context.peer(),
|
||||
)
|
||||
return auth_pb2.RequestOtpResponse(accepted=True)
|
||||
except AppError as exc:
|
||||
await _abort_for_app_error(context, exc)
|
||||
|
||||
async def VerifyOtp(self, request, context): # type: ignore[no-untyped-def]
|
||||
try:
|
||||
token_pair = await self._auth_service.verify_otp(
|
||||
mobile=request.mobile,
|
||||
code=request.code,
|
||||
purpose=request.purpose or "login",
|
||||
)
|
||||
return _token_response(token_pair)
|
||||
except AppError as exc:
|
||||
await _abort_for_app_error(context, exc)
|
||||
|
||||
async def RefreshToken(self, request, context): # type: ignore[no-untyped-def]
|
||||
try:
|
||||
token_pair = await self._auth_service.refresh_token(request.refresh_token)
|
||||
return _token_response(token_pair)
|
||||
except AppError as exc:
|
||||
await _abort_for_app_error(context, exc)
|
||||
|
||||
async def RevokeRefreshToken(self, request, context): # type: ignore[no-untyped-def]
|
||||
try:
|
||||
token = _extract_bearer_token(context.invocation_metadata())
|
||||
await self._auth_service.get_authenticated_user(token)
|
||||
await self._auth_service.revoke_refresh_token(request.refresh_token)
|
||||
return auth_pb2.RevokeRefreshTokenResponse(revoked=True)
|
||||
except AppError as exc:
|
||||
await _abort_for_app_error(context, exc)
|
||||
|
||||
async def PublicPing(self, request, context): # type: ignore[no-untyped-def]
|
||||
return auth_pb2.PingResponse(message="public ok")
|
||||
|
||||
async def UserOnly(self, request, context): # type: ignore[no-untyped-def]
|
||||
try:
|
||||
user = await self._auth_service.get_authenticated_user(
|
||||
_extract_bearer_token(context.invocation_metadata())
|
||||
)
|
||||
return _protected_response(user, "authenticated user ok")
|
||||
except AppError as exc:
|
||||
await _abort_for_app_error(context, exc)
|
||||
|
||||
async def AdminOnly(self, request, context): # type: ignore[no-untyped-def]
|
||||
try:
|
||||
user = await self._auth_service.require_role(
|
||||
_extract_bearer_token(context.invocation_metadata()), Role.ADMIN
|
||||
)
|
||||
return _protected_response(user, "admin ok")
|
||||
except AppError as exc:
|
||||
await _abort_for_app_error(context, exc)
|
||||
|
||||
|
||||
def _token_response(token_pair: TokenPair): # type: ignore[no-untyped-def]
|
||||
return auth_pb2.TokenResponse(
|
||||
access_token=token_pair.access_token,
|
||||
refresh_token=token_pair.refresh_token,
|
||||
token_type=token_pair.token_type,
|
||||
expires_in=token_pair.expires_in,
|
||||
role=token_pair.role.value,
|
||||
)
|
||||
|
||||
|
||||
def _protected_response(user: User, message: str): # type: ignore[no-untyped-def]
|
||||
return auth_pb2.ProtectedResponse(user_id=user.id, role=user.role.value, message=message)
|
||||
|
||||
|
||||
def _extract_bearer_token(metadata: Sequence[tuple[str, str]]) -> str:
|
||||
for key, value in metadata:
|
||||
if key.lower() == "authorization" and value.startswith("Bearer "):
|
||||
return value.removeprefix("Bearer ").strip()
|
||||
raise AuthenticationError("missing bearer token")
|
||||
|
||||
|
||||
async def _abort_for_app_error(context: grpc.aio.ServicerContext, exc: AppError) -> None:
|
||||
if isinstance(exc, AuthenticationError | InvalidOtp | OtpExpired | OtpAttemptsExceeded):
|
||||
await context.abort(grpc.StatusCode.UNAUTHENTICATED, str(exc))
|
||||
if isinstance(exc, InactiveUser):
|
||||
await context.abort(grpc.StatusCode.PERMISSION_DENIED, str(exc))
|
||||
if isinstance(exc, PermissionDenied):
|
||||
await context.abort(grpc.StatusCode.PERMISSION_DENIED, str(exc))
|
||||
if isinstance(exc, RateLimitExceeded):
|
||||
await context.abort(grpc.StatusCode.RESOURCE_EXHAUSTED, str(exc))
|
||||
if isinstance(exc, ValidationError):
|
||||
await context.abort(grpc.StatusCode.INVALID_ARGUMENT, str(exc))
|
||||
logger.exception("Unhandled application error")
|
||||
await context.abort(grpc.StatusCode.INTERNAL, "internal error")
|
||||
54
src/gapido_auth/transport/grpc/server.py
Normal file
54
src/gapido_auth/transport/grpc/server.py
Normal file
@@ -0,0 +1,54 @@
|
||||
# mypy: disable-error-code="attr-defined,no-untyped-call"
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import grpc
|
||||
from grpc_health.v1 import health, health_pb2, health_pb2_grpc
|
||||
from grpc_reflection.v1alpha import reflection
|
||||
|
||||
from gapido_auth.config import get_settings
|
||||
from gapido_auth.generated import auth_pb2, auth_pb2_grpc
|
||||
from gapido_auth.infrastructure.container import build_auth_service
|
||||
from gapido_auth.infrastructure.rabbitmq import connect_robust
|
||||
from gapido_auth.transport.grpc.auth_servicer import AuthGrpcServicer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def serve() -> None:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
settings = get_settings()
|
||||
rabbitmq = await connect_robust(settings.rabbitmq_url)
|
||||
channel = await rabbitmq.channel()
|
||||
container = await build_auth_service(settings, channel)
|
||||
|
||||
server = grpc.aio.server()
|
||||
auth_servicer = AuthGrpcServicer(container.auth_service)
|
||||
auth_pb2_grpc.add_AuthServiceServicer_to_server(auth_servicer, server)
|
||||
|
||||
health_servicer = health.HealthServicer()
|
||||
health_pb2_grpc.add_HealthServicer_to_server(health_servicer, server)
|
||||
service_names = (
|
||||
auth_pb2.DESCRIPTOR.services_by_name["AuthService"].full_name,
|
||||
health.SERVICE_NAME,
|
||||
reflection.SERVICE_NAME,
|
||||
)
|
||||
reflection.enable_server_reflection(service_names, server)
|
||||
health_servicer.set("", health_pb2.HealthCheckResponse.SERVING)
|
||||
|
||||
listen_addr = f"{settings.grpc_host}:{settings.grpc_port}"
|
||||
server.add_insecure_port(listen_addr)
|
||||
await server.start()
|
||||
logger.info("gRPC auth service started on %s", listen_addr)
|
||||
|
||||
try:
|
||||
await server.wait_for_termination()
|
||||
finally:
|
||||
await server.stop(grace=5)
|
||||
await container.redis.aclose()
|
||||
container.mongo_client.close()
|
||||
await rabbitmq.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(serve())
|
||||
Reference in New Issue
Block a user