Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,25 @@ info:
version: 0.1.0
paths:
/auth/token:
get:
tags:
- SimpleAuthManagerLogin
summary: Create Token All Admins
description: Create a token with no credentials only if ``simple_auth_manager_all_admins``
is True.
operationId: create_token_all_admins
responses:
'201':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPExceptionResponse'
post:
tags:
- SimpleAuthManagerLogin
summary: Create Token
description: Authenticate the user.
operationId: create_token
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/LoginBody'
required: true
parameters:
- name: Content-Type
in: header
required: false
schema:
type: string
description: Content-Type of the request body
enum:
- application/json
- application/x-www-form-urlencoded
default: application/json
title: Content-Type
description: Content-Type of the request body
responses:
'201':
description: Successful Response
Expand All @@ -47,23 +34,58 @@ paths:
schema:
$ref: '#/components/schemas/LoginResponse'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPExceptionResponse'
description: Bad Request
'401':
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPExceptionResponse'
description: Unauthorized
'415':
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPExceptionResponse'
description: Unsupported Media Type
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginBody'
application/x-www-form-urlencoded:
schema:
$ref: '#/components/schemas/LoginBody'
get:
tags:
- SimpleAuthManagerLogin
summary: Create Token All Admins
description: Create a token with no credentials only if ``simple_auth_manager_all_admins``
is True.
operationId: create_token_all_admins
responses:
'201':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPExceptionResponse'
description: Forbidden
/auth/token/login:
get:
tags:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@

from __future__ import annotations

from fastapi import Request, status
from fastapi import Depends, Request, status
from starlette.responses import RedirectResponse

from airflow.api_fastapi.auth.managers.base_auth_manager import COOKIE_NAME_JWT_TOKEN
from airflow.api_fastapi.auth.managers.simple.datamodels.login import LoginBody, LoginResponse
from airflow.api_fastapi.auth.managers.simple.services.login import SimpleAuthManagerLogin
from airflow.api_fastapi.auth.managers.simple.utils import parse_login_body
from airflow.api_fastapi.common.router import AirflowRouter
from airflow.api_fastapi.common.types import Mimetype
from airflow.api_fastapi.core_api.openapi.exceptions import create_openapi_http_exception_doc
from airflow.configuration import conf

Expand All @@ -33,10 +35,33 @@
@login_router.post(
"/token",
status_code=status.HTTP_201_CREATED,
responses=create_openapi_http_exception_doc([status.HTTP_400_BAD_REQUEST, status.HTTP_401_UNAUTHORIZED]),
responses={
**create_openapi_http_exception_doc(
[
status.HTTP_400_BAD_REQUEST,
status.HTTP_401_UNAUTHORIZED,
status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
]
),
201: {
"description": "Successful Response",
"content": {
Mimetype.JSON: {"schema": {"$ref": "#/components/schemas/LoginResponse"}},
},
},
},
openapi_extra={
"requestBody": {
"required": True,
"content": {
"application/json": {"schema": {"$ref": "#/components/schemas/LoginBody"}},
"application/x-www-form-urlencoded": {"schema": {"$ref": "#/components/schemas/LoginBody"}},
},
}
},
)
def create_token(
body: LoginBody,
body: LoginBody = Depends(parse_login_body),
) -> LoginResponse:
"""Authenticate the user."""
return LoginResponse(access_token=SimpleAuthManagerLogin.create_token(body=body))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import * as Common from "./common";
export const useSimpleAuthManagerLoginServiceCreateTokenAllAdmins = <TData = Common.SimpleAuthManagerLoginServiceCreateTokenAllAdminsDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>(queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: Common.UseSimpleAuthManagerLoginServiceCreateTokenAllAdminsKeyFn(queryKey), queryFn: () => SimpleAuthManagerLoginService.createTokenAllAdmins() as TData, ...options });
export const useSimpleAuthManagerLoginServiceLoginAllAdmins = <TData = Common.SimpleAuthManagerLoginServiceLoginAllAdminsDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>(queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: Common.UseSimpleAuthManagerLoginServiceLoginAllAdminsKeyFn(queryKey), queryFn: () => SimpleAuthManagerLoginService.loginAllAdmins() as TData, ...options });
export const useSimpleAuthManagerLoginServiceCreateToken = <TData = Common.SimpleAuthManagerLoginServiceCreateTokenMutationResult, TError = unknown, TContext = unknown>(options?: Omit<UseMutationOptions<TData, TError, {
contentType?: "application/json" | "application/x-www-form-urlencoded";
requestBody: LoginBody;
}, TContext>, "mutationFn">) => useMutation<TData, TError, {
contentType?: "application/json" | "application/x-www-form-urlencoded";
requestBody: LoginBody;
}, TContext>({ mutationFn: ({ requestBody }) => SimpleAuthManagerLoginService.createToken({ requestBody }) as unknown as Promise<TData>, ...options });
}, TContext>({ mutationFn: ({ contentType, requestBody }) => SimpleAuthManagerLoginService.createToken({ contentType, requestBody }) as unknown as Promise<TData>, ...options });
export const useSimpleAuthManagerLoginServiceCreateTokenCli = <TData = Common.SimpleAuthManagerLoginServiceCreateTokenCliMutationResult, TError = unknown, TContext = unknown>(options?: Omit<UseMutationOptions<TData, TError, {
requestBody: LoginBody;
}, TContext>, "mutationFn">) => useMutation<TData, TError, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,57 @@ import type { CancelablePromise } from "./core/CancelablePromise";
import { OpenAPI } from "./core/OpenAPI";
import { request as __request } from "./core/request";
import type {
CreateTokenAllAdminsResponse,
CreateTokenData,
CreateTokenResponse,
CreateTokenAllAdminsResponse,
CreateTokenCliData,
CreateTokenCliResponse,
} from "./types.gen";

