117 lines
4.1 KiB
Python
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
|