Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Simple auth manager to define its UI (login form) using fastapi #45696

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ airflow/git_version
# Exclude mode_modules pulled by "yarn" for compilation of www files generated by NPM
airflow/www/node_modules
airflow/ui/node_modules
airflow/auth/managers/simple/ui/node_modules

# Exclude link to docs
airflow/www/static/docs
Expand Down
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ repos:
- --fuzzy-match-generates-todo
- id: insert-license
name: Add license for all YAML files except Helm templates
exclude: ^\.github/.*$|^chart/templates/.*|.*/reproducible_build.yaml$|^airflow/api_fastapi/core_api/openapi/v1-generated.yaml$|^.*/pnpm-lock.yaml$
exclude: ^\.github/.*$|^chart/templates/.*|.*/reproducible_build.yaml$|^airflow/api_fastapi/core_api/openapi/v1-generated.yaml$|^airflow/auth/managers/simple/openapi/v1-generated.yaml$|^.*/pnpm-lock.yaml$
types: [yaml]
files: \.ya?ml$
args:
Expand Down Expand Up @@ -867,7 +867,7 @@ repos:
language: node
stages: ['manual']
types_or: [javascript, ts, tsx]
files: ^airflow/ui/
files: ^airflow/ui/|^airflow/auth/managers/simple/ui/
entry: ./scripts/ci/pre_commit/compile_ui_assets.py
pass_filenames: false
additional_dependencies: ['[email protected]']
Expand All @@ -876,7 +876,7 @@ repos:
language: node
stages: ['manual']
types_or: [javascript, ts, tsx]
files: ^airflow/ui/
files: ^airflow/ui/|^airflow/auth/managers/simple/ui/
entry: ./scripts/ci/pre_commit/compile_ui_assets_dev.py
pass_filenames: false
additional_dependencies: ['[email protected]']
Expand Down Expand Up @@ -1170,7 +1170,7 @@ repos:
description: TS types generation / ESLint / Prettier new UI files
language: node
types_or: [javascript, ts, tsx, yaml, css, json]
files: ^airflow/ui/|^airflow/api_fastapi/core_api/openapi/v1-generated\.yaml$
files: ^airflow/ui/|^airflow/api_fastapi/core_api/openapi/v1-generated\.yaml$|^airflow/auth/managers/simple/ui/|^airflow/auth/managers/simple/openapi/v1-generated\.yaml$
entry: ./scripts/ci/pre_commit/lint_ui.py
additional_dependencies: ['[email protected]']
pass_filenames: false
Expand Down
17 changes: 17 additions & 0 deletions airflow/auth/managers/simple/datamodels/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#
# 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.
35 changes: 35 additions & 0 deletions airflow/auth/managers/simple/datamodels/login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#
# 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 pydantic import Field

from airflow.api_fastapi.core_api.base import BaseModel


class LoginResponse(BaseModel):
"""Login serializer for responses."""

jwt_token: str


class LoginBody(BaseModel):
"""Login serializer for post bodies."""

username: str = Field()
password: str = Field()
17 changes: 17 additions & 0 deletions airflow/auth/managers/simple/openapi/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#
# 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.
114 changes: 114 additions & 0 deletions airflow/auth/managers/simple/openapi/v1-generated.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
openapi: 3.1.0
info:
title: Simple auth manager sub application
description: This is the simple auth manager fastapi sub application. This API is
only available if the auth manager used in the Airflow environment is simple auth
manager. This sub application provides the login form for users to log in.
version: 0.1.0
paths:
/auth/token:
post:
tags:
- SimpleAuthManagerLogin
summary: Create Token
description: Authenticate the user.
operationId: create_token
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/LoginBody'
required: true
responses:
'201':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPExceptionResponse'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPExceptionResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
components:
schemas:
HTTPExceptionResponse:
properties:
detail:
anyOf:
- type: string
- type: object
title: Detail
type: object
required:
- detail
title: HTTPExceptionResponse
description: HTTPException Model used for error response.
HTTPValidationError:
properties:
detail:
items:
$ref: '#/components/schemas/ValidationError'
type: array
title: Detail
type: object
title: HTTPValidationError
LoginBody:
properties:
username:
type: string
title: Username
password:
type: string
title: Password
type: object
required:
- username
- password
title: LoginBody
description: Login serializer for post bodies.
LoginResponse:
properties:
jwt_token:
type: string
title: Jwt Token
type: object
required:
- jwt_token
title: LoginResponse
description: Login serializer for responses.
ValidationError:
properties:
loc:
items:
anyOf:
- type: string
- type: integer
type: array
title: Location
msg:
type: string
title: Message
type:
type: string
title: Error Type
type: object
required:
- loc
- msg
- type
title: ValidationError
17 changes: 17 additions & 0 deletions airflow/auth/managers/simple/router/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#
# 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.
74 changes: 74 additions & 0 deletions airflow/auth/managers/simple/router/login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# 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, status

from airflow.api_fastapi.app import get_auth_manager
from airflow.api_fastapi.common.router import AirflowRouter
from airflow.api_fastapi.core_api.openapi.exceptions import create_openapi_http_exception_doc
from airflow.auth.managers.simple.datamodels.login import LoginBody, LoginResponse
from airflow.auth.managers.simple.simple_auth_manager import SimpleAuthManager
from airflow.auth.managers.simple.user import SimpleAuthManagerUser
from airflow.configuration import conf
from airflow.utils.jwt_signer import JWTSigner

login_router = AirflowRouter(tags=["SimpleAuthManagerLogin"])


@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]),
)
def create_token(
body: LoginBody,
) -> LoginResponse:
"""Authenticate the user."""
if not body.username or not body.password:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Username and password must be provided",
)

users = SimpleAuthManager.get_users()
passwords = SimpleAuthManager.get_passwords(users)
found_users = [
user
for user in users
if user["username"] == body.username and passwords[user["username"]] == body.password
]

if len(found_users) == 0:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid credentials",
)

user = SimpleAuthManagerUser(
username=body.username,
role=found_users[0]["role"],
)

signer = JWTSigner(
secret_key=conf.get("api", "auth_jwt_secret"),
expiration_time_in_seconds=conf.getint("api", "auth_jwt_expiration_time"),
audience="front-apis",
)
token = signer.generate_signed_token(get_auth_manager().serialize_user(user))
return LoginResponse(jwt_token=token)
Loading