export class SimpleAuthManagerLoginService {
/**
* Create Token All Admins
* Create a token with no credentials only if ``simple_auth_manager_all_admins`` is True.
* @returns LoginResponse Successful Response
* @throws ApiError
*/
public static createTokenAllAdmins(): CancelablePromise<CreateTokenAllAdminsResponse> {
return __request(OpenAPI, {
method: "GET",
url: "/auth/token",
errors: {
403: "Forbidden",
},
});
}

/**
* Create Token
* Authenticate the user.
* @param data The data for the request.
* @param data.requestBody
* @param data.contentType Content-Type of the request body
* @returns LoginResponse Successful Response
* @throws ApiError
*/
public static createToken(data: CreateTokenData): CancelablePromise<CreateTokenResponse> {
return __request(OpenAPI, {
method: "POST",
url: "/auth/token",
headers: {
"Content-Type": data.contentType,
},
body: data.requestBody,
mediaType: "application/json",
errors: {
400: "Bad Request",
401: "Unauthorized",
415: "Unsupported Media Type",
422: "Validation Error",
},
});
}

/**
* Create Token All Admins
* Create a token with no credentials only if ``simple_auth_manager_all_admins`` is True.
* @returns LoginResponse Successful Response
* @throws ApiError
*/
public static createTokenAllAdmins(): CancelablePromise<CreateTokenAllAdminsResponse> {
return __request(OpenAPI, {
method: "GET",
url: "/auth/token",
errors: {
403: "Forbidden",
},
});
}

/**
* Login All Admins
* Login the user with no credentials.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,18 @@ export type ValidationError = {
type: string;
};

export type CreateTokenAllAdminsResponse = LoginResponse;

export type CreateTokenData = {
/**
* Content-Type of the request body
*/
contentType?: "application/json" | "application/x-www-form-urlencoded";
requestBody: LoginBody;
};

export type CreateTokenResponse = LoginResponse;

export type CreateTokenAllAdminsResponse = LoginResponse;

export type CreateTokenCliData = {
requestBody: LoginBody;
};
Expand All @@ -52,18 +56,6 @@ export type CreateTokenCliResponse = LoginResponse;

export type $OpenApiTs = {
"/auth/token": {
get: {
res: {
/**
* Successful Response
*/
201: LoginResponse;
/**
* Forbidden
*/
403: HTTPExceptionResponse;
};
};
post: {
req: CreateTokenData;
res: {
Expand All @@ -79,12 +71,28 @@ export type $OpenApiTs = {
* Unauthorized
*/
401: HTTPExceptionResponse;
/**
* Unsupported Media Type
*/
415: HTTPExceptionResponse;
/**
* Validation Error
*/
422: HTTPValidationError;
};
};
get: {
res: {
/**
* Successful Response
*/
201: LoginResponse;
/**
* Forbidden
*/
403: HTTPExceptionResponse;
};
};
};
"/auth/token/login": {
get: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
from __future__ import annotations

from fastapi import HTTPException, Request, status
from fastapi.exceptions import RequestValidationError
from pydantic import ValidationError

from airflow.api_fastapi.auth.managers.simple.datamodels.login import LoginBody
from airflow.api_fastapi.common.headers import HeaderContentTypeJsonOrForm
from airflow.api_fastapi.common.types import Mimetype


async def parse_login_body(
request: Request,
content_type: HeaderContentTypeJsonOrForm,
) -> LoginBody:
try:
if content_type == Mimetype.JSON:
body = await request.json()
elif content_type == Mimetype.FORM:
form = await request.form()
body = {
"username": form.get("username"),
"password": form.get("password"),
}
else:
raise HTTPException(
status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
detail="Unsupported Media Type",
)
return LoginBody(**body)
except ValidationError as e:
raise RequestValidationError(repr(e))
23 changes: 23 additions & 0 deletions airflow-core/src/airflow/api_fastapi/common/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,26 @@ def header_accept_json_or_ndjson_depends(


HeaderAcceptJsonOrNdjson = Annotated[Mimetype, Depends(header_accept_json_or_ndjson_depends)]


def header_content_type_json_or_form_depends(
content_type: Annotated[
str,
Header(
alias="Content-Type",
description="Content-Type of the request body",
json_schema_extra={"enum": [Mimetype.JSON, Mimetype.FORM]},
),
] = Mimetype.JSON,
) -> Mimetype:
if content_type.startswith(Mimetype.JSON):
return Mimetype.JSON
if content_type.startswith(Mimetype.FORM):
return Mimetype.FORM
raise HTTPException(
status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
detail="Only application/json or application/x-www-form-urlencoded is supported",
)


HeaderContentTypeJsonOrForm = Annotated[Mimetype, Depends(header_content_type_json_or_form_depends)]
Loading