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