Finish initial provisioning API spec and impl

This commit is contained in:
Tulir Asokan
2018-07-13 21:25:51 +03:00
parent c0ceb1b2b0
commit 1fd920255f
4 changed files with 480 additions and 70 deletions
+33 -11
View File
@@ -43,29 +43,34 @@ class AuthAPI(abc.ABC):
message="Code requested successfully.")
except PhoneNumberInvalidError:
return self.get_login_response(mxid=user.mxid, state="request", status=400,
errcode="phone_number_invalid",
error="Invalid phone number.")
except PhoneNumberBannedError:
return self.get_login_response(mxid=user.mxid, state="request", status=403,
errcode="phone_number_banned",
error="Your phone number is banned from Telegram.")
except PhoneNumberAppSignupForbiddenError:
return self.get_login_response(mxid=user.mxid, state="request", status=403,
errcode="phone_number_app_signup_forbidden",
error="You have disabled 3rd party apps on your account.")
except PhoneNumberUnoccupiedError:
return self.get_login_response(mxid=user.mxid, state="request", status=404,
errcode="phone_number_unoccupied",
error="That phone number has not been registered.")
except PhoneNumberFloodError:
return self.get_login_response(
mxid=user.mxid, state="request", status=429,
mxid=user.mxid, state="request", status=429, errcode="phone_number_flood",
error="Your phone number has been temporarily blocked for flooding. "
"The ban is usually applied for around a day.")
except FloodWaitError as e:
return self.get_login_response(
mxid=user.mxid, state="request", status=429,
mxid=user.mxid, state="request", status=429, errcode="flood_wait",
error="Your phone number has been temporarily blocked for flooding. "
f"Please wait for {format_duration(e.seconds)} before trying again.")
except PhoneNumberBannedError:
return self.get_login_response(mxid=user.mxid, state="request", status=401,
error="Your phone number is banned from Telegram.")
except PhoneNumberAppSignupForbiddenError:
return self.get_login_response(mxid=user.mxid, state="request", status=401,
error="You have disabled 3rd party apps on your account.")
except Exception:
self.log.exception("Error requesting phone code")
return self.get_login_response(mxid=user.mxid, state="request", status=500,
errcode="exception",
error="Internal server error while requesting code.")
async def post_login_token(self, user, token):
@@ -76,6 +81,14 @@ class AuthAPI(abc.ABC):
user.command_status = None
return self.get_login_response(mxid=user.mxid, state="logged-in", status=200,
username=user_info.username)
except AccessTokenInvalidError:
return self.get_login_response(mxid=user.mxid, state="token", status=401,
errcode="bot_token_invalid",
error="Bot token invalid.")
except AccessTokenExpiredError:
return self.get_login_response(mxid=user.mxid, state="token", status=403,
errcode="bot_token_expired",
error="Bot token expired.")
except Exception:
self.log.exception("Error sending bot token")
return self.get_login_response(mxid=user.mxid, state="token", status=500,
@@ -90,10 +103,12 @@ class AuthAPI(abc.ABC):
return self.get_login_response(mxid=user.mxid, state="logged-in", status=200,
username=user_info.username)
except PhoneCodeInvalidError:
return self.get_login_response(mxid=user.mxid, state="code", status=403,
return self.get_login_response(mxid=user.mxid, state="code", status=401,
errcode="phone_code_invalid",
error="Incorrect phone code.")
except PhoneCodeExpiredError:
return self.get_login_response(mxid=user.mxid, state="code", status=403,
errcode="phone_code_expired",
error="Phone code expired.")
except SessionPasswordNeededError:
if not password_in_data:
@@ -103,12 +118,13 @@ class AuthAPI(abc.ABC):
"action": "Login (password entry)",
}
return self.get_login_response(
mxid=user.mxid, state="password", status=200,
mxid=user.mxid, state="password", status=202,
message="Code accepted, but you have 2-factor authentication is enabled.")
return None
except Exception:
self.log.exception("Error sending phone code")
return self.get_login_response(mxid=user.mxid, state="code", status=500,
errcode="exception",
error="Internal server error while sending code.")
async def post_login_password(self, user, password):
@@ -119,10 +135,16 @@ class AuthAPI(abc.ABC):
user.command_status = None
return self.get_login_response(mxid=user.mxid, state="logged-in", status=200,
username=user_info.username)
except (PasswordHashInvalidError, PasswordEmptyError):
except PasswordEmptyError:
return self.get_login_response(mxid=user.mxid, state="password", status=400,
errcode="password_empty",
error="Empty password.")
except PasswordHashInvalidError:
return self.get_login_response(mxid=user.mxid, state="password", status=401,
errcode="password_invalid",
error="Incorrect password.")
except Exception:
self.log.exception("Error sending password")
return self.get_login_response(mxid=user.mxid, state="password", status=500,
errcode="exception",
error="Internal server error while sending password.")
+66 -2
View File
@@ -17,11 +17,75 @@
from aiohttp import web
import logging
from ..common import AuthAPI
class ProvisioningAPI:
class ProvisioningAPI(AuthAPI):
log = logging.getLogger("mau.provisioning")
def __init__(self, loop):
self.loop = loop
super(AuthAPI, self).__init__(loop)
self.app = web.Application(loop=loop)
login_prefix = "/login/{mxid:@[^:]*:.+}"
self.app.router.add_route("POST", f"{login_prefix}/bot_token", self.send_bot_token)
self.app.router.add_route("POST", f"{login_prefix}/request_code", self.request_code)
self.app.router.add_route("POST", f"{login_prefix}/send_code", self.send_code)
self.app.router.add_route("POST", f"{login_prefix}/send_password", self.send_password)
def get_login_response(self, status=200, state="", username="", mxid="", message="", error="",
errcode=""):
if username:
resp = {
"state": "logged-in",
"username": username,
}
elif message:
resp = {
"message": message
}
else:
resp = {
"error": error,
"errcode": errcode,
}
return web.json_response(resp, status=status)
async def get_user(self, request: web.Request):
mxid = request.match_info["mxid"]
user = await User.get_by_mxid(mxid).ensure_started(even_if_no_session=True)
if not user.puppet_whitelisted:
return user, self.get_login_response(mxid=user.mxid, error="You are not whitelisted.",
errcode="mxid_not_whitelisted", status=403)
elif await user.is_logged_in():
return user, self.get_login_response(mxid=user.mxid, username=user.username, status=409)
return user, None
async def send_bot_token(self, request: web.Request):
user, err = await self.get_user(request)
if err:
return err
data = await request.json()
return await self.post_login_token(user, data.get("token", ""))
async def request_code(self, request: web.Request):
user, err = await self.get_user(request)
if err:
return err
data = await request.json()
return await self.post_login_phone(user, data.get("phone", ""))
async def send_code(self, request: web.Request):
user, err = await self.get_user(request)
if err:
return err
data = await request.json()
return await self.post_login_code(user, data.get("code", 0), password_in_data=False)
async def send_password(self, request: web.Request):
user, err = await self.get_user(request)
if err:
return err
data = await request.json()
return await self.post_login_password(user, data.get("password", ""))
+381 -56
View File
@@ -1,70 +1,395 @@
swagger: "2.0"
info:
title: mautrix-telegram provisioning
version: 0.3.0
description: The provisioning API for mautrix-telegram.
contact:
name: Tulir Asokan
email: tulir@maunium.net
url: https://maunium.net
license:
name: AGPLv3
url: https://github.com/tulir/mautrix-telegram/blob/master/LICENSE
externalDocs:
description: Provisioning API wiki page on GitHub.
url: https://github.com/tulir/mautrix-telegram/wiki/Provisioning-API
basePath: /_matrix/provision
schemes: [https]
consumes: [application/json]
produces: [application/json]
tags:
-
name: login
description: 'Authentication endpoints.'
- name: Authentication
paths:
/login/{mxid}/bot_token:
post:
operationId: post_bot_token
summary: Log in with a bot token
tags: [Authentication]
responses:
200:
description: Login successful
schema:
$ref: "#/definitions/AuthSuccess"
400:
$ref: "#/responses/MissingMXIDError"
401:
description: Invalid or expired bot token
schema:
type: object
title: Error
properties:
errcode:
type: string
title: Error code
description: A machine-readable error code
example: bot_token_<error>
enum:
- bot_token_invalid
- bot_token_expired
error:
$ref: "#/definitions/HumanReadableError"
403:
$ref: "#/responses/NotWhitelistedError"
409:
$ref: "#/responses/AlreadyLoggedInError"
500:
$ref: "#/responses/UnknownError"
parameters:
- name: mxid
in: path
description: The Matrix ID of the user who to log in as
required: true
type: string
- name: body
in: body
required: true
schema:
type: object
properties:
token:
type: string
description: The access token of the bot to log in as
example: "297900271:IXjeGEcAN61zHnjPgkWnYWyvVp9K4ulHBEv"
/login/{mxid}/request_code:
post:
operationId: post_login_phone
summary: Request a phone code from Telegram
tags: [Authentication]
responses:
200:
description: Code requested successfully
schema:
$ref: "#/definitions/AuthSuccess"
400:
description: Invalid phone number or missing Matrix ID
schema:
type: object
title: Error
properties:
errcode:
type: string
title: Error code
description: A machine-readable error code
example: machine_readable_error
enum:
- phone_number_invalid
- mxid_empty
error:
$ref: "#/definitions/HumanReadableError"
403:
description: Matrix ID is not whitelisted or phone number is banned or has forbidden 3rd party apps
schema:
type: object
title: Error
properties:
errcode:
type: string
title: Error code
description: A machine-readable error code
example: machine_readable_error
enum:
- mxid_not_whitelisted
- phone_number_banned
- phone_number_app_signup_forbidden
error:
$ref: "#/definitions/HumanReadableError"
404:
description: Unregistered phone number
schema:
type: object
title: Error
properties:
errcode:
type: string
title: Error code
description: A machine-readable error code
enum:
- phone_number_unoccupied
error:
$ref: "#/definitions/HumanReadableError"
409:
$ref: "#/responses/AlreadyLoggedInError"
429:
description: Phone number has been temporarily blocked for flooding
schema:
type: object
title: Error
properties:
errcode:
type: string
title: Error code
description: A machine-readable error code
enum:
- flood_wait
- phone_number_flood
error:
$ref: "#/definitions/HumanReadableError"
500:
$ref: "#/responses/UnknownError"
parameters:
- name: mxid
in: path
description: The Matrix ID of the user who to log in as
required: true
type: string
- name: body
in: body
required: true
schema:
type: object
properties:
phone:
type: string
description: The phone number to log in as.
example: "+123456789"
/login/{mxid}/send_code:
post:
operationId: post_login_code
summary: Send the login code
tags: [Authentication]
responses:
200:
description: Login successful
schema:
$ref: "#/definitions/AuthSuccess"
202:
description: Correct code, but two-factor authentication is enabled
schema:
$ref: "#/definitions/AuthSuccess"
400:
$ref: "#/responses/MissingMXIDError"
401:
description: Invalid phone code
schema:
type: object
title: Error
properties:
errcode:
type: string
title: Error code
description: A machine-readable error code
enum:
- phone_code_invalid
error:
$ref: "#/definitions/HumanReadableError"
403:
description: Matrix ID not whitelisted or phone code expired
schema:
type: object
title: Error
properties:
errcode:
type: string
title: Error code
description: A machine-readable error code
example: machine_readable_error
enum:
- mxid_not_whitelisted
- phone_code_expired
error:
$ref: "#/definitions/HumanReadableError"
409:
$ref: "#/responses/AlreadyLoggedInError"
500:
$ref: "#/responses/UnknownError"
parameters:
- name: mxid
in: path
description: The Matrix ID of the user who to log in as
required: true
type: string
- name: body
in: body
required: true
schema:
type: object
properties:
code:
type: integer
description: The phone code from Telegram.
format: int32
example: 123456
/login/{mxid}/send_password:
post:
operationId: post_login_password
summary: Send the two-factor auth password
tags: [Authentication]
responses:
200:
description: Login successful
schema:
$ref: "#/definitions/AuthSuccess"
400:
description: Missing password or Matrix ID
schema:
type: object
title: Error
properties:
errcode:
type: string
title: Error code
description: A machine-readable error code
example: <field>_empty
enum:
- password_empty
- mxid_empty
error:
$ref: "#/definitions/HumanReadableError"
401:
description: Incorrect password
schema:
type: object
title: Error
properties:
errcode:
type: string
title: Error code
description: A machine-readable error code
enum:
- password_invalid
error:
$ref: "#/definitions/HumanReadableError"
403:
$ref: "#/responses/NotWhitelistedError"
409:
$ref: "#/responses/AlreadyLoggedInError"
500:
$ref: "#/responses/UnknownError"
parameters:
- name: mxid
in: path
description: The Matrix ID of the user who to log in as
required: true
type: string
- name: body
in: body
required: true
schema:
type: object
properties:
password:
type: string
description: The two-factor auth password
format: password
example: hunter2
responses:
NotWhitelistedError:
description: Matrix ID not whitelisted for puppeting
schema:
type: object
title: Error
properties:
errcode:
type: string
title: Error code
description: A machine-readable error code
enum:
- mxid_not_whitelisted
error:
$ref: "#/definitions/HumanReadableError"
AlreadyLoggedInError:
description: The Matrix user is already logged in
schema:
type: object
properties:
state:
type: string
enum:
- logged-in
username:
type: string
description: The Telegram username the user is logged in as.
MissingMXIDError:
description: Missing Matrix ID
schema:
type: object
title: Error
properties:
errcode:
type: string
title: Error code
description: A machine-readable error code
enum:
- mxid_empty
error:
$ref: "#/definitions/HumanReadableError"
UnknownError:
description: Unknown error
schema:
type: object
title: UnknownError
properties:
errcode:
type: string
title: Error code
description: A machine-readable error code
enum:
- exception
error:
type: string
title: Error
description: A human-readable description of the error
example: Internal server error while <action>.
enum:
- Internal server error while requesting code.
- Internal server error while sending code.
- Internal server error while sending password.
- Internal server error while sending token.
definitions:
Error:
x-oad-type: object
type: object
title: Error
properties:
errcode:
x-oad-type: string
type: string
title: 'Error code'
description: 'A machine-readable error code'
error:
x-oad-type: string
type: string
title: Error
description: 'A human-readable description of the error'
status:
x-oad-type: integer
type: integer
title: Status
description: 'The HTTP status code'
format: int32
HumanReadableError:
type: string
description: A human-readable description of the error
example: A human-readable description of the error
AuthSuccess:
x-oad-type: object
type: object
properties:
state:
x-oad-type: string
type: string
description: The state/next step after the successful operation.
enum:
- code
- request
- password
- token
- logged-in
- code
- request
- password
- token
- logged-in
username:
type: string
description: The Telegram username the user is logged in as. Only applicable if state=logged-in
security:
-
Bearer: []
- Bearer: []
securityDefinitions:
Bearer:
description: 'Required authentication for all endpoints'
description: Required authentication for all endpoints
name: Authorization
in: header
type: apiKey
info:
title: 'mautrix-telegram provisioning'
version: 0.3.0
description: 'The provisioning API for mautrix-telegram.'
contact:
name: 'Tulir Asokan'
email: tulir@maunium.net
url: 'https://maunium.net'
license:
name: AGPLv3
url: 'https://github.com/tulir/mautrix-telegram/blob/master/LICENSE'
externalDocs:
description: 'Provisioning API wiki page on GitHub.'
url: 'https://github.com/tulir/mautrix-telegram/wiki/Provisioning-API'
basePath: /_matrix/provisioning
schemes:
- https
consumes:
- application/json
produces:
- application/json
swagger: '2.0'
-1
View File
@@ -16,7 +16,6 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from aiohttp import web
from mako.template import Template
import asyncio
import pkg_resources
import logging