storyboard/storyboard/api/v1/storage/swift_impl.py

117 lines
4.1 KiB
Python

# Copyright (c) 2019 Adam Coldrick
#
# Licensed 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 hashlib import sha1
import hmac
from time import time
import uuid
import openstack
from openstack import connection
from oslo_config import cfg
from six.moves.urllib import parse
from storyboard.api.v1.storage.storage import StorageBackend
CONF = cfg.CONF
SWIFT_OPTS = [
cfg.StrOpt("cloud",
default="",
help="Name of the cloud which provides Swift as "
"used in `clouds.yaml`. Other auth-related "
"options are ignored if this is set."),
cfg.StrOpt("auth_url",
default="http://127.0.0.1:8888/auth/v1.0",
help="URL to use to obtain an auth token from swift."),
cfg.StrOpt("auth_type",
default="v1password",
help="Swift auth type, defaults to 'v1password' "
"(which is legacy auth)."),
cfg.StrOpt("user",
default="test:tester",
help="User to use when authenticating with Swift to obtain an "
"auth token."),
cfg.StrOpt("password",
default="testing",
help="Password to use when authenticating with Swift."),
cfg.StrOpt("container",
default="storyboard",
help="Swift container to store attachments in. Will be "
"created if it doesn't already exist."),
cfg.StrOpt("temp_url_key",
default="secret_key",
help="Temp URL secret key to set for the container if it "
"is created by StoryBoard."),
cfg.IntOpt("temp_url_timeout",
default=120,
help="Number of seconds that Swift tempurl signatures "
"are valid for after generation.")
]
CONF.register_opts(SWIFT_OPTS, "swift")
class SwiftStorageImpl(StorageBackend):
"""Implementation of an attachment storage backend using swift."""
def _get_connection(self):
if CONF.swift.cloud:
return connection.Connection(
cloud=CONF.swift.cloud,
service_types={'object-store'})
return openstack.connect(
auth_type=CONF.swift.auth_type,
auth_url=CONF.swift.auth_url,
username=CONF.swift.user,
password=CONF.swift.password,
)
def _ensure_container_exists(self, conn):
names = [container.name
for container in conn.object_store.containers()]
if CONF.swift.container not in names:
conn.object_store.create_container(CONF.swift.container)
conn.object_store.set_container_temp_url_key(
CONF.swift.container, CONF.swift.temp_url_key)
conn.object_store.set_container_metadata(
CONF.swift.container, read_ACL=".r:*")
def get_upload_url(self):
conn = self._get_connection()
self._ensure_container_exists(conn)
url = conn.object_store.get_endpoint()
return "%s/%s" % (url, CONF.swift.container)
def get_auth(self):
conn = self._get_connection()
self._ensure_container_exists(conn)
name = str(uuid.uuid4())
endpoint = parse.urlparse(conn.object_store.get_endpoint())
path = '/'.join((endpoint.path, CONF.swift.container, name))
method = 'PUT'
expires = int(time() + CONF.swift.temp_url_timeout)
hmac_body = '%s\n%s\n%s' % (method, expires, path)
hmac_body = hmac_body.encode('utf8')
key = conn.object_store.get_temp_url_key(CONF.swift.container)
signature = hmac.new(key, hmac_body, sha1).hexdigest()
return expires, signature, name