blob: 6534361c4af898b544dc54d41fdee13ebb1403e8 [file]
# 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.
#
"""Administrative user provisioning with a separately managed capability."""
import logging
from flask import request
from werkzeug.exceptions import BadRequest, RequestEntityTooLarge
from openserverless import app
from openserverless.common import response_builder
from openserverless.error.config_exception import ConfigException
from openserverless.impl.provisioning.admission import provisioning_authorizer
from openserverless.impl.provisioning.kube_client import ProvisioningError, ProvisioningKubeClient
from openserverless.impl.provisioning.user_service import UserProvisioningService, bearer_token
@app.route("/system/api/v1/users", methods=["POST"])
def create_user():
"""
Create an OPS tenant (asynchronous provisioning).
---
tags: [User provisioning]
operationId: createUser
security:
- provisioningBearer: []
consumes: [application/json]
parameters:
- in: header
name: Authorization
required: true
type: string
description: Bearer token from the externally managed provisioning Secret.
- in: body
name: user
required: true
schema:
type: object
additionalProperties: false
required: [user, password, email]
properties:
user: {type: string, minLength: 5, maxLength: 61}
password: {type: string, format: password}
email: {type: string, format: email}
services: {type: string, default: all}
responses:
202: {description: "WhiskUser created; provisioning pending, not a readiness guarantee."}
400: {description: Invalid payload or unavailable requested service.}
401: {description: Missing or invalid provisioning token.}
409: {description: User exists or namespace is retained; no update performed.}
413: {description: Request body exceeds 16 KiB.}
415: {description: Content-Type must be application/json.}
503: {description: "Provisioning configuration or Kubernetes dependency unavailable."}
500: {description: Unexpected internal error.}
"""
try:
token = bearer_token(request.headers.get("Authorization"))
provisioning_authorizer.authorize(token)
service = UserProvisioningService(ProvisioningKubeClient())
if not request.is_json:
raise ProvisioningError("Content-Type must be application/json", 415)
request.max_content_length = 16384
body = request.get_json()
result = service.provision(body)
return response_builder.build_response_with_data(result, 202)
except ProvisioningError as exc:
response = response_builder.build_error_message(str(exc), exc.status)
if exc.status == 503:
response.headers["Retry-After"] = "1"
return response
except BadRequest:
return response_builder.build_error_message("Invalid JSON payload", 400)
except RequestEntityTooLarge:
return response_builder.build_error_message("Request body exceeds 16 KiB", 413)
except (ConfigException, OSError):
return response_builder.build_error_message("Provisioning dependency unavailable", 503)
except Exception as exc:
# Exception messages/tracebacks may contain caller data; log only type.
logging.error("User provisioning failed (%s)", type(exc).__name__)
return response_builder.build_error_message("User provisioning failed", 500)