summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-x.functests35
-rw-r--r--.gitignore22
-rw-r--r--.gitreview2
-rwxr-xr-x.unittests (renamed from unittests.sh)19
-rw-r--r--README.md12
-rwxr-xr-xbin/gluster-swift-gen-builders5
-rw-r--r--doc/markdown/auth_guide.md16
-rw-r--r--doc/markdown/swiftkerbauth/AD_client.md206
-rw-r--r--doc/markdown/swiftkerbauth/AD_server.md119
-rw-r--r--doc/markdown/swiftkerbauth/architecture.md105
-rw-r--r--doc/markdown/swiftkerbauth/ipa_client.md80
-rw-r--r--doc/markdown/swiftkerbauth/ipa_server.md146
-rw-r--r--doc/markdown/swiftkerbauth/swiftkerbauth_guide.md517
-rw-r--r--etc/object-expirer.conf-gluster33
-rwxr-xr-xfunctests.sh15
-rw-r--r--gluster/swift/__init__.py8
-rw-r--r--gluster/swift/common/DiskDir.py61
-rw-r--r--gluster/swift/common/Glusterfs.py13
-rw-r--r--gluster/swift/common/constraints.py41
-rw-r--r--gluster/swift/common/fs_utils.py8
-rw-r--r--gluster/swift/common/middleware/gswauth/swauth/middleware.py33
-rw-r--r--gluster/swift/common/middleware/gswauth/test_swauth/__init__.py6
-rw-r--r--gluster/swift/common/middleware/gswauth/test_swauth/unit/test_authtypes.py63
-rw-r--r--gluster/swift/common/middleware/gswauth/test_swauth/unit/test_middleware.py4519
-rw-r--r--gluster/swift/common/middleware/swiftkerbauth/__init__.py38
-rw-r--r--gluster/swift/common/middleware/swiftkerbauth/apachekerbauth/etc/httpd/conf.d/swift-auth.conf12
-rwxr-xr-xgluster/swift/common/middleware/swiftkerbauth/apachekerbauth/var/www/cgi-bin/swift-auth70
-rw-r--r--gluster/swift/common/middleware/swiftkerbauth/kerbauth.py463
-rw-r--r--gluster/swift/common/middleware/swiftkerbauth/kerbauth_utils.py137
-rw-r--r--gluster/swift/common/utils.py32
-rw-r--r--gluster/swift/obj/diskfile.py94
-rw-r--r--glusterfs-openstack-swift.spec26
-rw-r--r--requirements.txt (renamed from tools/requirements.txt)0
-rw-r--r--setup.py2
-rw-r--r--test-requirements.txt (renamed from tools/test-requires)0
-rw-r--r--test/functional/gluster_swift_tests.py91
-rw-r--r--test/functional/swift_test_client.py47
-rw-r--r--test/functional/swift_testing.py45
-rwxr-xr-xtest/functional/test_account.py584
-rwxr-xr-xtest/functional/test_container.py833
-rwxr-xr-xtest/functional/test_object.py524
-rw-r--r--test/functional/tests.py470
-rw-r--r--test/functional_auth/common_conf/account-server.conf (renamed from test/functional_auth/tempauth/conf/account-server.conf)4
-rw-r--r--test/functional_auth/common_conf/container-server.conf (renamed from test/functional_auth/gswauth/conf/container-server.conf)4
-rw-r--r--test/functional_auth/common_conf/fs.conf (renamed from test/functional_auth/gswauth/conf/fs.conf)0
-rw-r--r--test/functional_auth/common_conf/object-expirer.conf27
-rw-r--r--test/functional_auth/common_conf/object-server.conf (renamed from test/functional_auth/gswauth/conf/object-server.conf)5
-rw-r--r--test/functional_auth/common_conf/swift.conf (renamed from test/functional_auth/gswauth/conf/swift.conf)0
-rw-r--r--test/functional_auth/common_conf/test.conf (renamed from test/functional_auth/swiftkerbauth/conf/test.conf)23
-rw-r--r--test/functional_auth/gswauth/conf/account-server.conf32
-rw-r--r--test/functional_auth/gswauth/conf/object-expirer.conf17
-rw-r--r--test/functional_auth/gswauth/conf/proxy-server.conf26
-rw-r--r--test/functional_auth/keystone/conf/account-server.conf32
-rw-r--r--test/functional_auth/keystone/conf/container-server.conf35
-rw-r--r--test/functional_auth/keystone/conf/fs.conf19
-rw-r--r--test/functional_auth/keystone/conf/object-server.conf48
-rw-r--r--test/functional_auth/keystone/conf/proxy-server.conf27
-rw-r--r--test/functional_auth/keystone/conf/swift.conf85
-rw-r--r--test/functional_auth/swiftkerbauth/__init__.py0
-rw-r--r--test/functional_auth/swiftkerbauth/conf/account-server.conf36
-rw-r--r--test/functional_auth/swiftkerbauth/conf/container-server.conf36
-rw-r--r--test/functional_auth/swiftkerbauth/conf/fs.conf13
-rw-r--r--test/functional_auth/swiftkerbauth/conf/object-server.conf51
-rw-r--r--test/functional_auth/swiftkerbauth/conf/proxy-server.conf17
-rw-r--r--test/functional_auth/swiftkerbauth/conf/swift.conf84
-rw-r--r--test/functional_auth/swiftkerbauth/test_swkrbath_active.py93
-rw-r--r--test/functional_auth/tempauth/conf/container-server.conf35
-rw-r--r--test/functional_auth/tempauth/conf/fs.conf19
-rw-r--r--test/functional_auth/tempauth/conf/object-expirer.conf17
-rw-r--r--test/functional_auth/tempauth/conf/object-server.conf48
-rw-r--r--test/functional_auth/tempauth/conf/proxy-server.conf21
-rw-r--r--test/functional_auth/tempauth/conf/swift.conf85
-rw-r--r--test/object_expirer_functional/__init__.py (renamed from gluster/swift/common/middleware/gswauth/test_swauth/unit/__init__.py)0
-rw-r--r--test/object_expirer_functional/test_object_expirer.py332
-rw-r--r--test/unit/__init__.py39
-rw-r--r--test/unit/common/middleware/swiftkerbauth/__init__.py0
-rw-r--r--test/unit/common/middleware/swiftkerbauth/test_kerbauth.py478
-rw-r--r--test/unit/common/middleware/swiftkerbauth/test_kerbauth_utils.py77
-rw-r--r--test/unit/common/test_constraints.py73
-rw-r--r--test/unit/common/test_diskdir.py14
-rw-r--r--test/unit/common/test_utils.py24
-rw-r--r--test/unit/obj/test_diskfile.py55
-rw-r--r--test/unit/obj/test_expirer.py14
-rwxr-xr-xtest/unit/proxy/controllers/test_obj.py98
-rw-r--r--test/unit/proxy/test_server.py794
-rwxr-xr-xtools/gswauth_functional_tests.sh13
-rwxr-xr-xtools/keystone_functional_tests.sh6
-rwxr-xr-xtools/object_expirer_functional.sh (renamed from tools/swkrbath_functional_tests.sh)53
-rwxr-xr-xtools/tempauth_functional_tests.sh (renamed from tools/functional_tests.sh)9
-rw-r--r--tox.ini42
90 files changed, 3780 insertions, 8832 deletions
diff --git a/.functests b/.functests
new file mode 100755
index 0000000..6b13b69
--- /dev/null
+++ b/.functests
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+# Copyright (c) 2013 Red Hat, Inc.
+#
+# 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.
+
+# This program expects to be run by tox in a virtual python environment
+# so that it does not pollute the host development system
+
+EXIT_STATUS=0
+
+# Run functional tests with tempauth as auth middleware
+bash tools/tempauth_functional_tests.sh || EXIT_STATUS=$?
+
+# Run functional tests with gswauth as auth middleware
+bash tools/gswauth_functional_tests.sh || EXIT_STATUS=$?
+
+# Run object expirer functional tests if gsexpiring volume is found.
+if mount | grep "gsexpiring on /mnt/gluster-object/gsexpiring type fuse.glusterfs"; then
+ echo "Running object expirer functional tests"
+ bash tools/object_expirer_functional.sh || EXIT_STATUS=$?
+fi
+
+exit $EXIT_STATUS
diff --git a/.gitignore b/.gitignore
index cd4a111..938554e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,13 @@
-/.tox
-gluster_swift.egg-info
-/test/unit/.coverage
-/test/unit/nosetests.xml
-/test/unit/coverage.xml
-/test/unit/cover
-/build
-/swift
-*.pyc
+*.py[co]
+*.sw?
+*~
+dist
+build
+cover
+functional_tests_result
+.coverage
+*.egg
+*.egg-info
+.tox
+pycscope.*
+cscope.*
diff --git a/.gitreview b/.gitreview
index 6b69219..466f899 100644
--- a/.gitreview
+++ b/.gitreview
@@ -1,2 +1,2 @@
[gerrit]
-defaultbranch=master
+defaultbranch=icehouse
diff --git a/unittests.sh b/.unittests
index 940ada7..fcd066a 100755
--- a/unittests.sh
+++ b/.unittests
@@ -15,10 +15,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+TOP_DIR=$(python -c "import os; print os.path.dirname(os.path.realpath('$0'))")
-cd $(dirname $0)/test/unit
-nosetests -v --exe --with-coverage --cover-package gluster --cover-erase --cover-html --cover-branches $@
-
-saved_status=$?
+python -c 'from distutils.version import LooseVersion as Ver; import nose, sys; sys.exit(0 if Ver(nose.__version__) >= Ver("1.2.0") else 1)'
+if [ $? != 0 ]; then
+ cover_branches=""
+else
+ # Having the HTML reports is REALLY useful for achieving 100% branch
+ # coverage.
+ cover_branches="--cover-branches --cover-html --cover-html-dir=$TOP_DIR/cover"
+fi
+cd $TOP_DIR/test/unit
+nosetests -v --exe --with-coverage --cover-package gluster. --cover-erase $cover_branches $@
+rvalue=$?
rm -f .coverage
-exit $saved_status
+cd -
+exit $rvalue
diff --git a/README.md b/README.md
index fd63499..42ab315 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,3 @@
-# Gluster-Swift has moved to Swift-On-File
-The Gluster-Swift project is being moved to a new project named [Swift-On-File][].
-This new name better represents its nature as a Swift backend that supports
-multiple Posix Filesystems, not just GlusterFS. The last released version of
-Gluster-Swift was the [Icehouse release][].
-
-[Swift-On-File][https://github.com/swiftonfile/swiftonfile]
-[Icehouse release][https://github.com/swiftonfile/swiftonfile/releases/tag/v1.13.1-2]
-
+# Gluster-Swift
+Gluster-Swift enables files and directories created on GlusterFS
+to be accessed as objects via the Swift API.
diff --git a/bin/gluster-swift-gen-builders b/bin/gluster-swift-gen-builders
index 2e5fe1f..4cf757d 100755
--- a/bin/gluster-swift-gen-builders
+++ b/bin/gluster-swift-gen-builders
@@ -68,7 +68,10 @@ if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
fi
-cd /etc/swift
+if ! cd "/etc/swift"; then
+ echo "The path /etc/swift not accessible. Please check if it exists."
+ exit 1
+fi
for builder_file in $builder_files
do
diff --git a/doc/markdown/auth_guide.md b/doc/markdown/auth_guide.md
index 274dd32..86c3650 100644
--- a/doc/markdown/auth_guide.md
+++ b/doc/markdown/auth_guide.md
@@ -12,13 +12,6 @@
* [User roles](#gswauth_user_roles)
* [GSwauth Tools](#gswauth_tools)
* [Authenticating a user](#gswauth_authenticate)
-* [Swiftkerbauth](#swiftkerbauth)
- * [Architecture](swiftkerbauth/architecture.md)
- * [RHEL IPA Server Guide](swiftkerbauth/ipa_server.md)
- * [RHEL IPA Client Guide](swiftkerbauth/ipa_client.md)
- * [Windows AD Server Guide](swiftkerbauth/AD_server.md)
- * [Windows AD Client Guide](swiftkerbauth/AD_client.md)
- * [Swiftkerbauth Guide](swiftkerbauth/swiftkerbauth_guide.md)
## <a name="keystone" />Keystone ##
The Standard Openstack authentication service
@@ -468,12 +461,3 @@ bash-4.2$ swift --os-auth-token=AUTH_tk7e68ef4698f14c7f95af07ab7b298610 --os-sto
README.md
~~~
**Note:** Reseller admins must always use the second method to acquire a token, in order to be given access to other accounts different than his own. The first method of using the username and password will give them access only to their own accounts.
-
-## <a name="swiftkerbauth" />Swiftkerbauth ##
-Kerberos authentication filter
-
-Carsten Clasohm implemented a new authentication filter for swift
-that uses Kerberos tickets for single sign on authentication, and
-grants administrator permissions based on the users group membership
-in a directory service like Red Hat Enterprise Linux Identity Management
-or Microsoft Active Directory.
diff --git a/doc/markdown/swiftkerbauth/AD_client.md b/doc/markdown/swiftkerbauth/AD_client.md
deleted file mode 100644
index 0947a1e..0000000
--- a/doc/markdown/swiftkerbauth/AD_client.md
+++ /dev/null
@@ -1,206 +0,0 @@
-#AD client setup guide
-
-###Contents
-* [Setup Overview] (#setup)
-* [Configure Network] (#network)
-* [Installing AD Client] (#AD-client)
-
-<a name="setup" />
-###Setup Overview
-
-This guide talks about adding fedora linux client to windows domain.
-The test setup included a client machine with Fedora 19 installed
-on it with all the latest packages updated. The crux is to add this linux
-machine to Windows Domain. This linux box is expected to act as RHS node and on which swiftkerbauth,
-apachekerbauth code would run.
-
-Set hostname (FQDN) to fcclient.winad.com
-
- # hostnamectl set-hostname "fcclient.winad.com"
-
- # hostname "fcclient.winad.com"
-
-
-<a name="network" />
-### Configure client
-
-* Deploy Fedora linux 19.
-
-* Update the system with latest packages.
-
-* Configure SELinux security parameters.
-
-* Install & configure samba
-
-* Configure DNS
-
-* Synchronize the time services
-
-* Join Domain
-
-* Install / Configure Kerberos Client
-
-
-The document assumes the installing Fedora Linux and configuring SELinux
-parameters to 'permissive' is known already.
-
-###Install & Configure Samba:
- # yum -y install samba samba-client samba-common samba-winbind
- samba-winbind-clients
-
- # service start smb
-
- # ps -aef | grep smb
- # chkconfig smb on
-
-###Synchronize time services
-The kerberos authentication and most of the DNS functionality could fail with
-clock skew if times are not synchronized.
-
- # cat /etc/ntp.conf
- server ns1.bos.redhat.com
- server 10.5.26.10
-
- # service ntpd stop
-
- # ntpdate 10.16.255.2
-
- # service ntpd start
-
- #chkconfig ntpd on
-
-Check if Windows server in the whole environment is also time synchronized with
-same source.
-
- # C:\Users\Administrator>w32tm /query /status | find "Source"
-
- Source: ns1.xxx.xxx.com
-
-###Configure DNS on client
-Improperly resolved hostname is the leading cause in authentication failures.
-Best practice is to configure fedora client to use Windows DNS.
-'nameserver' below is the IP address of the windows server.
- # cat /etc/resolve.conf
- domain server.winad.com
- search server.winad.com
- nameserver 10.nn.nnn.3
-
-###Set the hostname of the client properly (FQDN)
- # cat /etc/sysconfig/network
- HOSTNAME=fcclient.winad.com
-
-
-###Install & Configure kerberos client
-
- # yum -y install krb5-workstation
-
-Edit the /etc/krb5.conf as follows:
-
- # cat /etc/krb5.conf
- [logging]
- default = FILE:/var/log/krb5libs.log
- kdc = FILE:/var/log/krb5kdc.log
- admin_server = FILE:/var/log/kadmind.log
-
- [libdefaults]
- default_realm = WINAD.COM
- dns_lookup_realm = false
- dns_lookup_kdc = false
- ticket_lifetime = 24h
- renew_lifetime = 7d
- forwardable = true
-
- [realms]
- WINAD.COM = {
- kdc = server.winad.com
- admin_server = server.winad.com
- }
- [domain_realm]
- .demo = server.winad.com
- demo = server.winad.com
-
-###Join Domain
-Fire command 'system-config-authentication' on client. This should display a
-graphical wizard. Below inputs would help configure this wizard.
-
- - User account data base = winbind
- - winbind domain = winad
- - security model = ads
- - winbind ads realm = winad.com
- - winbind controller = server.winad.com
- - template shell = /bin/bash
- - let the other options be as is to default.
- - Perform Join domain and appy settings and quit. Please note this join should
- not see any errors. This makes the client fedora box to join the windows
- domain.
-
-###Configure the kerberos client
-This would bring the users/groups from Windows Active directory to this
-fedora client.
-
-Edit /etc/samba/smb.conf file to have below parameters in the global section.
-
- # cat /etc/samba/smb.conf
- [global]
- workgroup = winad
- realm = winad.com
- server string = Samba Server Version %v
- security = ADS
- allow trusted domains = No
- password server = server.winad.com
- log file = /var/log/samba/log.%m
- max log size = 50
- idmap uid = 10000­19999
- idmap gid = 10000­19999
- template shell = /bin/bash
- winbind separator = +
- winbind use default domain = Yes
- idmap config REFARCH­AD:range = 10000000­19999999
- idmap config REFARCH­AD:backend = rid
- cups options = raw
-
-
- # service smb stop
-
- # service winbind stop
-
- # tar -cvf /var/tmp/samba-cache-backup.tar /var/lib/samba
-
- # ls -la /var/tmp/samba-cache-backup.tar
-
- # rm ­-f /var/lib/samba/*
-
-
-Verify that no kerberos ticket available and cached.
-
- # kdestroy
-
- # klist
-
-Rejoin the domain.
-
- # net join -S server -U Administrstor
-
-Test that client rejoined the domain.
-
- # net ads info
-
-Restart smb and winbind service.
-
- # wbinfo --domain-users
-
-Perform kinit for the domain users prepared on active directory. This is obtain
-the kerberos ticket for user 'auth_admin'
-
- # kinit auth_admin
-
- # id -Gn auth_admin
-
-###Notes
-Obtaining the HTTP service principal & keytab file and installing it with
-swiftkerbauth is added to swiftkerbauth_guide
-
-###References
-Reference Document for adding Linux box to windows domain :
-Integrating Red Hat Enterprise Linux 6
-with Active Directory
diff --git a/doc/markdown/swiftkerbauth/AD_server.md b/doc/markdown/swiftkerbauth/AD_server.md
deleted file mode 100644
index 66d90f2..0000000
--- a/doc/markdown/swiftkerbauth/AD_server.md
+++ /dev/null
@@ -1,119 +0,0 @@
-#Windows Active Directory & Domain Controller Server Guide
-
-###Contents
-* [Setup Overview] (#Setup)
-* [Installing Active Directory Services] (#AD-server)
-* [Configuring DNS] (#DNS)
-* [Adding Users and Groups] (#users-groups)
-
-
-<a name="Setup" />
-###Setup Overview
-
-The setup includes a server machine installed with Windows 2008 R2 Server, with
-Domain Controller, Active Directory services & DNS server installed alongwith.
-The steps to install windows operating system and above servers can be found
-on MicroSoft Documentation. This windows Active Directory server would act as an
-authentication server in the whole setup. This would provide the access control
-and permissions for users on certain data objects.
-
-
-Windows 2008 R2 deployment:
-
-http://technet.microsoft.com/en-us/library/dd283085.aspx
-
-
-Configuring Active Directory, Domain Services, DNS server:
-
-http://technet.microsoft.com/en-us/library/cc770946.aspx
-
-
-<a name="AD-server" />
-###Installing AD Server
-
-Administrators need to follow simple instructions in Server Manager on Windows
-2008, and should add Active Directory Domain Services & DNS server. It is
-recommended to use static IP for DNS server. Preferred Hostname(FQDN) for
-Windows server could be of format hostname 'server.winad.com' where
-'winad.com' is a domain name.
-
-Following tips would help prepare a test setup neatly.
-
- - Select Active Directory Domain services wizard in Server Manager
- - Move on to install it with all the pre-requisits, e.g. .NET framework etc.
- - Configure Active directory after installtion via exapanding the 'Roles'
- section in the server manager.
- - Create a new Domain in the New Forest.
- - Type the FQDN, winad.com
- - Set Forest functional level Windows 2008 R2.
- - Selct additional options for this domain controller as DNS server.
- - Leave the log locations to default provided by wizard.
- - Set the Administrator Password carefully.
- - Thats it. You are done configuring active directory.
-
-
-<a name="dns" />
-###Configuring DNS
-
-This section explains configuring the DNS server installed on Windows 2008 R2
-server. You must know know about
-
- - Forward lookup zone
-
- - Reverse lookup zone
-
- - Zone type
-
-A forward lookup zone is simply a way to resolve hostnames to IP address.
-A reverse lookup zone is to lookup DNS hostname of the host IP.
-
-Following tips would help configure the Zones on DNS server.
-
- - Create a Forward lookup zone.
- - Create it a primary zone.
- - Add the Clients using their ip addresses and FQDN to this forward lookup
- zones.
- - This would add type 'A' record for that host on DNS server.
- - Similarly create a Reverser lookup zone.
- - Add clients 'PTR' record to this zone via browsing through the forward
- zones clients.
-
-The above setup can be tested on client once it joins the domain using 'dig'
-command as mentioned below.
-
-
-On client:
-
- # dig fcclient.winad.com
- This should yield you a Answer section mentioning its IP address.
-
- Reverse lookup can be tested using
-
- # 'dig -t ptr 101.56.168.192.in-addr.arpa.'
- The answer section should state the FQDN of the client.
-
- Repeat the above steps on client for Windows AD server as well.
-
-
-<a name="users-groups" />
-###Adding users and groups
-
-The following convention is to be followed in creating group names:
-
- <reseller-prefix>\_<volume-name>
-
- <reseller-prefix>\_<account-name>
-
-As of now, account=volume=group
-
-For example:
-
- AUTH\_test
-
-Adding groups and users to the Windows domain is easy task.
-
- - Start -> Administrative Tools -> Active Directory Users & Computers
- - Expand the domain name which was prepared earlier. e.g winad.com
- - Add groups with appropreate access rights.
- - Add users to the group with appropreate permissions.
- - Make sure you set password for users prepared on AD server.
diff --git a/doc/markdown/swiftkerbauth/architecture.md b/doc/markdown/swiftkerbauth/architecture.md
deleted file mode 100644
index fc6d764..0000000
--- a/doc/markdown/swiftkerbauth/architecture.md
+++ /dev/null
@@ -1,105 +0,0 @@
-# Architecture
-
-The Swift API is HTTP-based. As described in the Swift documentation
-[1], clients first make a request to an authentication URL, providing
-a username and password. The reply contains a token which is used in
-all subsequent requests.
-
-Swift has a chain of filters through which all client requests go. The
-filters to use are configured with the pipeline parameter in
-/etc/swift/proxy-server.conf:
-
- [pipeline:main]
- pipeline = healthcheck cache tempauth proxy-server
-
-For the single sign authentication, we added a new filter called
-"kerbauth" and put it into the filter pipeline in place of tempauth.
-
-The filter checks the URL for each client request. If it matches the
-authentication URL, the client is redirected to a URL on a different
-server (on the same machine). The URL is handled by a CGI script, which
-is set up to authenticate the client with Kerberos negotiation, retrieve
-the user's system groups [2], store them in a memcache ring shared with
-the Swift server, and return the authentication token to the client.
-
-When the client provides the token as part of a resource request, the
-kerbauth filter checks it against its memcache, grants administrator
-rights based on the group membership retrieved from memcache, and
-either grants or denies the resource access.
-
-[1] http://docs.openstack.org/api/openstack-object-storage/1.0/content/authentication-object-dev-guide.html
-
-[2] The user data and system groups are usually provided by Red Hat
- Enterprise Linux identity Management or Microsoft Active
- Directory. The script relies on the system configuration to be set
- accordingly (/etc/nsswitch.conf).
-
-*****
-
-## kerbauth.py
-
-The script kerbauth.py began as a copy of the tempauth.py script from
-from tempauth middleware. It contains the following modifications, among
-others:
-
-In the __init__ method, we read the ext_authentication_url parameter
-from /etc/swift/proxy-server.conf. This is the URL that clients are
-redirected to when they access either the Swift authentication URL, or
-when they request a resource without a valid authentication token.
-
-The configuration in proxy-server.conf looks like this:
-
- [filter:kerbauth]
- use = egg:swiftkerbauth#kerbauth
- ext_authentication_url = http://client.rhelbox.com/cgi-bin/swift-auth
-
-The authorize method was changed so that global administrator rights
-are granted if the user is a member of the auth_reseller_admin
-group. Administrator rights for a specific account like vol1 are
-granted if the user is a member of the auth_vol1 group. [3]
-
-The denied_response method was changed to return a HTTP redirect to
-the external authentication URL if no valid token was provided by the
-client.
-
-Most of the handle_get_token method was moved to the external
-authentication script. This method now returns a HTTP redirect.
-
-In the __call__ and get_groups method, we removed support for the
-HTTP_AUTHORIZATION header, which is only needed when Amazon S3 is
-used.
-
-Like tempauth.py, kerbauth.py uses a Swift wrapper to access
-memcache. This wrapper converts the key to an MD5 hash and uses the
-hash value to determine on which of a pre-defined list of servers to
-store the data.
-
-[3] "auth" is the default reseller prefix, and would be different if
- the reseller_prefix parameter in proxy-server.conf was set.
-
-## swift-auth CGI script
-
-swift-auth resides on an Apache server and assumes that Apache is
-configured to authenticate the user before this script is
-executed. The script retrieves the username from the REMOTE_USER
-environment variable, and checks if there already is a token for this
-user in the memcache ring. If not, it generates a new one, retrieves
-the user's system groups with "id -Gn USERNAME", stores this
-information in the memcache ring, and returns the token to the client.
-
-To allow the CGI script to connect to memcache, the SELinux booleans
-httpd_can_network_connect and httpd_can_network_memcache had to be
-set.
-
-The tempauth filter uses the uuid module to generate token
-strings. This module creates and runs temporary files, which leads to
-AVC denial messages in /var/log/audit/audit.log when used from an
-Apache CGI script. While the module still works, the audit log would
-grow quickly. Instead of writing an SELinux policy module to allow or
-to silently ignore these accesses, the swift-auth script uses the
-"random" module for generating token strings.
-
-Red Hat Enterprise Linux 6 comes with Python 2.6 which only provides
-method to list the locally defined user groups. To include groups from
-Red Hat Enterprise Linux Identity Management and in the future from
-Active Directory, the "id" command is run in a subprocess.
diff --git a/doc/markdown/swiftkerbauth/ipa_client.md b/doc/markdown/swiftkerbauth/ipa_client.md
deleted file mode 100644
index f6afc42..0000000
--- a/doc/markdown/swiftkerbauth/ipa_client.md
+++ /dev/null
@@ -1,80 +0,0 @@
-#IPA Client Guide
-
-##Contents
-* [Setup Overview] (#setup)
-* [Configure Network] (#network)
-* [Installing IPA Client] (#ipa-client)
-
-<a name="setup" />
-##Setup Overview
-We have used a F18 box as IPA client machine and used FreeIPA client.
-This document borrows instructions from the following more detailed guide.
-[RHEL 6 Identity Management Guide][]
-
-
-<a name="network" />
-## Configure network
-
-Set hostname (FQDN) to client.rhelbox.com
-> hostnamectl set-hostname "client.rhelbox.com"
->
-> hostname "client.rhelbox.com"
-
-Add following to /etc/sysconfig/network:
-
- HOSTNAME=client.rhelbox.com
-
-Add the following to /etc/hostname
-
- client.rhelbox.com
-
-Add the following to /etc/hosts
-
- 192.168.56.110 server.rhelbox.com server
- 192.168.56.101 client.rhelbox.com client
-
-Logout and login again and verify hostname :
-> hostname --fqdn
-
-Edit */etc/resolv.conf* to add this at beginning of file
-
- nameserver 192.168.56.110
-
-Warning: NetworkManager changes resolv.conf on restart
-
-Turn off firewall
-> service iptables stop
->
-> chkconfig iptables off
-
-<a name="ipa-client" />
-## Installing IPA Client
-
-Install IPA client packages:
-
-For RHEL:
-> yum install ipa-client ipa-admintools
-
-For Fedora:
-> yum install freeipa-client freeipa-admintools
-
-Install IPA client and add to domain:
->ipa-client-install --enable-dns-updates
-
- Discovery was successful!
- Hostname: client.rhelbox.com
- Realm: RHELBOX.COM
- DNS Domain: rhelbox.com
- IPA Server: server.rhelbox.com
- BaseDN: dc=rhelbox,dc=com
-
- Continue to configure the system with these values? [no]: yes
- User authorized to enroll computers: admin
-
-Check if client is configured correctly:
-> kinit admin
->
-> getent passwd admin
-
-
-[RHEL 6 Identity Management Guide]: https://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Identity_Management_Guide/
diff --git a/doc/markdown/swiftkerbauth/ipa_server.md b/doc/markdown/swiftkerbauth/ipa_server.md
deleted file mode 100644
index 55e654e..0000000
--- a/doc/markdown/swiftkerbauth/ipa_server.md
+++ /dev/null
@@ -1,146 +0,0 @@
-#IPA Server Guide
-
-##Contents
-* [Setup Overview] (#setup)
-* [Configure Network] (#network)
-* [Installing IPA Server] (#ipa-server)
-* [Configuring DNS] (#dns)
-* [Adding Users and Groups] (#users-groups)
-
-
-<a name="setup" />
-##Setup Overview
-We have used a RHEL 6.4 box as IPA and DNS server. This document borrows
-instructions from the following more detailed guide.
-[RHEL 6 Identity Management Guide][]
-
-
-<a name="network" />
-## Configure network
-
-Change hostname (FQDN) to server.rhelbox.com
-> hostname "server.rhelbox.com"
-
-Add following to */etc/sysconfig/network* file
-
- HOSTNAME=server.rhelbox.com
-
-Add the following to */etc/hosts* file
-
- 192.168.56.110 server.rhelbox.com server
- 192.168.56.101 client.rhelbox.com client
-
-Logout and login again and verify new hostname
-> hostname --fqdn
-
-Turn off firewall
-> service iptables stop
->
-> chkconfig iptables off
-
-
-<a name="ipa-server" />
-## Installing IPA Server
-
-Install IPA server packages and DNS dependencies
-> yum install ipa-server bind bind-dyndb-ldap
-
-Run the following interactive setup to install IPA server with DNS
-> ipa-server-install --setup-dns
-
- The IPA Master Server will be configured with:
- Hostname: server.rhelbox.com
- IP address: 192.168.56.110
- Domain name: rhelbox.com
- Realm name: RHELBOX.COM
-
- BIND DNS server will be configured to serve IPA domain with:
- Forwarders: No forwarders
- Reverse zone: 56.168.192.in-addr.arpa.
-
-The installation may take some time.
-
-Check if IPA is installed correctly :
-> kinit admin
->
-> ipa user-find admin
-
-
-<a name="dns" />
-## Configuring DNS
-
-Edit */etc/resolv.conf* to add this at beginning of file :
-
- nameserver 192.168.56.110
-
-Warning: NetworkManager changes resolv.conf on restart
-
-Add a DNS A record and PTR record for the client under rhelbox.com zone
-> ipa dnsrecord-add rhelbox.com client --a-rec=192.168.56.101 --a-create-reverse
-
-Check if DNS resolution is working by running :
-
-> dig server.rhelbox.com
-
- ;; ANSWER SECTION:
- server.rhelbox.com. 1200 IN A 192.168.56.110
-
-> dig client.rhelbox.com
-
- ;; ANSWER SECTION:
- client.rhelbox.com. 86400 IN A 192.168.56.101
-
-Check if reverse resolution works :
-
-> dig -t ptr 101.56.168.192.in-addr.arpa.
-
- ;; ANSWER SECTION:
- 101.56.168.192.in-addr.arpa. 86400 IN PTR client.rhelbox.com.
-
-
-> dig -t ptr 110.56.168.192.in-addr.arpa.
-
- ;; ANSWER SECTION:
- 110.56.168.192.in-addr.arpa. 86400 IN PTR server.rhelbox.com.
-
-
-<a name="users-groups" />
-## Adding users and groups
-
-The following convention is to be followed in creating group names:
-
- <reseller-prefix>\_<volume-name>
-
- <reseller-prefix>\_<account-name>
-
-As of now, account=volume=group
-
-For example:
-
- AUTH\_test
-
-Create *auth_reseller_admin* user group
-> ipa group-add auth_reseller_admin --desc="Full access to all Swift accounts"
-
-Create *auth_rhs_test* user group
-> ipa group-add auth_rhs_test --desc="Full access to rhs_test account"
-
-Create user *auth_admin* user as member of *auth_reseller_admin* user group
-> ipa user-add auth_admin --first=Auth --last=Admin --password
->
-> ipa group-add-member auth_reseller_admin --users=auth_admin
-
-Create user *rhs_test_admin* as member of *auth_rhs_test* user group
-> ipa user-add rhs_test_admin --first=RHS --last=Admin --password
->
-> ipa group-add-member auth_rhs_test --users=rhs_test_admin
-
-Create user *jsmith* with no relevant group membership
-> ipa user-add rhs_test_admin --first=RHS --last=Admin --password
-
-You can verify users have been added by running
->ipa user-find admin
-
-NOTE: Every user has to change password on first login.
-
-[RHEL 6 Identity Management Guide]: https://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Identity_Management_Guide/
diff --git a/doc/markdown/swiftkerbauth/swiftkerbauth_guide.md b/doc/markdown/swiftkerbauth/swiftkerbauth_guide.md
deleted file mode 100644
index 5da1827..0000000
--- a/doc/markdown/swiftkerbauth/swiftkerbauth_guide.md
+++ /dev/null
@@ -1,517 +0,0 @@
-#swiftkerbauth
-
-* [Installing Kerberos module for Apache] (#httpd-kerb-install)
-* [Creating HTTP Service Principal] (#http-principal)
-* [Installing and configuring swiftkerbauth] (#install-swiftkerbauth)
-* [Using swiftkerbauth] (#use-swiftkerbauth)
-* [Configurable Parameters] (#config-swiftkerbauth)
-* [Functional tests] (#swfunctest)
-
-<a name="httpd-kerb-install" />
-## Installing Kerberos module for Apache on IPA client
-
-Install httpd server with kerberos module:
-> yum install httpd mod_auth_kerb
->
-> service httpd restart
-
-Check if auth_kerb_module is loaded :
-> httpd -M | grep kerb
-
-Change httpd log level to debug by adding/changing the following in
-*/etc/httpd/conf/httpd.conf* file
-
- LogLevel debug
-
-httpd logs are at */var/log/httpd/error_log* for troubleshooting
-
-If SELinux is enabled, allow Apache to connect to memcache and
-activate the changes by running
->setsebool -P httpd_can_network_connect 1
->
->setsebool -P httpd_can_network_memcache 1
-
-*****
-
-<a name="http-principal" />
-## Creating HTTP Service Principal on IPA server
-
-Add a HTTP Kerberos service principal :
-> ipa service-add HTTP/client.rhelbox.com@RHELBOX.COM
-
-Retrieve the HTTP service principal to a keytab file:
-> ipa-getkeytab -s server.rhelbox.com -p HTTP/client.rhelbox.com@RHELBOX.COM -k /tmp/http.keytab
-
-Copy keytab file to client:
-> scp /tmp/http.keytab root@192.168.56.101:/etc/httpd/conf/http.keytab
-
-## Creating HTTP Service Principal on Windows AD server
-
-Add a HTTP Kerberos service principal:
-> c:\>ktpass.exe -princ HTTP/fcclient.winad.com@WINAD.COM -mapuser
-> auth_admin@WINAD.COM -pass Redhat*123 -out c:\HTTP.keytab -crypto DES-CBC-CRC
-> -kvno 0
-
-Use winscp to copy HTTP.ketab file to /etc/httpd/conf/http.keytab
-
-*****
-
-<a name="install-swiftkerbauth" />
-##Installing and configuring swiftkerbauth on IPA client
-
-Prerequisites for installing swiftkerbauth
-* swift (havana)
-* gluster-swift (optional)
-
-You can install swiftkerbauth using one of these three ways:
-
-Installing swiftkerbauth from source:
-> python setup.py install
-
-Installing swiftkerbauth using pip:
-> pip install swiftkerbauth
-
-Installing swiftkerbauth from RPMs:
-> ./makerpm.sh
->
-> rpm -ivh dist/swiftkerbauth-1.0.0-1.noarch.rpm
-
-Edit */etc/httpd/conf.d/swift-auth.conf* and change KrbServiceName, KrbAuthRealms and Krb5KeyTab parameters accordingly.
-More detail on configuring kerberos for apache can be found at:
-[auth_kerb_module Configuration][]
-
-Make /etc/httpd/conf/http.keytab readable by any user :
-> chmod 644 /etc/httpd/conf/http.keytab
-
-And preferably change owner of keytab file to apache :
-> chown apache:apache /etc/httpd/conf/http.keytab
-
-Reload httpd
-> service httpd reload
-
-Make authentication script executable:
-> chmod +x /var/www/cgi-bin/swift-auth
-
-*****
-
-<a name="#use-swiftkerbauth" />
-##Using swiftkerbauth
-
-### Adding kerbauth filter in swift pipeline
-
-Edit */etc/swift/proxy-server.conf* and add a new filter section as follows:
-
- [filter:kerbauth]
- use = egg:swiftkerbauth#kerbauth
- ext_authentication_url = http://client.rhelbox.com/cgi-bin/swift-auth
- auth_mode=passive
-
-Add kerbauth to pipeline
-
- [pipeline:main]
- pipeline = catch_errors healthcheck proxy-logging cache proxy-logging kerbauth proxy-server
-
-If the Swift server is not one of your Gluster nodes, edit
-*/etc/swift/fs.conf* and change the following lines in the DEFAULT
-section:
-
- mount_ip = RHS_NODE_HOSTNAME
- remote_cluster = yes
-
-Restart swift to activate kerbauth filer
-> swift-init main restart
-
-
-###Examples
-
-####Authenticate user and get Kerberos ticket
-
-> kinit auth_admin
-
-NOTE: curl ignores user specified in -u option. All further curl commands
-will use the currently authenticated auth_admin user.
-
-####Get an authentication token:
-> curl -v -u : --negotiate --location-trusted http://client.rhelbox.com:8080/auth/v1.0
-
- * About to connect() to client.rhelbox.com port 8080 (#0)
- * Trying 192.168.56.101...
- * connected
- * Connected to client.rhelbox.com (192.168.56.101) port 8080 (#0)
- > GET /auth/v1.0 HTTP/1.1
- > User-Agent: curl/7.27.0
- > Host: client.rhelbox.com:8080
- > Accept: */*
- >
- < HTTP/1.1 303 See Other
- < Content-Type: text/html; charset=UTF-8
- < Location: http://client.rhelbox.com/cgi-bin/swift-auth
- < Content-Length: 0
- < X-Trans-Id: txecd415aae89b4320b6145-0052417ea5
- < Date: Tue, 24 Sep 2013 11:59:33 GMT
- <
- * Connection #0 to host client.rhelbox.com left intact
- * Issue another request to this URL: 'http://client.rhelbox.com/cgi-bin/swift-auth'
- * About to connect() to client.rhelbox.com port 80 (#1)
- * Trying 192.168.56.101...
- * connected
- * Connected to client.rhelbox.com (192.168.56.101) port 80 (#1)
- > GET /cgi-bin/swift-auth HTTP/1.1
- > User-Agent: curl/7.27.0
- > Host: client.rhelbox.com
- > Accept: */*
- >
- < HTTP/1.1 401 Unauthorized
- < Date: Tue, 24 Sep 2013 11:59:33 GMT
- < Server: Apache/2.4.6 (Fedora) mod_auth_kerb/5.4
- < WWW-Authenticate: Negotiate
- < WWW-Authenticate: Basic realm="Swift Authentication"
- < Content-Length: 381
- < Content-Type: text/html; charset=iso-8859-1
- <
- * Ignoring the response-body
- * Connection #1 to host client.rhelbox.com left intact
- * Issue another request to this URL: 'http://client.rhelbox.com/cgi-bin/swift-auth'
- * Re-using existing connection! (#1) with host (nil)
- * Connected to (nil) (192.168.56.101) port 80 (#1)
- * Server auth using GSS-Negotiate with user ''
- > GET /cgi-bin/swift-auth HTTP/1.1
- > Authorization: Negotiate YIICYgYJKoZIhvcSAQICAQBuggJRMIICTaADAgEFoQMCAQ6iBwMFACAAAACjggFgYYIBXDCCAVigAwIBBaENGwtSSEVMQk9YLkNPTaIlMCOgAwIBA6EcMBobBEhUVFAbEmNsaWVudC5yaGVsYm94LmNvbaOCARkwggEVoAMCARKhAwIBAaKCAQcEggEDx9SH2R90RO4eAkhsNKow/DYfjv1rWhgxNRqj/My3yslASSgefls48VdDNHVVWqr1Kd6mB/9BIoumpA+of+KSAg2QfPtcWiVFj5n5Fa8fyCHyQPvV8c92KzUdrBPc8OVn0aldFp0I4P1MsYZbnddDRSH3kjVA5oSucHF59DhZWiGJV/F6sVimBSeoTBHQD38Cs5RhyDHNyUad9v3gZERVGCJXC76i7+yyaoIDA+N9s0hasHajhTnjs3XQBYfZFwp8lWl3Ub+sOtPO1Ng7mFlSAYXCM6ljlKTEaxRwaYoXUC1EoIqEOG/8pC9SJThS2M1G7MW1c5xm4lksNss72OH4gtPns6SB0zCB0KADAgESooHIBIHFrLtai5U8ajEWo1J9B26PnIUqLd+uA0KPd2Y2FjrH6rx4xT8qG2p8i36SVGubvwBVmfQ7lSJcXt6wUvb43qyPs/fMiSY7QxHxt7/btMgxQl6JWMagvXMhCNXnhEHNNaTdBcG5KFERDGeo0txaAD1bzZ4mnxCQmoqusGzZ6wdDw6+5wq1tK/hQTQUgk2NwxfXAg2J5K02/3fKjFR2h7zewI1pEyhhpeONRkkRETcyojkK2EbVzZ8kc3RsuwzFYsJ+9u5Qj3E4=
- > User-Agent: curl/7.27.0
- > Host: client.rhelbox.com
- > Accept: */*
- >
- < HTTP/1.1 200 OK
- < Date: Tue, 24 Sep 2013 11:59:33 GMT
- < Server: Apache/2.4.6 (Fedora) mod_auth_kerb/5.4
- < WWW-Authenticate: Negotiate YIGZBgkqhkiG9xIBAgICAG+BiTCBhqADAgEFoQMCAQ+iejB4oAMCARKicQRveeZTV/QRJSIOoOWPbZkEmtdug9V5ZcMGXWqAJvCAnrvw9gHbklMyLl8f8jU2e0wU3ehtchLEL4dVeAYgKsnUgw4wGhHu59AZBwSbHRKSpv3I6gWEZqC4NAEuZJFW9ipdUHOiclBQniVXXCsRF/5Y
- < X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a
- < X-Debug-Remote-User: auth_admin
- < X-Debug-Groups: auth_admin,auth_reseller_admin
- < X-Debug-Token-Life: 86400s
- < X-Debug-Token-Expires: Wed Sep 25 17:29:33 2013
- < Content-Length: 0
- < Content-Type: text/html; charset=UTF-8
- <
- * Connection #1 to host (nil) left intact
- * Closing connection #0
- * Closing connection #1
-
-The header *X-Auth-Token* in response contains the token *AUTH_tk083b8abc92f4a514f34224a181ed568a*.
-
-####PUT a container
->curl -v -X PUT -H 'X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a' http://client.rhelbox.com:8080/v1/AUTH_myvolume/c1
-
- * About to connect() to client.rhelbox.com port 8080 (#0)
- * Trying 192.168.56.101...
- * connected
- * Connected to client.rhelbox.com (192.168.56.101) port 8080 (#0)
- > PUT /v1/AUTH_myvolume/c1 HTTP/1.1
- > User-Agent: curl/7.27.0
- > Host: client.rhelbox.com:8080
- > Accept: */*
- > X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a
- >
- < HTTP/1.1 201 Created
- < Content-Length: 0
- < Content-Type: text/html; charset=UTF-8
- < X-Trans-Id: txc420b0ebf9714445900e8-0052418863
- < Date: Tue, 24 Sep 2013 12:41:07 GMT
- <
- * Connection #0 to host client.rhelbox.com left intact
- * Closing connection #0
-
-####GET a container listing
-> curl -v -X GET -H 'X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a' http://client.rhelbox.com:8080/v1/AUTH_myvolume
-
- * About to connect() to client.rhelbox.com port 8080 (#0)
- * Trying 192.168.56.101...
- * connected
- * Connected to client.rhelbox.com (192.168.56.101) port 8080 (#0)
- > GET /v1/AUTH_myvolume HTTP/1.1
- > User-Agent: curl/7.27.0
- > Host: client.rhelbox.com:8080
- > Accept: */*
- > X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a
- >
- < HTTP/1.1 200 OK
- < Content-Length: 3
- < X-Account-Container-Count: 0
- < Accept-Ranges: bytes
- < X-Account-Object-Count: 0
- < X-Bytes-Used: 0
- < X-Timestamp: 1379997117.09468
- < X-Object-Count: 0
- < X-Account-Bytes-Used: 0
- < X-Type: Account
- < Content-Type: text/plain; charset=utf-8
- < X-Container-Count: 0
- < X-Trans-Id: tx89826736a1ab4d6aae6e3-00524188dc
- < Date: Tue, 24 Sep 2013 12:43:08 GMT
- <
- c1
- * Connection #0 to host client.rhelbox.com left intact
- * Closing connection #0
-
-####PUT an object in container
-> curl -v -X PUT -H 'X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a' http://client.rhelbox.com:8080/v1/AUTH_myvolume/c1/object1 -d'Hello world'
-
- * About to connect() to client.rhelbox.com port 8080 (#0)
- * Trying 192.168.56.101...
- * connected
- * Connected to client.rhelbox.com (192.168.56.101) port 8080 (#0)
- > PUT /v1/AUTH_myvolume/c1/object1 HTTP/1.1
- > User-Agent: curl/7.27.0
- > Host: client.rhelbox.com:8080
- > Accept: */*
- > X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a
- > Content-Length: 11
- > Content-Type: application/x-www-form-urlencoded
- >
- * upload completely sent off: 11 out of 11 bytes
- < HTTP/1.1 201 Created
- < Last-Modified: Wed, 25 Sep 2013 06:08:00 GMT
- < Content-Length: 0
- < Etag: 3e25960a79dbc69b674cd4ec67a72c62
- < Content-Type: text/html; charset=UTF-8
- < X-Trans-Id: tx01f1b5a430cf4af3897be-0052427dc0
- < Date: Wed, 25 Sep 2013 06:08:01 GMT
- <
- * Connection #0 to host client.rhelbox.com left intact
- * Closing connection #0
-
-####Give permission to jsmith to list and download objects from c1 container
-> curl -v -X POST -H 'X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a' -H 'X-Container-Read: jsmith' http://client.rhelbox.com:8080/v1/AUTH_myvolume/c1
-
- * About to connect() to client.rhelbox.com port 8080 (#0)
- * Trying 192.168.56.101...
- * connected
- * Connected to client.rhelbox.com (192.168.56.101) port 8080 (#0)
- > POST /v1/AUTH_myvolume/c1 HTTP/1.1
- > User-Agent: curl/7.27.0
- > Host: client.rhelbox.com:8080
- > Accept: */*
- > X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a
- > X-Container-Read: jsmith
- >
- < HTTP/1.1 204 No Content
- < Content-Length: 0
- < Content-Type: text/html; charset=UTF-8
- < X-Trans-Id: txcedea3e2557d463eb591d-0052427f60
- < Date: Wed, 25 Sep 2013 06:14:56 GMT
- <
- * Connection #0 to host client.rhelbox.com left intact
- * Closing connection #0
-
-####Access container as jsmith
-
-> kinit jsmith
-
-Get token for jsmith
-> curl -v -u : --negotiate --location-trusted http://client.rhelbox.com:8080/auth/v1.0
-
- * About to connect() to client.rhelbox.com port 8080 (#0)
- * Trying 192.168.56.101...
- * connected
- * Connected to client.rhelbox.com (192.168.56.101) port 8080 (#0)
- > GET /auth/v1.0 HTTP/1.1
- > User-Agent: curl/7.27.0
- > Host: client.rhelbox.com:8080
- > Accept: */*
- >
- < HTTP/1.1 303 See Other
- < Content-Type: text/html; charset=UTF-8
- < Location: http://client.rhelbox.com/cgi-bin/swift-auth
- < Content-Length: 0
- < X-Trans-Id: txf51e1bf7f8c5496f8cc93-005242800b
- < Date: Wed, 25 Sep 2013 06:17:47 GMT
- <
- * Connection #0 to host client.rhelbox.com left intact
- * Issue another request to this URL: 'http://client.rhelbox.com/cgi-bin/swift-auth'
- * About to connect() to client.rhelbox.com port 80 (#1)
- * Trying 192.168.56.101...
- * connected
- * Connected to client.rhelbox.com (192.168.56.101) port 80 (#1)
- > GET /cgi-bin/swift-auth HTTP/1.1
- > User-Agent: curl/7.27.0
- > Host: client.rhelbox.com
- > Accept: */*
- >
- < HTTP/1.1 401 Unauthorized
- < Date: Wed, 25 Sep 2013 06:17:47 GMT
- < Server: Apache/2.4.6 (Fedora) mod_auth_kerb/5.4
- < WWW-Authenticate: Negotiate
- < WWW-Authenticate: Basic realm="Swift Authentication"
- < Content-Length: 381
- < Content-Type: text/html; charset=iso-8859-1
- <
- * Ignoring the response-body
- * Connection #1 to host client.rhelbox.com left intact
- * Issue another request to this URL: 'http://client.rhelbox.com/cgi-bin/swift-auth'
- * Re-using existing connection! (#1) with host (nil)
- * Connected to (nil) (192.168.56.101) port 80 (#1)
- * Server auth using GSS-Negotiate with user ''
- > GET /cgi-bin/swift-auth HTTP/1.1
- > Authorization: Negotiate YIICWAYJKoZIhvcSAQICAQBuggJHMIICQ6ADAgEFoQMCAQ6iBwMFACAAAACjggFbYYIBVzCCAVOgAwIBBaENGwtSSEVMQk9YLkNPTaIlMCOgAwIBA6EcMBobBEhUVFAbEmNsaWVudC5yaGVsYm94LmNvbaOCARQwggEQoAMCARKhAwIBAaKCAQIEgf/+3OaXYCSEjcsjU3t3lOLcYG84GBP9Kj9YTHc7yVMlcam4ivCwMqCkzxgvNo2E3a5KSWyFwngeX4b/QFbCKPXA4sfBibZRkeMk5gr2f0MLI3gWEAIYq7bJLre04bnkD2F0MzijPJrOLIx1KmFe08UGWCEmnG2uj07lvIR1RwV/7dMM4J1B+KKvDVKA0LxahwPIpx8oOON2yMGcstrBAHBBk5pmpt1Gg9Lh7xdNPsjP0IfI5Q0zkGCRBKpvpXymP1lQpQXlHbqkdBYOmG4+p/R+vIosO4ui1G6GWE9t71h3AqW61CcCj3/oOjZsG56k8HMSNk/+3mfUTP86nzLRGkekgc4wgcugAwIBEqKBwwSBwPsG9nGloEnOsA1abP4R1/yUDcikjjwKiacvZ+cu7bWEzu3L376k08U8C2YIClyUJy3Grt68LxhnfZ65VCZ5J5IOLiXOJnHBIoJ1L4GMYp4EgZzHvI7R3U3DApMzNWZwc1MsSF5UGhmLwxSevDLetJHjgKzKNteRyVN/8CFgjSBEjGSN1Qgy1RZHuQR9d3JHPczONZ4+ZgStfy+I1m2IUIgW3+4JGFVafHiBQVwSWRNfdXFgI3wBz7slntd7r3qMWA==
- > User-Agent: curl/7.27.0
- > Host: client.rhelbox.com
- > Accept: */*
- >
- < HTTP/1.1 200 OK
- < Date: Wed, 25 Sep 2013 06:17:47 GMT
- < Server: Apache/2.4.6 (Fedora) mod_auth_kerb/5.4
- < WWW-Authenticate: Negotiate YIGYBgkqhkiG9xIBAgICAG+BiDCBhaADAgEFoQMCAQ+ieTB3oAMCARKicARuH2YpjFrtgIhGr5nO7gh/21EvGH9tayRo5A3pw5pxD1B1036ePLG/x98OdMrSflse5s8ttz8FmvRphCFJa8kfYtnWULgoFLF2F2a1zBdSo2oCA0R05YFwArNhkg6ou5o7wWZkERHK33CKlhudSj8=
- < X-Auth-Token: AUTH_tkb5a20eb8207a819e76619431c8410447
- < X-Debug-Remote-User: jsmith
- < X-Debug-Groups: jsmith
- < X-Debug-Token-Life: 86400s
- < X-Debug-Token-Expires: Thu Sep 26 11:47:47 2013
- < Content-Length: 0
- < Content-Type: text/html; charset=UTF-8
- <
- * Connection #1 to host (nil) left intact
- * Closing connection #0
- * Closing connection #1
-
-List the container using authentication token for jsmith:
-> curl -v -X GET -H 'X-Auth-Token: AUTH_tkb5a20eb8207a819e76619431c8410447' http://client.rhelbox.com:8080/v1/AUTH_myvolume/c1
-
- * About to connect() to client.rhelbox.com port 8080 (#0)
- * Trying 192.168.56.101...
- * connected
- * Connected to client.rhelbox.com (192.168.56.101) port 8080 (#0)
- > GET /v1/AUTH_myvolume/c1 HTTP/1.1
- > User-Agent: curl/7.27.0
- > Host: client.rhelbox.com:8080
- > Accept: */*
- > X-Auth-Token: AUTH_tkb5a20eb8207a819e76619431c8410447
- >
- < HTTP/1.1 200 OK
- < Content-Length: 8
- < X-Container-Object-Count: 0
- < Accept-Ranges: bytes
- < X-Timestamp: 1
- < X-Container-Bytes-Used: 0
- < Content-Type: text/plain; charset=utf-8
- < X-Trans-Id: tx575215929c654d9f9f284-00524280a4
- < Date: Wed, 25 Sep 2013 06:20:20 GMT
- <
- object1
- * Connection #0 to host client.rhelbox.com left intact
- * Closing connection #0
-
-Downloading the object as jsmith:
-> curl -v -X GET -H 'X-Auth-Token: AUTH_tkb5a20eb8207a819e76619431c8410447' http://client.rhelbox.com:8080/v1/AUTH_myvolume/c1/object1
-
- * About to connect() to client.rhelbox.com port 8080 (#0)
- * Trying 192.168.56.101...
- * connected
- * Connected to client.rhelbox.com (192.168.56.101) port 8080 (#0)
- > GET /v1/AUTH_myvolume/c1/object1 HTTP/1.1
- > User-Agent: curl/7.27.0
- > Host: client.rhelbox.com:8080
- > Accept: */*
- > X-Auth-Token: AUTH_tkb5a20eb8207a819e76619431c8410447
- >
- < HTTP/1.1 200 OK
- < Content-Length: 11
- < Accept-Ranges: bytes
- < Last-Modified: Wed, 25 Sep 2013 06:08:00 GMT
- < Etag: 3e25960a79dbc69b674cd4ec67a72c62
- < X-Timestamp: 1380089280.98829
- < Content-Type: application/x-www-form-urlencoded
- < X-Trans-Id: tx19b5cc3847854f40a6ca8-00524281aa
- < Date: Wed, 25 Sep 2013 06:24:42 GMT
- <
- * Connection #0 to host client.rhelbox.com left intact
- Hello world* Closing connection #0
-
-For curl to follow the redirect, you need to specify additional
-options. With these, and with a current Kerberos ticket, you should
-get the Kerberos user's cached authentication token, or a new one if
-the previous token has expired.
-
-> curl -v -u : --negotiate --location-trusted -X GET http://client.rhelbox.com:8080/v1/AUTH_myvolume/c1/object1
-
-The --negotiate option is for curl to perform Kerberos authentication and
---location-trusted is for curl to follow the redirect.
-
-[auth_kerb_module Configuration]: http://modauthkerb.sourceforge.net/configure.html
-
-
-#### Get an authentication token when auth_mode=passive:
-> curl -v -H 'X-Auth-User: test:auth_admin' -H 'X-Auth-Key: Redhat*123' http://127.0.0.1:8080/auth/v1.0
-
-**NOTE**: X-Storage-Url response header can be returned only in passive mode.
-
-<a name="config-swiftkerbauth" />
-##Configurable Parameters
-
-The kerbauth filter section in **/etc/swift/proxy-server.conf** looks something
-like this:
-
- [filter:kerbauth]
- use = egg:swiftkerbauth#kerbauth
- ext_authentication_url = http://client.rhelbox.com/cgi-bin/swift-auth
- auth_method = active
- token_life = 86400
- debug_headers = yes
- realm_name = RHELBOX.COM
-
-Of all the options listed above, specifying **ext\_authentication\_url** is
-mandatory. The rest of the options are optional and have default values.
-
-#### ext\_authentication\_url
-A URL specifying location of the swift-auth CGI script. Avoid using IP address.
-Default value: None
-
-#### token_life
-After how many seconds the cached information about an authentication token is
-discarded.
-Default value: 86400
-
-#### debug_headers
-When turned on, the response headers sent to the user will contain additional
-debug information apart from the auth token.
-Default value: yes
-
-#### auth_method
-Set this to **"active"** when you want to allow access **only to clients
-residing inside the domain**. In this mode, authentication is performed by
-mod\_auth\_kerb using the Kerberos ticket bundled with the client request.
-No username and password have to be specified to get a token.
-Set this to **"passive"** when you want to allow access to clients residing
-outside the domain. In this mode, authentication is performed by gleaning
-username and password from request headers (X-Auth-User and X-Auth-Key) and
-running kinit command against it.
-Default value: passive
-
-#### realm_name
-This is applicable only when the auth_method=passive. This option specifies
-realm name if storage server belongs to more than one realm and realm name is not
-part of the username specified in X-Auth-User header.
-
-<a name="swfunctest" />
-##Functional tests for SwiftkerbAuth
-
-Functional tests to be run on the storage node after SwiftKerbAuth is setup using
-either IPA server or Windows AD. The gluster-swift/doc/markdown/swiftkerbauth
-directory contains the SwiftkerbAuth setup documents. There are two modes of
-working with SwiftKerbAuth. 'PASSIVE' mode indicates the client is outside the
-domain configured using SwiftKerbAuth. Client provides the 'Username' and
-'Password' while invoking a command. SwiftKerbAuth auth filter code then
-would get the ticket granting ticket from AD server or IPA server.
-In 'ACTIVE' mode of SwiftKerbAuth, User is already logged into storage node using
-its kerberos credentials. That user is authenticated across AD/IPA server.
-
-In PASSIVE mode all the generic functional tests are run. ACTIVE mode has a
-different way of acquiring Ticket Granting Ticket. And hence the different
-framework of functional tests there.
-
-The accounts, users, passwords must be prepared on AD/IPA server as per
-mentioned in test/functional_auth/swiftkerbauth/conf/test.conf
-
-Command to invoke SwiftKerbAuth functional tests is
-> $tox -e swfunctest
-
-This would run both ACTIVE and PASSIVE mode functional test cases.
diff --git a/etc/object-expirer.conf-gluster b/etc/object-expirer.conf-gluster
index 4449ee2..55c4fbf 100644
--- a/etc/object-expirer.conf-gluster
+++ b/etc/object-expirer.conf-gluster
@@ -1,18 +1,41 @@
-#TODO: Add documentation to explain various options
-#For now, refer: https://github.com/openstack/swift/blob/master/etc/object-expirer.conf-sample
-
[DEFAULT]
+user = root
[object-expirer]
user = root
log_facility = LOG_LOCAL2
-log_level = DEBUG
+log_level = INFO
+
# The following parameters are used by object-expirer and needs to be same
# across all conf files!
auto_create_account_prefix = gs
expiring_objects_account_name = expiring
-interval = 30
+# The swift-object-expirer daemon will run every 'interval' number of seconds
+# interval = 300
+
+# Emit a log line report of the progress so far every 'report_interval'
+# number of seconds.
+# report_interval = 300
+
+# concurrency is the level of concurrency to use to do the work, this value
+# must be set to at least 1
+# concurrency = 1
+
+# processes is how many parts to divide the work into, one part per process
+# that will be doing the work
+# processes set 0 means that a single process will be doing all the work
+# processes can also be specified on the command line and will override the
+# config value
+# processes = 0
+
+# process is which of the parts a particular process will work on
+# process can also be specified on the command line and will overide the config
+# value
+# process is "zero based", if you want to use 3 processes, you should run
+# processes with process set to 0, 1, and 2
+# process = 0
+
[pipeline:main]
pipeline = catch_errors cache proxy-server
diff --git a/functests.sh b/functests.sh
deleted file mode 100755
index 9797f58..0000000
--- a/functests.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-
-SRC_DIR=$(dirname $0)
-
-cd ${SRC_DIR}/test/functional
-nosetests --exe $@
-func1=$?
-cd -
-
-cd ${SRC_DIR}/test/functionalnosetests
-nosetests --exe $@
-func2=$?
-cd -
-
-exit $((func1 + func2))
diff --git a/gluster/swift/__init__.py b/gluster/swift/__init__.py
index 17f2fcf..555581a 100644
--- a/gluster/swift/__init__.py
+++ b/gluster/swift/__init__.py
@@ -42,9 +42,9 @@ class PkgInfo(object):
return '%s-dev' % (self.canonical_version,)
-###
-### Change the Package version here
-###
-_pkginfo = PkgInfo('1.13.0', '0', 'gluster_swift', False)
+#
+# Change the Package version here
+#
+_pkginfo = PkgInfo('1.13.1', '2', 'gluster_swift', False)
__version__ = _pkginfo.pretty_version
__canonical_version__ = _pkginfo.canonical_version
diff --git a/gluster/swift/common/DiskDir.py b/gluster/swift/common/DiskDir.py
index 0a91009..6112709 100644
--- a/gluster/swift/common/DiskDir.py
+++ b/gluster/swift/common/DiskDir.py
@@ -26,7 +26,10 @@ from gluster.swift.common.utils import validate_account, validate_container, \
X_CONTENT_LENGTH, X_TIMESTAMP, X_PUT_TIMESTAMP, X_ETAG, X_OBJECTS_COUNT, \
X_BYTES_USED, X_CONTAINER_COUNT, DIR_TYPE, rmobjdir, dir_is_object
from gluster.swift.common import Glusterfs
-from gluster.swift.common.exceptions import FileOrDirNotFoundError
+from gluster.swift.common.exceptions import FileOrDirNotFoundError, \
+ GlusterFileSystemIOError
+from swift.common.constraints import MAX_META_COUNT, MAX_META_OVERALL_SIZE
+from swift.common.swob import HTTPBadRequest
DATADIR = 'containers'
@@ -176,7 +179,12 @@ class DiskCommon(object):
def _dir_exists_read_metadata(self):
self._dir_exists = do_exists(self.datadir)
if self._dir_exists:
- self.metadata = _read_metadata(self.datadir)
+ try:
+ self.metadata = _read_metadata(self.datadir)
+ except GlusterFileSystemIOError as err:
+ if err.errno in (errno.ENOENT, errno.ESTALE):
+ return False
+ raise
return self._dir_exists
def is_deleted(self):
@@ -195,12 +203,41 @@ class DiskCommon(object):
except FileOrDirNotFoundError:
return True
- def update_metadata(self, metadata):
+ def validate_metadata(self, metadata):
+ """
+ Validates that metadata falls within acceptable limits.
+
+ :param metadata: to be validated
+ :raises: HTTPBadRequest if MAX_META_COUNT or MAX_META_OVERALL_SIZE
+ is exceeded
+ """
+ meta_count = 0
+ meta_size = 0
+ for key, (value, timestamp) in metadata.iteritems():
+ key = key.lower()
+ if value != '' and (key.startswith('x-account-meta') or
+ key.startswith('x-container-meta')):
+ prefix = 'x-account-meta-'
+ if key.startswith('x-container-meta-'):
+ prefix = 'x-container-meta-'
+ key = key[len(prefix):]
+ meta_count = meta_count + 1
+ meta_size = meta_size + len(key) + len(value)
+ if meta_count > MAX_META_COUNT:
+ raise HTTPBadRequest('Too many metadata items; max %d'
+ % MAX_META_COUNT)
+ if meta_size > MAX_META_OVERALL_SIZE:
+ raise HTTPBadRequest('Total metadata too large; max %d'
+ % MAX_META_OVERALL_SIZE)
+
+ def update_metadata(self, metadata, validate_metadata=False):
assert self.metadata, "Valid container/account metadata should have " \
"been created by now"
if metadata:
new_metadata = self.metadata.copy()
new_metadata.update(metadata)
+ if validate_metadata:
+ self.validate_metadata(new_metadata)
if new_metadata != self.metadata:
write_metadata(self.datadir, new_metadata)
self.metadata = new_metadata
@@ -362,7 +399,15 @@ class DiskDir(DiskCommon):
count = 0
for obj in objects:
obj_path = os.path.join(self.datadir, obj)
- metadata = read_metadata(obj_path)
+ try:
+ metadata = read_metadata(obj_path)
+ except GlusterFileSystemIOError as err:
+ if err.errno in (errno.ENOENT, errno.ESTALE):
+ # obj might have been deleted by another process
+ # since the objects list was originally built
+ continue
+ else:
+ raise err
if not metadata or not validate_object(metadata):
if delimiter == '/' and obj_path[-1] == delimiter:
clean_obj_path = obj_path[:-1]
@@ -373,7 +418,7 @@ class DiskDir(DiskCommon):
except OSError as e:
# FIXME - total hack to get upstream swift ported unit
# test cases working for now.
- if e.errno != errno.ENOENT:
+ if e.errno not in (errno.ENOENT, errno.ESTALE):
raise
if not Glusterfs._implicit_dir_objects and metadata \
and metadata[X_CONTENT_TYPE] == DIR_TYPE \
@@ -450,7 +495,7 @@ class DiskDir(DiskCommon):
# If we create it, ensure we own it.
do_chown(self.datadir, self.uid, self.gid)
metadata = get_container_metadata(self.datadir)
- metadata[X_TIMESTAMP] = timestamp
+ metadata[X_TIMESTAMP] = (timestamp, 0)
write_metadata(self.datadir, metadata)
self.metadata = metadata
self._dir_exists = True
@@ -562,7 +607,7 @@ class DiskAccount(DiskCommon):
:param metadata: Metadata to write.
"""
metadata = get_account_metadata(self.datadir)
- metadata[X_TIMESTAMP] = timestamp
+ metadata[X_TIMESTAMP] = (timestamp, 0)
write_metadata(self.datadir, metadata)
self.metadata = metadata
@@ -672,7 +717,7 @@ class DiskAccount(DiskCommon):
except OSError as e:
# FIXME - total hack to get upstream swift ported unit
# test cases working for now.
- if e.errno != errno.ENOENT:
+ if e.errno not in (errno.ENOENT, errno.ESTALE):
raise
if metadata:
list_item.append(metadata[X_OBJECTS_COUNT][0])
diff --git a/gluster/swift/common/Glusterfs.py b/gluster/swift/common/Glusterfs.py
index 5d2cab1..0098bff 100644
--- a/gluster/swift/common/Glusterfs.py
+++ b/gluster/swift/common/Glusterfs.py
@@ -37,7 +37,6 @@ _allow_mount_per_server = False
_implicit_dir_objects = False
_container_update_object_count = False
_account_update_container_count = False
-_ignore_unsupported_headers = False
if _fs_conf.read(os.path.join(SWIFT_DIR, 'fs.conf')):
try:
@@ -98,18 +97,6 @@ if _fs_conf.read(os.path.join(SWIFT_DIR, 'fs.conf')):
except (NoSectionError, NoOptionError):
pass
- # -- Hidden configuration option --
- # Ignore unsupported headers and allow them in a request without
- # returning a 400-BadRequest. This setting can be set to
- # allow unsupported headers such as X-Delete-At and
- # X-Delete-After even though they will not be used.
- try:
- _ignore_unsupported_headers = \
- _fs_conf.get('DEFAULT',
- 'ignore_unsupported_headers',
- "no") in TRUE_VALUES
- except (NoSectionError, NoOptionError):
- pass
NAME = 'glusterfs'
diff --git a/gluster/swift/common/constraints.py b/gluster/swift/common/constraints.py
index 80616f2..7979b43 100644
--- a/gluster/swift/common/constraints.py
+++ b/gluster/swift/common/constraints.py
@@ -14,16 +14,12 @@
# limitations under the License.
import os
-try:
- from webob.exc import HTTPBadRequest
-except ImportError:
- from swift.common.swob import HTTPBadRequest
+from swift.common.swob import HTTPBadRequest
import swift.common.constraints
import swift.common.ring as _ring
from gluster.swift.common import Glusterfs, ring
MAX_OBJECT_NAME_COMPONENT_LENGTH = 255
-UNSUPPORTED_HEADERS = []
def set_object_name_component_length(len=None):
@@ -56,40 +52,8 @@ def validate_obj_name_component(obj):
return ''
-def validate_headers(req):
- """
- Validate client header requests
- :param req: Http request
- """
- if not Glusterfs._ignore_unsupported_headers:
- for unsupported_header in UNSUPPORTED_HEADERS:
- if unsupported_header in req.headers:
- return '%s headers are not supported' \
- % ','.join(UNSUPPORTED_HEADERS)
- return ''
-
# Save the original check object creation
__check_object_creation = swift.common.constraints.check_object_creation
-__check_metadata = swift.common.constraints.check_metadata
-
-
-def gluster_check_metadata(req, target_type, POST=True):
- """
- :param req: HTTP request object
- :param target_type: Value from POST passed to __check_metadata
- :param POST: Only call __check_metadata on POST since Swift only
- calls check_metadata on POSTs.
- """
- ret = None
- if POST:
- ret = __check_metadata(req, target_type)
- if ret is None:
- bdy = validate_headers(req)
- if bdy:
- ret = HTTPBadRequest(body=bdy,
- request=req,
- content_type='text/plain')
- return ret
# Define our new one which invokes the original
@@ -119,14 +83,11 @@ def gluster_check_object_creation(req, object_name):
ret = HTTPBadRequest(body=bdy,
request=req,
content_type='text/plain')
- if ret is None:
- ret = gluster_check_metadata(req, 'object', POST=False)
return ret
# Replace the original checks with ours
swift.common.constraints.check_object_creation = gluster_check_object_creation
-swift.common.constraints.check_metadata = gluster_check_metadata
# Replace the original check mount with ours
swift.common.constraints.check_mount = Glusterfs.mount
diff --git a/gluster/swift/common/fs_utils.py b/gluster/swift/common/fs_utils.py
index 9f698df..e0fa7ce 100644
--- a/gluster/swift/common/fs_utils.py
+++ b/gluster/swift/common/fs_utils.py
@@ -162,7 +162,7 @@ def dir_empty(path):
files = do_listdir(path)
return not files
except GlusterFileSystemOSError as err:
- if err.errno == errno.ENOENT:
+ if err.errno in (errno.ENOENT, errno.ESTALE):
raise FileOrDirNotFoundError()
if err.errno == errno.ENOTDIR:
raise NotDirectoryError()
@@ -210,7 +210,7 @@ def do_stat(path):
serr = err
sleep(random.uniform(0.001, 0.005))
continue
- if err.errno == errno.ENOENT:
+ if err.errno in (errno.ENOENT, errno.ESTALE):
stats = None
else:
raise GlusterFileSystemOSError(
@@ -268,10 +268,10 @@ def do_unlink(path, log=True):
try:
os.unlink(path)
except OSError as err:
- if err.errno != errno.ENOENT:
+ if err.errno not in (errno.ENOENT, errno.ESTALE):
raise GlusterFileSystemOSError(
err.errno, '%s, os.unlink("%s")' % (err.strerror, path))
- elif log:
+ else:
logging.warn("fs_utils: os.unlink failed on non-existent path: %s",
path)
diff --git a/gluster/swift/common/middleware/gswauth/swauth/middleware.py b/gluster/swift/common/middleware/gswauth/swauth/middleware.py
index 314eedb..cdcc638 100644
--- a/gluster/swift/common/middleware/gswauth/swauth/middleware.py
+++ b/gluster/swift/common/middleware/gswauth/swauth/middleware.py
@@ -241,7 +241,7 @@ class Swauth(object):
version, rest = split_path(env.get('PATH_INFO', ''),
1, 2, True)
except ValueError:
- version, rest = None, None
+ rest = None
if rest and rest.startswith(self.reseller_prefix):
# Handle anonymous access to accounts I'm the definitive
# auth for.
@@ -693,7 +693,7 @@ class Swauth(object):
return HTTPBadRequest(request=req)
try:
new_services = json.loads(req.body)
- except ValueError, err:
+ except ValueError as err:
return HTTPBadRequest(body=str(err))
# Get the current services information
path = quote('/v1/%s/%s/.services' % (self.auth_account, account))
@@ -1405,7 +1405,7 @@ class Swauth(object):
memcache_key,
(self.itoken_expires,
'%s,.reseller_admin,%s' % (self.metadata_volume,
- self.auth_account)),
+ self.auth_account)),
timeout=self.token_life)
return self.itoken
@@ -1589,19 +1589,20 @@ class Swauth(object):
if getattr(req, 'client_disconnect', False) or \
getattr(response, 'client_disconnect', False):
status_int = 499
- self.logger.info(
- ' '.join(quote(str(x)) for x in (client or '-',
- req.remote_addr or '-', strftime('%d/%b/%Y/%H/%M/%S', gmtime()),
- req.method, the_request, req.environ['SERVER_PROTOCOL'],
- status_int, req.referer or '-', req.user_agent or '-',
- req.headers.get(
- 'x-auth-token',
- req.headers.get('x-auth-admin-user', '-')),
- getattr(req, 'bytes_transferred', 0) or '-',
- getattr(response, 'bytes_transferred', 0) or '-',
- req.headers.get('etag', '-'),
- req.headers.get('x-trans-id', '-'), logged_headers or '-',
- trans_time)))
+ self.logger.info(' '.join(quote(str(x)) for x in
+ (client or '-',
+ req.remote_addr or '-', strftime('%d/%b/%Y/%H/%M/%S',
+ gmtime()),
+ req.method, the_request,
+ req.environ['SERVER_PROTOCOL'], status_int,
+ req.referer or '-', req.user_agent or '-',
+ req.headers.get('x-auth-token', req.headers.get(
+ 'x-auth-admin-user', '-')),
+ getattr(req, 'bytes_transferred', 0) or '-',
+ getattr(response, 'bytes_transferred', 0) or '-',
+ req.headers.get('etag', '-'),
+ req.headers.get('x-trans-id', '-'), logged_headers
+ or '-', trans_time)))
def filter_factory(global_conf, **local_conf):
diff --git a/gluster/swift/common/middleware/gswauth/test_swauth/__init__.py b/gluster/swift/common/middleware/gswauth/test_swauth/__init__.py
deleted file mode 100644
index f53bc5a..0000000
--- a/gluster/swift/common/middleware/gswauth/test_swauth/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-# See http://code.google.com/p/python-nose/issues/detail?id=373
-# The code below enables nosetests to work with i18n _() blocks
-
-import __builtin__
-
-setattr(__builtin__, '_', lambda x: x)
diff --git a/gluster/swift/common/middleware/gswauth/test_swauth/unit/test_authtypes.py b/gluster/swift/common/middleware/gswauth/test_swauth/unit/test_authtypes.py
deleted file mode 100644
index d9b7b55..0000000
--- a/gluster/swift/common/middleware/gswauth/test_swauth/unit/test_authtypes.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# 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.
-#
-# Pablo Llopis 2011
-
-import unittest
-from swauth import authtypes
-
-
-class TestPlaintext(unittest.TestCase):
-
- def setUp(self):
- self.auth_encoder = authtypes.Plaintext()
-
- def test_plaintext_encode(self):
- enc_key = self.auth_encoder.encode('keystring')
- self.assertEquals('plaintext:keystring', enc_key)
-
- def test_plaintext_valid_match(self):
- creds = 'plaintext:keystring'
- match = self.auth_encoder.match('keystring', creds)
- self.assertEquals(match, True)
-
- def test_plaintext_invalid_match(self):
- creds = 'plaintext:other-keystring'
- match = self.auth_encoder.match('keystring', creds)
- self.assertEquals(match, False)
-
-
-class TestSha1(unittest.TestCase):
-
- def setUp(self):
- self.auth_encoder = authtypes.Sha1()
- self.auth_encoder.salt = 'salt'
-
- def test_sha1_encode(self):
- enc_key = self.auth_encoder.encode('keystring')
- self.assertEquals('sha1:salt$d50dc700c296e23ce5b41f7431a0e01f69010f06',
- enc_key)
-
- def test_sha1_valid_match(self):
- creds = 'sha1:salt$d50dc700c296e23ce5b41f7431a0e01f69010f06'
- match = self.auth_encoder.match('keystring', creds)
- self.assertEquals(match, True)
-
- def test_sha1_invalid_match(self):
- creds = 'sha1:salt$deadbabedeadbabedeadbabec0ffeebadc0ffeee'
- match = self.auth_encoder.match('keystring', creds)
- self.assertEquals(match, False)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/gluster/swift/common/middleware/gswauth/test_swauth/unit/test_middleware.py b/gluster/swift/common/middleware/gswauth/test_swauth/unit/test_middleware.py
deleted file mode 100644
index 62259ff..0000000
--- a/gluster/swift/common/middleware/gswauth/test_swauth/unit/test_middleware.py
+++ /dev/null
@@ -1,4519 +0,0 @@
-# Copyright (c) 2010-2011 OpenStack, LLC.
-#
-# 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.
-
-try:
- import simplejson as json
-except ImportError:
- import json
-import unittest
-from contextlib import contextmanager
-from time import time
-
-from swift.common.swob import Request, Response
-
-from swauth import middleware as auth
-from swauth.authtypes import MAX_TOKEN_LENGTH
-
-
-DEFAULT_TOKEN_LIFE = 86400
-MAX_TOKEN_LIFE = 100000
-
-
-class FakeMemcache(object):
-
- def __init__(self):
- self.store = {}
-
- def get(self, key):
- return self.store.get(key)
-
- def set(self, key, value, timeout=0, time=0):
- self.store[key] = value
- return True
-
- def incr(self, key, timeout=0, time=0):
- self.store[key] = self.store.setdefault(key, 0) + 1
- return self.store[key]
-
- @contextmanager
- def soft_lock(self, key, timeout=0, retries=5, time=0):
- yield True
-
- def delete(self, key):
- try:
- del self.store[key]
- except Exception:
- pass
- return True
-
-
-class FakeApp(object):
-
- def __init__(
- self, status_headers_body_iter=None, acl=None, sync_key=None):
- self.calls = 0
- self.status_headers_body_iter = status_headers_body_iter
- if not self.status_headers_body_iter:
- self.status_headers_body_iter = iter(
- [('404 Not Found', {}, '')])
- self.acl = acl
- self.sync_key = sync_key
-
- def __call__(self, env, start_response):
- self.calls += 1
- self.request = Request.blank('', environ=env)
- if self.acl:
- self.request.acl = self.acl
- if self.sync_key:
- self.request.environ[
- 'swift_sync_key'] = self.sync_key
- if 'swift.authorize' in env:
- resp = env['swift.authorize'](self.request)
- if resp:
- return resp(env, start_response)
- status, headers, body = self.status_headers_body_iter.next(
- )
- return Response(status=status, headers=headers,
- body=body)(env, start_response)
-
-
-class FakeConn(object):
-
- def __init__(self, status_headers_body_iter=None):
- self.calls = 0
- self.status_headers_body_iter = status_headers_body_iter
- if not self.status_headers_body_iter:
- self.status_headers_body_iter = iter(
- [('404 Not Found', {}, '')])
-
- def request(self, method, path, headers):
- self.calls += 1
- self.request_path = path
- self.status, self.headers, self.body = \
- self.status_headers_body_iter.next()
- self.status, self.reason = self.status.split(' ', 1)
- self.status = int(self.status)
-
- def getresponse(self):
- return self
-
- def read(self):
- body = self.body
- self.body = ''
- return body
-
-
-class TestAuth(unittest.TestCase):
-
- def setUp(self):
- self.test_auth = \
- auth.filter_factory({
- 'super_admin_key': 'supertest',
- 'token_life': str(DEFAULT_TOKEN_LIFE),
- 'max_token_life': str(MAX_TOKEN_LIFE)})(FakeApp())
-
- def test_super_admin_key_not_required(self):
- auth.filter_factory({})(FakeApp())
-
- def test_reseller_prefix_init(self):
- app = FakeApp()
- ath = auth.filter_factory(
- {'super_admin_key': 'supertest'})(app)
- self.assertEquals(ath.reseller_prefix, 'AUTH_')
- ath = auth.filter_factory(
- {'super_admin_key': 'supertest',
- 'reseller_prefix': 'TEST'})(app)
- self.assertEquals(ath.reseller_prefix, 'TEST_')
- ath = auth.filter_factory(
- {'super_admin_key': 'supertest',
- 'reseller_prefix': 'TEST_'})(app)
- self.assertEquals(ath.reseller_prefix, 'TEST_')
-
- def test_auth_prefix_init(self):
- app = FakeApp()
- ath = auth.filter_factory(
- {'super_admin_key': 'supertest'})(app)
- self.assertEquals(ath.auth_prefix, '/auth/')
- ath = auth.filter_factory(
- {'super_admin_key': 'supertest',
- 'auth_prefix': ''})(app)
- self.assertEquals(ath.auth_prefix, '/auth/')
- ath = auth.filter_factory(
- {'super_admin_key': 'supertest',
- 'auth_prefix': '/test/'})(app)
- self.assertEquals(ath.auth_prefix, '/test/')
- ath = auth.filter_factory(
- {'super_admin_key': 'supertest',
- 'auth_prefix': '/test'})(app)
- self.assertEquals(ath.auth_prefix, '/test/')
- ath = auth.filter_factory(
- {'super_admin_key': 'supertest',
- 'auth_prefix': 'test/'})(app)
- self.assertEquals(ath.auth_prefix, '/test/')
- ath = auth.filter_factory(
- {'super_admin_key': 'supertest',
- 'auth_prefix': 'test'})(app)
- self.assertEquals(ath.auth_prefix, '/test/')
-
- def test_no_auth_type_init(self):
- app = FakeApp()
- ath = auth.filter_factory({})(app)
- self.assertEquals(ath.auth_type, 'Plaintext')
-
- def test_valid_auth_type_init(self):
- app = FakeApp()
- ath = auth.filter_factory(
- {'auth_type': 'sha1'})(app)
- self.assertEquals(ath.auth_type, 'Sha1')
- ath = auth.filter_factory(
- {'auth_type': 'plaintext'})(app)
- self.assertEquals(ath.auth_type, 'Plaintext')
-
- def test_invalid_auth_type_init(self):
- app = FakeApp()
- exc = None
- try:
- auth.filter_factory(
- {'auth_type': 'NONEXISTANT'})(app)
- except Exception as err:
- exc = err
- self.assertEquals(str(exc),
- 'Invalid auth_type in config file: %s' %
- 'Nonexistant')
-
- def test_default_swift_cluster_init(self):
- app = FakeApp()
- self.assertRaises(Exception, auth.filter_factory({
- 'super_admin_key': 'supertest',
- 'default_swift_cluster': 'local#badscheme://host/path'}), app)
- ath = auth.filter_factory(
- {'super_admin_key': 'supertest'})(app)
- self.assertEquals(ath.default_swift_cluster,
- 'local#http://127.0.0.1:8080/v1')
- ath = auth.filter_factory({
- 'super_admin_key': 'supertest',
- 'default_swift_cluster': 'local#http://host/path'})(app)
- self.assertEquals(ath.default_swift_cluster,
- 'local#http://host/path')
- ath = auth.filter_factory({
- 'super_admin_key': 'supertest',
- 'default_swift_cluster': 'local#https://host/path/'})(app)
- self.assertEquals(ath.dsc_url, 'https://host/path')
- self.assertEquals(ath.dsc_url2, 'https://host/path')
- ath = auth.filter_factory({
- 'super_admin_key': 'supertest',
- 'default_swift_cluster':
- 'local#https://host/path/#http://host2/path2/'})(app)
- self.assertEquals(ath.dsc_url, 'https://host/path')
- self.assertEquals(
- ath.dsc_url2,
- 'http://host2/path2')
-
- def test_top_level_denied(self):
- resp = Request.blank(
- '/').get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
-
- def test_anon(self):
- resp = Request.blank(
- '/v1/AUTH_account').get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
- self.assertEquals(resp.environ['swift.authorize'],
- self.test_auth.authorize)
-
- def test_auth_deny_non_reseller_prefix(self):
- resp = Request.blank(
- '/v1/BLAH_account',
- headers={'X-Auth-Token': 'BLAH_t'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
- self.assertEquals(resp.environ['swift.authorize'],
- self.test_auth.denied_response)
-
- def test_auth_deny_non_reseller_prefix_no_override(
- self):
- fake_authorize = lambda x: Response(
- status='500 Fake')
- resp = Request.blank(
- '/v1/BLAH_account',
- headers={'X-Auth-Token': 'BLAH_t'},
- environ={'swift.authorize': fake_authorize}).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(resp.environ['swift.authorize'], fake_authorize)
-
- def test_auth_no_reseller_prefix_deny(self):
- # Ensures that when we have no reseller prefix, we don't deny a request
- # outright but set up a denial swift.authorize and pass the request on
- # down the chain.
- local_app = FakeApp()
- local_auth = auth.filter_factory(
- {'super_admin_key': 'supertest',
- 'reseller_prefix': ''})(local_app)
- resp = Request.blank(
- '/v1/account',
- headers={'X-Auth-Token': 't'}).get_response(local_auth)
- self.assertEquals(resp.status_int, 401)
- # one for checking auth, two for request passed
- # along
- self.assertEquals(local_app.calls, 2)
- self.assertEquals(resp.environ['swift.authorize'],
- local_auth.denied_response)
-
- def test_auth_no_reseller_prefix_allow(self):
- # Ensures that when we have no reseller prefix, we can still allow
- # access if our auth server accepts requests
- local_app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps(
- {'account': 'act', 'user': 'act:usr',
- 'account_id': 'AUTH_cfa',
- 'groups': [{'name': 'act:usr'}, {'name': 'act'},
- {'name': '.admin'}],
- 'expires': time() + 60})),
- ('204 No Content', {}, '')]))
- local_auth = auth.filter_factory(
- {'super_admin_key': 'supertest',
- 'reseller_prefix': ''})(local_app)
- resp = Request.blank(
- '/v1/act',
- headers={'X-Auth-Token': 't'}).get_response(local_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(local_app.calls, 2)
- self.assertEquals(resp.environ['swift.authorize'],
- local_auth.authorize)
-
- def test_auth_no_reseller_prefix_no_token(self):
- # Check that normally we set up a call back to our
- # authorize.
- local_auth = \
- auth.filter_factory(
- {'super_admin_key': 'supertest',
- 'reseller_prefix': ''})(FakeApp(iter([])))
- resp = Request.blank(
- '/v1/account').get_response(
- local_auth)
- self.assertEquals(resp.status_int, 401)
- self.assertEquals(
- resp.environ['swift.authorize'], local_auth.authorize)
- # Now make sure we don't override an existing swift.authorize when we
- # have no reseller prefix.
- local_auth = \
- auth.filter_factory(
- {'super_admin_key': 'supertest',
- 'reseller_prefix': ''})(FakeApp())
- local_authorize = lambda req: Response('test')
- resp = Request.blank(
- '/v1/account', environ={'swift.authorize':
- local_authorize}).get_response(local_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertEquals(
- resp.environ['swift.authorize'],
- local_authorize)
-
- def test_auth_fail(self):
- resp = Request.blank(
- '/v1/AUTH_cfa',
- headers={'X-Auth-Token': 'AUTH_t'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
-
- def test_auth_success(self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps(
- {'account': 'act', 'user': 'act:usr',
- 'account_id': 'AUTH_cfa',
- 'groups': [{'name': 'act:usr'}, {'name': 'act'},
- {'name': '.admin'}],
- 'expires': time() + 60})),
- ('204 No Content', {}, '')]))
- resp = Request.blank(
- '/v1/AUTH_cfa',
- headers={'X-Auth-Token': 'AUTH_t'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_auth_memcache(self):
- # First run our test without memcache, showing we need to return the
- # token contents twice.
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps(
- {'account': 'act', 'user': 'act:usr',
- 'account_id': 'AUTH_cfa',
- 'groups': [{'name': 'act:usr'}, {'name': 'act'},
- {'name': '.admin'}],
- 'expires': time() + 60})),
- ('204 No Content', {}, ''),
- ('200 Ok', {},
- json.dumps(
- {'account': 'act', 'user': 'act:usr',
- 'account_id': 'AUTH_cfa',
- 'groups': [{'name': 'act:usr'}, {'name': 'act'},
- {'name': '.admin'}],
- 'expires': time() + 60})),
- ('204 No Content', {}, '')]))
- resp = Request.blank(
- '/v1/AUTH_cfa',
- headers={'X-Auth-Token': 'AUTH_t'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- resp = Request.blank(
- '/v1/AUTH_cfa',
- headers={'X-Auth-Token': 'AUTH_t'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(self.test_auth.app.calls, 4)
- # Now run our test with memcache, showing we no longer need to return
- # the token contents twice.
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps(
- {'account': 'act', 'user': 'act:usr',
- 'account_id': 'AUTH_cfa',
- 'groups': [{'name': 'act:usr'}, {'name': 'act'},
- {'name': '.admin'}],
- 'expires': time() + 60})),
- ('204 No Content', {}, ''),
- # Don't need a second token object returned if memcache is
- # used
- ('204 No Content', {}, '')]))
- fake_memcache = FakeMemcache()
- resp = Request.blank(
- '/v1/AUTH_cfa',
- headers={'X-Auth-Token': 'AUTH_t'},
- environ={'swift.cache': fake_memcache}).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 204)
- resp = Request.blank(
- '/v1/AUTH_cfa',
- headers={'X-Auth-Token': 'AUTH_t'},
- environ={'swift.cache': fake_memcache}).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(self.test_auth.app.calls, 3)
-
- def test_auth_just_expired(self):
- self.test_auth.app = FakeApp(iter([
- # Request for token (which will have expired)
- ('200 Ok', {},
- json.dumps(
- {'account': 'act', 'user': 'act:usr',
- 'account_id': 'AUTH_cfa',
- 'groups': [{'name': 'act:usr'}, {'name': 'act'},
- {'name': '.admin'}],
- 'expires': time() - 1})),
- # Request to delete token
- ('204 No Content', {}, '')]))
- resp = Request.blank(
- '/v1/AUTH_cfa',
- headers={'X-Auth-Token': 'AUTH_t'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_middleware_storage_token(self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps(
- {'account': 'act', 'user': 'act:usr',
- 'account_id': 'AUTH_cfa',
- 'groups': [{'name': 'act:usr'}, {'name': 'act'},
- {'name': '.admin'}],
- 'expires': time() + 60})),
- ('204 No Content', {}, '')]))
- resp = Request.blank(
- '/v1/AUTH_cfa',
- headers={'X-Storage-Token': 'AUTH_t'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_authorize_bad_path(self):
- req = Request.blank('/badpath')
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 401)
- req = Request.blank('/badpath')
- req.remote_user = 'act:usr,act,AUTH_cfa'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
-
- def test_authorize_account_access(self):
- req = Request.blank('/v1/AUTH_cfa')
- req.remote_user = 'act:usr,act,AUTH_cfa'
- self.assertEquals(
- self.test_auth.authorize(req),
- None)
- req = Request.blank('/v1/AUTH_cfa')
- req.remote_user = 'act:usr,act'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
-
- def test_authorize_acl_group_access(self):
- req = Request.blank('/v1/AUTH_cfa')
- req.remote_user = 'act:usr,act'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
- req = Request.blank('/v1/AUTH_cfa')
- req.remote_user = 'act:usr,act'
- req.acl = 'act'
- self.assertEquals(
- self.test_auth.authorize(req),
- None)
- req = Request.blank('/v1/AUTH_cfa')
- req.remote_user = 'act:usr,act'
- req.acl = 'act:usr'
- self.assertEquals(
- self.test_auth.authorize(req),
- None)
- req = Request.blank('/v1/AUTH_cfa')
- req.remote_user = 'act:usr,act'
- req.acl = 'act2'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
- req = Request.blank('/v1/AUTH_cfa')
- req.remote_user = 'act:usr,act'
- req.acl = 'act:usr2'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
-
- def test_deny_cross_reseller(self):
- # Tests that cross-reseller is denied, even if ACLs/group
- # names match
- req = Request.blank('/v1/OTHER_cfa')
- req.remote_user = 'act:usr,act,AUTH_cfa'
- req.acl = 'act'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
-
- def test_authorize_acl_referrer_access(self):
- req = Request.blank('/v1/AUTH_cfa/c')
- req.remote_user = 'act:usr,act'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
- req = Request.blank('/v1/AUTH_cfa/c')
- req.remote_user = 'act:usr,act'
- req.acl = '.r:*,.rlistings'
- self.assertEquals(
- self.test_auth.authorize(req),
- None)
- req = Request.blank('/v1/AUTH_cfa/c')
- req.remote_user = 'act:usr,act'
- req.acl = '.r:*' # No listings allowed
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
- req = Request.blank('/v1/AUTH_cfa/c')
- req.remote_user = 'act:usr,act'
- req.acl = '.r:.example.com,.rlistings'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
- req = Request.blank('/v1/AUTH_cfa/c')
- req.remote_user = 'act:usr,act'
- req.referer = 'http://www.example.com/index.html'
- req.acl = '.r:.example.com,.rlistings'
- self.assertEquals(
- self.test_auth.authorize(req),
- None)
- req = Request.blank('/v1/AUTH_cfa/c')
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 401)
- req = Request.blank('/v1/AUTH_cfa/c')
- req.acl = '.r:*,.rlistings'
- self.assertEquals(
- self.test_auth.authorize(req),
- None)
- req = Request.blank('/v1/AUTH_cfa/c')
- req.acl = '.r:*' # No listings allowed
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 401)
- req = Request.blank('/v1/AUTH_cfa/c')
- req.acl = '.r:.example.com,.rlistings'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 401)
- req = Request.blank('/v1/AUTH_cfa/c')
- req.referer = 'http://www.example.com/index.html'
- req.acl = '.r:.example.com,.rlistings'
- self.assertEquals(
- self.test_auth.authorize(req),
- None)
-
- def test_detect_reseller_request(self):
- req = self._make_request('/v1/AUTH_admin',
- headers={'X-Auth-Token': 'AUTH_t'})
- cache_key = 'AUTH_/auth/AUTH_t'
- cache_entry = (time() + 3600, '.reseller_admin')
- req.environ['swift.cache'].set(
- cache_key, cache_entry)
- self.assertTrue(req.environ.get('reseller_request'))
-
- def test_account_put_permissions(self):
- req = Request.blank(
- '/v1/AUTH_new',
- environ={'REQUEST_METHOD': 'PUT'})
- req.remote_user = 'act:usr,act'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
-
- req = Request.blank(
- '/v1/AUTH_new',
- environ={'REQUEST_METHOD': 'PUT'})
- req.remote_user = 'act:usr,act,AUTH_other'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
-
- # Even PUTs to your own account as account admin
- # should fail
- req = Request.blank(
- '/v1/AUTH_old',
- environ={'REQUEST_METHOD': 'PUT'})
- req.remote_user = 'act:usr,act,AUTH_old'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
-
- req = Request.blank(
- '/v1/AUTH_new',
- environ={'REQUEST_METHOD': 'PUT'})
- req.remote_user = 'act:usr,act,.reseller_admin'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp, None)
-
- # .super_admin is not something the middleware should ever see or care
- # about
- req = Request.blank(
- '/v1/AUTH_new',
- environ={'REQUEST_METHOD': 'PUT'})
- req.remote_user = 'act:usr,act,.super_admin'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
-
- def test_account_delete_permissions(self):
- req = Request.blank('/v1/AUTH_new',
- environ={'REQUEST_METHOD': 'DELETE'})
- req.remote_user = 'act:usr,act'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
-
- req = Request.blank('/v1/AUTH_new',
- environ={'REQUEST_METHOD': 'DELETE'})
- req.remote_user = 'act:usr,act,AUTH_other'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
-
- # Even DELETEs to your own account as account admin should
- # fail
- req = Request.blank('/v1/AUTH_old',
- environ={'REQUEST_METHOD': 'DELETE'})
- req.remote_user = 'act:usr,act,AUTH_old'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
-
- req = Request.blank('/v1/AUTH_new',
- environ={'REQUEST_METHOD': 'DELETE'})
- req.remote_user = 'act:usr,act,.reseller_admin'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp, None)
-
- # .super_admin is not something the middleware should ever see or care
- # about
- req = Request.blank('/v1/AUTH_new',
- environ={'REQUEST_METHOD': 'DELETE'})
- req.remote_user = 'act:usr,act,.super_admin'
- resp = self.test_auth.authorize(req)
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
-
- def test_get_token_fail(self):
- resp = Request.blank(
- '/auth/v1.0').get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 401)
- resp = Request.blank(
- '/auth/v1.0',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
-
- def test_get_token_fail_invalid_key(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]}))]))
- resp = Request.blank(
- '/auth/v1.0',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'invalid'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_get_token_fail_invalid_x_auth_user_format(
- self):
- resp = Request.blank(
- '/auth/v1/act/auth',
- headers={'X-Auth-User': 'usr',
- 'X-Auth-Key': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
-
- def test_get_token_fail_non_matching_account_in_request(
- self):
- resp = Request.blank(
- '/auth/v1/act/auth',
- headers={'X-Auth-User': 'act2:usr',
- 'X-Auth-Key': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
-
- def test_get_token_fail_bad_path(self):
- resp = Request.blank(
- '/auth/v1/act/auth/invalid',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
-
- def test_get_token_fail_missing_key(self):
- resp = Request.blank(
- '/auth/v1/act/auth',
- headers={'X-Auth-User': 'act:usr'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
-
- def test_get_token_fail_get_user_details(self):
- self.test_auth.app = FakeApp(iter([
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank(
- '/auth/v1.0',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_get_token_fail_get_account(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]})),
- # GET of account
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank(
- '/auth/v1.0',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_get_token_fail_put_new_token(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]})),
- # GET of account
- ('204 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of new token
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank(
- '/auth/v1.0',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 3)
-
- def test_get_token_fail_post_to_user(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]})),
- # GET of account
- ('204 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of new token
- ('201 Created', {}, ''),
- # POST of token to user object
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank(
- '/auth/v1.0',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 4)
-
- def test_get_token_fail_get_services(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]})),
- # GET of account
- ('204 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of new token
- ('201 Created', {}, ''),
- # POST of token to user object
- ('204 No Content', {}, ''),
- # GET of services object
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank(
- '/auth/v1.0',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 5)
-
- def test_get_token_fail_get_existing_token(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {'X-Object-Meta-Auth-Token': 'AUTH_tktest'},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]})),
- # GET of token
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank(
- '/auth/v1.0',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_get_token_success_v1_0(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]})),
- # GET of account
- ('204 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of new token
- ('201 Created', {}, ''),
- # POST of token to user object
- ('204 No Content', {}, ''),
- # GET of services object
- ('200 Ok', {}, json.dumps({"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
- resp = Request.blank(
- '/auth/v1.0',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assert_(resp.headers.get(
- 'x-auth-token',
- '').startswith('AUTH_tk'), resp.headers.get('x-auth-token'))
- self.assertEquals(resp.headers.get('x-auth-token'),
- resp.headers.get('x-storage-token'))
- self.assertEquals(resp.headers.get('x-storage-url'),
- 'http://127.0.0.1:8080/v1/AUTH_cfa')
- self.assertEquals(
- json.loads(resp.body),
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})
- self.assertEquals(self.test_auth.app.calls, 5)
-
- def test_get_token_success_v1_0_with_user_token_life(
- self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]})),
- # GET of account
- ('204 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of new token
- ('201 Created', {}, ''),
- # POST of token to user object
- ('204 No Content', {}, ''),
- # GET of services object
- ('200 Ok', {}, json.dumps({"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
- resp = Request.blank(
- '/auth/v1.0',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'key',
- 'X-Auth-Token-Lifetime': 10}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- left = int(resp.headers['x-auth-token-expires'])
- self.assertTrue(left > 0, '%d > 0' % left)
- self.assertTrue(left <= 10, '%d <= 10' % left)
- self.assert_(resp.headers.get(
- 'x-auth-token',
- '').startswith('AUTH_tk'), resp.headers.get('x-auth-token'))
- self.assertEquals(resp.headers.get('x-auth-token'),
- resp.headers.get('x-storage-token'))
- self.assertEquals(resp.headers.get('x-storage-url'),
- 'http://127.0.0.1:8080/v1/AUTH_cfa')
- self.assertEquals(
- json.loads(resp.body),
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})
- self.assertEquals(self.test_auth.app.calls, 5)
-
- def test_get_token_success_v1_0_with_user_token_life_past_max(
- self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]})),
- # GET of account
- ('204 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of new token
- ('201 Created', {}, ''),
- # POST of token to user object
- ('204 No Content', {}, ''),
- # GET of services object
- ('200 Ok', {}, json.dumps({"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
- req = Request.blank(
- '/auth/v1.0',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'key',
- 'X-Auth-Token-Lifetime': MAX_TOKEN_LIFE * 10})
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- left = int(resp.headers['x-auth-token-expires'])
- self.assertTrue(left > DEFAULT_TOKEN_LIFE,
- '%d > %d' % (left, DEFAULT_TOKEN_LIFE))
- self.assertTrue(left <= MAX_TOKEN_LIFE,
- '%d <= %d' % (left, MAX_TOKEN_LIFE))
- self.assert_(resp.headers.get(
- 'x-auth-token',
- '').startswith('AUTH_tk'), resp.headers.get('x-auth-token'))
- self.assertEquals(resp.headers.get('x-auth-token'),
- resp.headers.get('x-storage-token'))
- self.assertEquals(resp.headers.get('x-storage-url'),
- 'http://127.0.0.1:8080/v1/AUTH_cfa')
- self.assertEquals(
- json.loads(resp.body),
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})
- self.assertEquals(self.test_auth.app.calls, 5)
-
- def test_get_token_success_v1_act_auth(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]})),
- # GET of account
- ('204 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of new token
- ('201 Created', {}, ''),
- # POST of token to user object
- ('204 No Content', {}, ''),
- # GET of services object
- ('200 Ok', {}, json.dumps({"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
- resp = Request.blank(
- '/auth/v1/act/auth',
- headers={'X-Storage-User': 'usr',
- 'X-Storage-Pass': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assert_(resp.headers.get(
- 'x-auth-token',
- '').startswith('AUTH_tk'), resp.headers.get('x-auth-token'))
- self.assertEquals(resp.headers.get('x-auth-token'),
- resp.headers.get('x-storage-token'))
- self.assertEquals(resp.headers.get('x-storage-url'),
- 'http://127.0.0.1:8080/v1/AUTH_cfa')
- self.assertEquals(
- json.loads(resp.body),
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})
- self.assertEquals(self.test_auth.app.calls, 5)
-
- def test_get_token_success_storage_instead_of_auth(
- self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]})),
- # GET of account
- ('204 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of new token
- ('201 Created', {}, ''),
- # POST of token to user object
- ('204 No Content', {}, ''),
- # GET of services object
- ('200 Ok', {}, json.dumps({"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
- resp = Request.blank(
- '/auth/v1.0',
- headers={'X-Storage-User': 'act:usr',
- 'X-Storage-Pass': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assert_(
- resp.headers.get(
- 'x-auth-token',
- '').startswith('AUTH_tk'), resp.headers.get('x-auth-token'))
- self.assertEquals(resp.headers.get('x-auth-token'),
- resp.headers.get('x-storage-token'))
- self.assertEquals(resp.headers.get('x-storage-url'),
- 'http://127.0.0.1:8080/v1/AUTH_cfa')
- self.assertEquals(
- json.loads(resp.body),
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})
- self.assertEquals(self.test_auth.app.calls, 5)
-
- def test_get_token_success_v1_act_auth_auth_instead_of_storage(
- self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]})),
- # GET of account
- ('204 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of new token
- ('201 Created', {}, ''),
- # POST of token to user object
- ('204 No Content', {}, ''),
- # GET of services object
- ('200 Ok', {}, json.dumps({"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
- resp = Request.blank(
- '/auth/v1/act/auth',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assert_(resp.headers.get(
- 'x-auth-token',
- '').startswith('AUTH_tk'), resp.headers.get('x-auth-token'))
- self.assertEquals(resp.headers.get('x-auth-token'),
- resp.headers.get('x-storage-token'))
- self.assertEquals(resp.headers.get('x-storage-url'),
- 'http://127.0.0.1:8080/v1/AUTH_cfa')
- self.assertEquals(
- json.loads(resp.body),
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})
- self.assertEquals(self.test_auth.app.calls, 5)
-
- def test_get_token_success_existing_token(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {'X-Object-Meta-Auth-Token': 'AUTH_tktest'},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]})),
- # GET of token
- ('200 Ok', {}, json.dumps(
- {"account": "act", "user": "usr",
- "account_id": "AUTH_cfa",
- "groups": [{'name': "act:usr"},
- {'name': "key"}, {'name': ".admin"}],
- "expires": 9999999999.9999999})),
- # GET of services object
- ('200 Ok', {}, json.dumps({"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
- resp = Request.blank(
- '/auth/v1.0',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertEquals(
- resp.headers.get('x-auth-token'),
- 'AUTH_tktest')
- self.assertEquals(resp.headers.get('x-auth-token'),
- resp.headers.get('x-storage-token'))
- self.assertEquals(resp.headers.get('x-storage-url'),
- 'http://127.0.0.1:8080/v1/AUTH_cfa')
- self.assertEquals(
- json.loads(resp.body),
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})
- self.assertEquals(self.test_auth.app.calls, 3)
-
- def test_get_token_success_existing_token_but_request_new_one(
- self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {'X-Object-Meta-Auth-Token': 'AUTH_tktest'},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]})),
- # DELETE of expired token
- ('204 No Content', {}, ''),
- # GET of account
- ('204 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of new token
- ('201 Created', {}, ''),
- # POST of token to user object
- ('204 No Content', {}, ''),
- # GET of services object
- ('200 Ok', {}, json.dumps({"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
- resp = Request.blank(
- '/auth/v1.0',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'key',
- 'X-Auth-New-Token': 'true'}).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertNotEquals(
- resp.headers.get('x-auth-token'), 'AUTH_tktest')
- self.assertEquals(resp.headers.get('x-auth-token'),
- resp.headers.get('x-storage-token'))
- self.assertEquals(resp.headers.get('x-storage-url'),
- 'http://127.0.0.1:8080/v1/AUTH_cfa')
- self.assertEquals(
- json.loads(resp.body),
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})
- self.assertEquals(self.test_auth.app.calls, 6)
-
- def test_get_token_success_existing_token_expired(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {'X-Object-Meta-Auth-Token': 'AUTH_tktest'},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]})),
- # GET of token
- ('200 Ok', {}, json.dumps(
- {"account": "act", "user": "usr",
- "account_id": "AUTH_cfa",
- "groups": [{'name': "act:usr"},
- {'name': "key"}, {'name': ".admin"}],
- "expires": 0.0})),
- # DELETE of expired token
- ('204 No Content', {}, ''),
- # GET of account
- ('204 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of new token
- ('201 Created', {}, ''),
- # POST of token to user object
- ('204 No Content', {}, ''),
- # GET of services object
- ('200 Ok', {}, json.dumps({"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
- resp = Request.blank(
- '/auth/v1.0',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertNotEquals(
- resp.headers.get('x-auth-token'),
- 'AUTH_tktest')
- self.assertEquals(resp.headers.get('x-auth-token'),
- resp.headers.get('x-storage-token'))
- self.assertEquals(resp.headers.get('x-storage-url'),
- 'http://127.0.0.1:8080/v1/AUTH_cfa')
- self.assertEquals(
- json.loads(resp.body),
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})
- self.assertEquals(self.test_auth.app.calls, 7)
-
- def test_get_token_success_existing_token_expired_fail_deleting_old(
- self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {'X-Object-Meta-Auth-Token': 'AUTH_tktest'},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]})),
- # GET of token
- ('200 Ok', {}, json.dumps({"account": "act", "user": "usr",
- "account_id": "AUTH_cfa",
- "groups": [{'name': "act:usr"},
- {'name': "key"}, {'name': ".admin"}],
- "expires": 0.0})),
- # DELETE of expired token
- ('503 Service Unavailable', {}, ''),
- # GET of account
- ('204 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of new token
- ('201 Created', {}, ''),
- # POST of token to user object
- ('204 No Content', {}, ''),
- # GET of services object
- ('200 Ok', {}, json.dumps({"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
- resp = Request.blank(
- '/auth/v1.0',
- headers={'X-Auth-User': 'act:usr',
- 'X-Auth-Key': 'key'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertNotEquals(
- resp.headers.get('x-auth-token'),
- 'AUTH_tktest')
- self.assertEquals(resp.headers.get('x-auth-token'),
- resp.headers.get('x-storage-token'))
- self.assertEquals(resp.headers.get('x-storage-url'),
- 'http://127.0.0.1:8080/v1/AUTH_cfa')
- self.assertEquals(
- json.loads(resp.body),
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})
- self.assertEquals(self.test_auth.app.calls, 7)
-
- def test_prep_success(self):
- list_to_iter = [
- # PUT of .auth account
- ('201 Created', {}, ''),
- # PUT of .account_id container
- ('201 Created', {}, '')]
- # PUT of .token* containers
- for x in xrange(16):
- list_to_iter.append(('201 Created', {}, ''))
- self.test_auth.app = FakeApp(iter(list_to_iter))
- resp = Request.blank('/auth/v2/.prep',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(self.test_auth.app.calls, 18)
-
- def test_prep_bad_method(self):
- resp = Request.blank('/auth/v2/.prep',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
- resp = Request.blank('/auth/v2/.prep',
- environ={
- 'REQUEST_METHOD': 'HEAD'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
- resp = Request.blank('/auth/v2/.prep',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
-
- def test_prep_bad_creds(self):
- resp = Request.blank('/auth/v2/.prep',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- 'super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- resp = Request.blank('/auth/v2/.prep',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'upertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- resp = Request.blank('/auth/v2/.prep',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User': '.super_admin'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- resp = Request.blank('/auth/v2/.prep',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- resp = Request.blank(
- '/auth/v2/.prep',
- environ={'REQUEST_METHOD': 'POST'}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
-
- def test_prep_fail_account_create(self):
- self.test_auth.app = FakeApp(iter([
- # PUT of .auth account
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/.prep',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_prep_fail_token_container_create(self):
- self.test_auth.app = FakeApp(iter([
- # PUT of .auth account
- ('201 Created', {}, ''),
- # PUT of .token container
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/.prep',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_prep_fail_account_id_container_create(self):
- self.test_auth.app = FakeApp(iter([
- # PUT of .auth account
- ('201 Created', {}, ''),
- # PUT of .token container
- ('201 Created', {}, ''),
- # PUT of .account_id container
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/.prep',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 3)
-
- def test_get_reseller_success(self):
- self.test_auth.app = FakeApp(iter([
- # GET of .auth account (list containers)
- ('200 Ok', {}, json.dumps([
- {"name": ".token", "count": 0, "bytes": 0},
- {"name": ".account_id",
- "count": 0, "bytes": 0},
- {"name": "act", "count": 0, "bytes": 0}])),
- # GET of .auth account (list containers
- # continuation)
- ('200 Ok', {}, '[]')]))
- resp = Request.blank('/auth/v2',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertEquals(json.loads(resp.body),
- {"accounts": [{"name": "act"}]})
- self.assertEquals(self.test_auth.app.calls, 2)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:adm"},
- {"name": "test"}, {"name": ".admin"},
- {"name": ".reseller_admin"}], "auth": "plaintext:key"})),
- # GET of .auth account (list containers)
- ('200 Ok', {}, json.dumps([
- {"name": ".token", "count": 0, "bytes": 0},
- {"name": ".account_id",
- "count": 0, "bytes": 0},
- {"name": "act", "count": 0, "bytes": 0}])),
- # GET of .auth account (list containers
- # continuation)
- ('200 Ok', {}, '[]')]))
- resp = Request.blank('/auth/v2',
- headers={
- 'X-Auth-Admin-User':
- 'act:adm',
- 'X-Auth-Admin-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertEquals(json.loads(resp.body),
- {"accounts": [{"name": "act"}]})
- self.assertEquals(self.test_auth.app.calls, 3)
-
- def test_get_reseller_fail_bad_creds(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('404 Not Found', {}, '')]))
- resp = Request.blank('/auth/v2',
- headers={
- 'X-Auth-Admin-User':
- 'super:admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object (account admin, but not reseller
- # admin)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:adm"},
- {"name": "test"}, {"name": ".admin"}],
- "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2',
- headers={
- 'X-Auth-Admin-User':
- 'act:adm',
- 'X-Auth-Admin-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object (regular user)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:usr"},
- {"name": "test"}], "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2',
- headers={
- 'X-Auth-Admin-User':
- 'act:usr',
- 'X-Auth-Admin-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_get_reseller_fail_listing(self):
- self.test_auth.app = FakeApp(iter([
- # GET of .auth account (list containers)
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of .auth account (list containers)
- ('200 Ok', {}, json.dumps([
- {"name": ".token", "count": 0, "bytes": 0},
- {"name": ".account_id",
- "count": 0, "bytes": 0},
- {"name": "act", "count": 0, "bytes": 0}])),
- # GET of .auth account (list containers
- # continuation)
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_get_account_success(self):
- self.test_auth.app = FakeApp(iter([
- # GET of .services object
- ('200 Ok', {},
- json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})),
- # GET of account container (list objects)
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"},
- {"name": "tester", "hash": "etag", "bytes": 104,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.736680"},
- {"name": "tester3", "hash": "etag", "bytes": 86,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:28.135530"}])),
- # GET of account container (list objects
- # continuation)
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]')]))
- resp = Request.blank('/auth/v2/act',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertEquals(
- json.loads(resp.body),
- {'account_id': 'AUTH_cfa',
- 'services': {'storage':
- {'default': 'local',
- 'local': 'http://127.0.0.1:8080/v1/AUTH_cfa'}},
- 'users': [{'name': 'tester'}, {'name': 'tester3'}]})
- self.assertEquals(self.test_auth.app.calls, 3)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:adm"},
- {"name": "test"}, {"name": ".admin"}],
- "auth": "plaintext:key"})),
- # GET of .services object
- ('200 Ok', {},
- json.dumps({"storage":
- {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})),
- # GET of account container (list objects)
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"},
- {"name": "tester", "hash": "etag", "bytes": 104,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.736680"},
- {"name": "tester3", "hash": "etag", "bytes": 86,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:28.135530"}])),
- # GET of account container (list objects
- # continuation)
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]')]))
- resp = Request.blank('/auth/v2/act',
- headers={
- 'X-Auth-Admin-User':
- 'act:adm',
- 'X-Auth-Admin-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertEquals(
- json.loads(resp.body),
- {'account_id': 'AUTH_cfa',
- 'services': {'storage':
- {'default': 'local',
- 'local': 'http://127.0.0.1:8080/v1/AUTH_cfa'}},
- 'users': [{'name': 'tester'}, {'name': 'tester3'}]})
- self.assertEquals(self.test_auth.app.calls, 4)
-
- def test_get_account_fail_bad_account_name(self):
- resp = Request.blank('/auth/v2/.token',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
- resp = Request.blank('/auth/v2/.anything',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
-
- def test_get_account_fail_creds(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('404 Not Found', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- headers={
- 'X-Auth-Admin-User':
- 'super:admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object (account admin, but wrong
- # account)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act2:adm"},
- {"name": "test"}, {"name": ".admin"}],
- "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act',
- headers={
- 'X-Auth-Admin-User':
- 'act2:adm',
- 'X-Auth-Admin-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object (regular user)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:usr"},
- {"name": "test"}], "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act',
- headers={
- 'X-Auth-Admin-User':
- 'act:usr',
- 'X-Auth-Admin-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_get_account_fail_get_services(self):
- self.test_auth.app = FakeApp(iter([
- # GET of .services object
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of .services object
- ('404 Not Found', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 404)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_get_account_fail_listing(self):
- self.test_auth.app = FakeApp(iter([
- # GET of .services object
- ('200 Ok', {}, json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})),
- # GET of account container (list objects)
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- self.test_auth.app = FakeApp(iter([
- # GET of .services object
- ('200 Ok', {}, json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})),
- # GET of account container (list objects)
- ('404 Not Found', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 404)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- self.test_auth.app = FakeApp(iter([
- # GET of .services object
- ('200 Ok', {}, json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})),
- # GET of account container (list objects)
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"},
- {"name": "tester", "hash": "etag", "bytes": 104,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.736680"},
- {"name": "tester3", "hash": "etag", "bytes": 86,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:28.135530"}])),
- # GET of account container (list objects
- # continuation)
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 3)
-
- def test_set_services_new_service(self):
- self.test_auth.app = FakeApp(iter([
- # GET of .services object
- ('200 Ok', {},
- json.dumps({"storage":
- {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})),
- # PUT of new .services object
- ('204 No Content', {}, '')]))
- resp = Request.blank('/auth/v2/act/.services',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'},
- body=json.dumps(
- {'new_service':
- {'new_endpoint': 'new_value'}})
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertEquals(
- json.loads(resp.body),
- {'storage': {'default': 'local',
- 'local': 'http://127.0.0.1:8080/v1/AUTH_cfa'},
- 'new_service': {'new_endpoint': 'new_value'}})
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_set_services_new_endpoint(self):
- self.test_auth.app = FakeApp(iter([
- # GET of .services object
- ('200 Ok', {}, json.dumps(
- {"storage":
- {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})),
- # PUT of new .services object
- ('204 No Content', {}, '')]))
- resp = Request.blank('/auth/v2/act/.services',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'},
- body=json.dumps(
- {'storage': {'new_endpoint': 'new_value'}})
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertEquals(
- json.loads(resp.body),
- {'storage': {'default': 'local',
- 'local':
- 'http://127.0.0.1:8080/v1/AUTH_cfa',
- 'new_endpoint': 'new_value'}})
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_set_services_update_endpoint(self):
- self.test_auth.app = FakeApp(iter([
- # GET of .services object
- ('200 Ok', {}, json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})),
- # PUT of new .services object
- ('204 No Content', {}, '')]))
- resp = Request.blank('/auth/v2/act/.services',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'},
- body=json.dumps(
- {'storage': {'local': 'new_value'}})
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertEquals(json.loads(resp.body),
- {'storage': {'default': 'local',
- 'local': 'new_value'}})
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_set_services_fail_bad_creds(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('404 Not Found', {}, '')]))
- resp = Request.blank('/auth/v2/act/.services',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- 'super:admin',
- 'X-Auth-Admin-Key': 'supertest'},
- body=json.dumps(
- {'storage': {'local': 'new_value'}})
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object (account admin, but not reseller
- # admin)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:adm"},
- {"name": "test"}, {"name": ".admin"}],
- "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/.services',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- 'act:adm',
- 'X-Auth-Admin-Key': 'key'},
- body=json.dumps(
- {'storage': {'local': 'new_value'}})
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object (regular user)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:usr"},
- {"name": "test"}], "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/.services',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- 'act:usr',
- 'X-Auth-Admin-Key': 'key'},
- body=json.dumps(
- {'storage': {'local': 'new_value'}})
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_set_services_fail_bad_account_name(self):
- resp = Request.blank('/auth/v2/.act/.services',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'},
- body=json.dumps(
- {'storage': {'local': 'new_value'}})
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
-
- def test_set_services_fail_bad_json(self):
- resp = Request.blank('/auth/v2/act/.services',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'},
- body='garbage'
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
- resp = Request.blank('/auth/v2/act/.services',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'},
- body=''
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
-
- def test_set_services_fail_get_services(self):
- self.test_auth.app = FakeApp(iter([
- # GET of .services object
- ('503 Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act/.services',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'},
- body=json.dumps({
- 'new_service': {'new_endpoint': 'new_value'}})
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of .services object
- ('404 Not Found', {}, '')]))
- resp = Request.blank('/auth/v2/act/.services',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'},
- body=json.dumps({
- 'new_service': {'new_endpoint': 'new_value'}})
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 404)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_set_services_fail_put_services(self):
- self.test_auth.app = FakeApp(iter([
- # GET of .services object
- ('200 Ok', {}, json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})),
- # PUT of new .services object
- ('503 Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act/.services',
- environ={
- 'REQUEST_METHOD': 'POST'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'},
- body=json.dumps(
- {'new_service':
- {'new_endpoint': 'new_value'}})
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_put_account_success(self):
- conn = FakeConn(iter([
- # PUT of storage account itself
- ('201 Created', {}, '')]))
- self.test_auth.get_conn = lambda: conn
- self.test_auth.app = FakeApp(iter([
- # Initial HEAD of account container to check for
- # pre-existence
- ('404 Not Found', {}, ''),
- # PUT of account container
- ('204 No Content', {}, ''),
- # PUT of .account_id mapping object
- ('204 No Content', {}, ''),
- # PUT of .services object
- ('204 No Content', {}, ''),
- # POST to account container updating
- # X-Container-Meta-Account-Id
- ('204 No Content', {}, '')]))
- resp = Request.blank(
- '/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'PUT', 'swift.cache': FakeMemcache()},
- headers={'X-Auth-Admin-User': '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(self.test_auth.app.calls, 5)
- self.assertEquals(conn.calls, 1)
-
- def test_put_account_success_preexist_but_not_completed(
- self):
- conn = FakeConn(iter([
- # PUT of storage account itself
- ('201 Created', {}, '')]))
- self.test_auth.get_conn = lambda: conn
- self.test_auth.app = FakeApp(iter([
- # Initial HEAD of account container to check for pre-existence
- # We're going to show it as existing this time, but with no
- # X-Container-Meta-Account-Id, indicating a failed
- # previous attempt
- ('200 Ok', {}, ''),
- # PUT of .account_id mapping object
- ('204 No Content', {}, ''),
- # PUT of .services object
- ('204 No Content', {}, ''),
- # POST to account container updating
- # X-Container-Meta-Account-Id
- ('204 No Content', {}, '')]))
- resp = Request.blank(
- '/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'PUT',
- 'swift.cache': FakeMemcache()},
- headers={'X-Auth-Admin-User': '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(self.test_auth.app.calls, 4)
- self.assertEquals(conn.calls, 1)
-
- def test_put_account_success_preexist_and_completed(
- self):
- self.test_auth.app = FakeApp(iter([
- # Initial HEAD of account container to check for pre-existence
- # We're going to show it as existing this time, and with an
- # X-Container-Meta-Account-Id, indicating it already
- # exists
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '')]))
- resp = Request.blank(
- '/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'PUT', 'swift.cache': FakeMemcache()},
- headers={'X-Auth-Admin-User': '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 202)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_put_account_success_with_given_suffix(self):
- conn = FakeConn(iter([
- # PUT of storage account itself
- ('201 Created', {}, '')]))
- self.test_auth.get_conn = lambda: conn
- self.test_auth.app = FakeApp(iter([
- # Initial HEAD of account container to check for
- # pre-existence
- ('404 Not Found', {}, ''),
- # PUT of account container
- ('204 No Content', {}, ''),
- # PUT of .account_id mapping object
- ('204 No Content', {}, ''),
- # PUT of .services object
- ('204 No Content', {}, ''),
- # POST to account container updating
- # X-Container-Meta-Account-Id
- ('204 No Content', {}, '')]))
- resp = Request.blank(
- '/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'PUT', 'swift.cache': FakeMemcache()},
- headers={'X-Auth-Admin-User': '.super_admin',
- 'X-Auth-Admin-Key': 'supertest',
- 'X-Account-Suffix': 'test-suffix'}).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(
- conn.request_path,
- '/v1/AUTH_test-suffix')
- self.assertEquals(self.test_auth.app.calls, 5)
- self.assertEquals(conn.calls, 1)
-
- def test_put_account_fail_bad_creds(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('404 Not Found', {}, '')]))
- resp = Request.blank(
- '/auth/v2/act',
- environ={'REQUEST_METHOD': 'PUT',
- 'swift.cache': FakeMemcache()},
- headers={'X-Auth-Admin-User': 'super:admin',
- 'X-Auth-Admin-Key': 'supertest'},).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object (account admin, but not reseller
- # admin)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:adm"},
- {"name": "test"}, {"name": ".admin"}],
- "auth": "plaintext:key"}))]))
- resp = Request.blank(
- '/auth/v2/act',
- environ={'REQUEST_METHOD': 'PUT',
- 'swift.cache': FakeMemcache()},
- headers={'X-Auth-Admin-User': 'act:adm',
- 'X-Auth-Admin-Key': 'key'},).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object (regular user)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:usr"},
- {"name": "test"}], "auth": "plaintext:key"}))]))
- resp = Request.blank(
- '/auth/v2/act',
- environ={'REQUEST_METHOD': 'PUT', 'swift.cache': FakeMemcache()},
- headers={'X-Auth-Admin-User': 'act:usr',
- 'X-Auth-Admin-Key': 'key'},).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_put_account_fail_invalid_account_name(self):
- resp = Request.blank(
- '/auth/v2/.act',
- environ={'REQUEST_METHOD': 'PUT', 'swift.cache': FakeMemcache()},
- headers={'X-Auth-Admin-User': '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'},).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 400)
-
- def test_put_account_fail_on_initial_account_head(self):
- self.test_auth.app = FakeApp(iter([
- # Initial HEAD of account container to check for
- # pre-existence
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank(
- '/auth/v2/act',
- environ={'REQUEST_METHOD': 'PUT',
- 'swift.cache': FakeMemcache()},
- headers={'X-Auth-Admin-User': '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_put_account_fail_on_account_marker_put(self):
- self.test_auth.app = FakeApp(iter([
- # Initial HEAD of account container to check for
- # pre-existence
- ('404 Not Found', {}, ''),
- # PUT of account container
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank(
- '/auth/v2/act',
- environ={'REQUEST_METHOD': 'PUT',
- 'swift.cache': FakeMemcache()},
- headers={'X-Auth-Admin-User': '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_put_account_fail_on_storage_account_put(self):
- conn = FakeConn(iter([
- # PUT of storage account itself
- ('503 Service Unavailable', {}, '')]))
- self.test_auth.get_conn = lambda: conn
- self.test_auth.app = FakeApp(iter([
- # Initial HEAD of account container to check for
- # pre-existence
- ('404 Not Found', {}, ''),
- # PUT of account container
- ('204 No Content', {}, '')]))
- resp = Request.blank(
- '/auth/v2/act',
- environ={'REQUEST_METHOD': 'PUT',
- 'swift.cache': FakeMemcache()},
- headers={'X-Auth-Admin-User': '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(conn.calls, 1)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_put_account_fail_on_account_id_mapping(self):
- conn = FakeConn(iter([
- # PUT of storage account itself
- ('201 Created', {}, '')]))
- self.test_auth.get_conn = lambda: conn
- self.test_auth.app = FakeApp(iter([
- # Initial HEAD of account container to check for
- # pre-existence
- ('404 Not Found', {}, ''),
- # PUT of account container
- ('204 No Content', {}, ''),
- # PUT of .account_id mapping object
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank(
- '/auth/v2/act',
- environ={'REQUEST_METHOD': 'PUT',
- 'swift.cache': FakeMemcache()},
- headers={'X-Auth-Admin-User': '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(conn.calls, 1)
- self.assertEquals(self.test_auth.app.calls, 3)
-
- def test_put_account_fail_on_services_object(self):
- conn = FakeConn(iter([
- # PUT of storage account itself
- ('201 Created', {}, '')]))
- self.test_auth.get_conn = lambda: conn
- self.test_auth.app = FakeApp(iter([
- # Initial HEAD of account container to check for
- # pre-existence
- ('404 Not Found', {}, ''),
- # PUT of account container
- ('204 No Content', {}, ''),
- # PUT of .account_id mapping object
- ('204 No Content', {}, ''),
- # PUT of .services object
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank(
- '/auth/v2/act',
- environ={'REQUEST_METHOD': 'PUT',
- 'swift.cache': FakeMemcache()},
- headers={'X-Auth-Admin-User': '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(conn.calls, 1)
- self.assertEquals(self.test_auth.app.calls, 4)
-
- def test_put_account_fail_on_post_mapping(self):
- conn = FakeConn(iter([
- # PUT of storage account itself
- ('201 Created', {}, '')]))
- self.test_auth.get_conn = lambda: conn
- self.test_auth.app = FakeApp(iter([
- # Initial HEAD of account container to check for
- # pre-existence
- ('404 Not Found', {}, ''),
- # PUT of account container
- ('204 No Content', {}, ''),
- # PUT of .account_id mapping object
- ('204 No Content', {}, ''),
- # PUT of .services object
- ('204 No Content', {}, ''),
- # POST to account container updating
- # X-Container-Meta-Account-Id
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank(
- '/auth/v2/act',
- environ={'REQUEST_METHOD': 'PUT', 'swift.cache': FakeMemcache()},
- headers={'X-Auth-Admin-User': '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(conn.calls, 1)
- self.assertEquals(self.test_auth.app.calls, 5)
-
- def test_delete_account_success(self):
- conn = FakeConn(iter([
- # DELETE of storage account itself
- ('204 No Content', {}, '')]))
- self.test_auth.get_conn = lambda x: conn
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"}])),
- # Account's container listing, checking for users
- # (continuation)
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]'),
- # GET the .services object
- ('200 Ok', {}, json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})),
- # DELETE the .services object
- ('204 No Content', {}, ''),
- # DELETE the .account_id mapping object
- ('204 No Content', {}, ''),
- # DELETE the account container
- ('204 No Content', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(self.test_auth.app.calls, 6)
- self.assertEquals(conn.calls, 1)
-
- def test_delete_account_success_missing_services(self):
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"}])),
- # Account's container listing, checking for users
- # (continuation)
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]'),
- # GET the .services object
- ('404 Not Found', {}, ''),
- # DELETE the .account_id mapping object
- ('204 No Content', {}, ''),
- # DELETE the account container
- ('204 No Content', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(self.test_auth.app.calls, 5)
-
- def test_delete_account_success_missing_storage_account(
- self):
- conn = FakeConn(iter([
- # DELETE of storage account itself
- ('404 Not Found', {}, '')]))
- self.test_auth.get_conn = lambda x: conn
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"}])),
- # Account's container listing, checking for users
- # (continuation)
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]'),
- # GET the .services object
- ('200 Ok', {}, json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})),
- # DELETE the .services object
- ('204 No Content', {}, ''),
- # DELETE the .account_id mapping object
- ('204 No Content', {}, ''),
- # DELETE the account container
- ('204 No Content', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(self.test_auth.app.calls, 6)
- self.assertEquals(conn.calls, 1)
-
- def test_delete_account_success_missing_account_id_mapping(
- self):
- conn = FakeConn(iter([
- # DELETE of storage account itself
- ('204 No Content', {}, '')]))
- self.test_auth.get_conn = lambda x: conn
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"}])),
- # Account's container listing, checking for users
- # (continuation)
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]'),
- # GET the .services object
- ('200 Ok', {}, json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})),
- # DELETE the .services object
- ('204 No Content', {}, ''),
- # DELETE the .account_id mapping object
- ('404 Not Found', {}, ''),
- # DELETE the account container
- ('204 No Content', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(self.test_auth.app.calls, 6)
- self.assertEquals(conn.calls, 1)
-
- def test_delete_account_success_missing_account_container_at_end(
- self):
- conn = FakeConn(iter([
- # DELETE of storage account itself
- ('204 No Content', {}, '')]))
- self.test_auth.get_conn = lambda x: conn
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"}])),
- # Account's container listing, checking for users
- # (continuation)
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]'),
- # GET the .services object
- ('200 Ok', {}, json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})),
- # DELETE the .services object
- ('204 No Content', {}, ''),
- # DELETE the .account_id mapping object
- ('204 No Content', {}, ''),
- # DELETE the account container
- ('404 Not Found', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(self.test_auth.app.calls, 6)
- self.assertEquals(conn.calls, 1)
-
- def test_delete_account_fail_bad_creds(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('404 Not Found', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- 'super:admin',
- 'X-Auth-Admin-Key': 'supertest'},
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object (account admin, but not reseller
- # admin)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:adm"},
- {"name": "test"}, {"name": ".admin"}],
- "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- 'act:adm',
- 'X-Auth-Admin-Key': 'key'},
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object (regular user)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:usr"},
- {"name": "test"}], "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- 'act:usr',
- 'X-Auth-Admin-Key': 'key'},
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_delete_account_fail_invalid_account_name(self):
- resp = Request.blank('/auth/v2/.act',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
-
- def test_delete_account_fail_not_found(self):
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('404 Not Found', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 404)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_delete_account_fail_not_found_concurrency(
- self):
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"}])),
- # Account's container listing, checking for users
- # (continuation)
- ('404 Not Found', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 404)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_delete_account_fail_list_account(self):
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_delete_account_fail_list_account_concurrency(
- self):
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"}])),
- # Account's container listing, checking for users
- # (continuation)
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_delete_account_fail_has_users(self):
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"},
- {"name": "tester", "hash": "etag", "bytes": 104,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.736680"}]))]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 409)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_delete_account_fail_has_users2(self):
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"}])),
- # Account's container listing, checking for users
- # (continuation)
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": "tester", "hash": "etag", "bytes": 104,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.736680"}]))]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 409)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_delete_account_fail_get_services(self):
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"}])),
- # Account's container listing, checking for users
- # (continuation)
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]'),
- # GET the .services object
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 3)
-
- def test_delete_account_fail_delete_storage_account(
- self):
- conn = FakeConn(iter([
- # DELETE of storage account itself
- ('409 Conflict', {}, '')]))
- self.test_auth.get_conn = lambda x: conn
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"}])),
- # Account's container listing, checking for users
- # (continuation)
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]'),
- # GET the .services object
- ('200 Ok', {}, json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 409)
- self.assertEquals(self.test_auth.app.calls, 3)
- self.assertEquals(conn.calls, 1)
-
- def test_delete_account_fail_delete_storage_account2(
- self):
- conn = FakeConn(iter([
- # DELETE of storage account itself
- ('204 No Content', {}, ''),
- # DELETE of storage account itself
- ('409 Conflict', {}, '')]))
- self.test_auth.get_conn = lambda x: conn
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"}])),
- # Account's container listing, checking for users
- # (continuation)
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]'),
- # GET the .services object
- ('200 Ok', {}, json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa",
- "other": "http://127.0.0.1:8080/v1/AUTH_cfa2"}}))]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 3)
- self.assertEquals(conn.calls, 2)
-
- def test_delete_account_fail_delete_storage_account3(
- self):
- conn = FakeConn(iter([
- # DELETE of storage account itself
- ('503 Service Unavailable', {}, '')]))
- self.test_auth.get_conn = lambda x: conn
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"}])),
- # Account's container listing, checking for users
- # (continuation)
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]'),
- # GET the .services object
- ('200 Ok', {}, json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 3)
- self.assertEquals(conn.calls, 1)
-
- def test_delete_account_fail_delete_storage_account4(
- self):
- conn = FakeConn(iter([
- # DELETE of storage account itself
- ('204 No Content', {}, ''),
- # DELETE of storage account itself
- ('503 Service Unavailable', {}, '')]))
- self.test_auth.get_conn = lambda x: conn
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"}])),
- # Account's container listing, checking for users
- # (continuation)
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]'),
- # GET the .services object
- ('200 Ok', {}, json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa",
- "other": "http://127.0.0.1:8080/v1/AUTH_cfa2"}}))]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 3)
- self.assertEquals(conn.calls, 2)
-
- def test_delete_account_fail_delete_services(self):
- conn = FakeConn(iter([
- # DELETE of storage account itself
- ('204 No Content', {}, '')]))
- self.test_auth.get_conn = lambda x: conn
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"}])),
- # Account's container listing, checking for users
- # (continuation)
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]'),
- # GET the .services object
- ('200 Ok', {}, json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})),
- # DELETE the .services object
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 4)
- self.assertEquals(conn.calls, 1)
-
- def test_delete_account_fail_delete_account_id_mapping(
- self):
- conn = FakeConn(iter([
- # DELETE of storage account itself
- ('204 No Content', {}, '')]))
- self.test_auth.get_conn = lambda x: conn
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"}])),
- # Account's container listing, checking for users
- # (continuation)
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]'),
- # GET the .services object
- ('200 Ok', {}, json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})),
- # DELETE the .services object
- ('204 No Content', {}, ''),
- # DELETE the .account_id mapping object
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 5)
- self.assertEquals(conn.calls, 1)
-
- def test_delete_account_fail_delete_account_container(
- self):
- conn = FakeConn(iter([
- # DELETE of storage account itself
- ('204 No Content', {}, '')]))
- self.test_auth.get_conn = lambda x: conn
- self.test_auth.app = FakeApp(iter([
- # Account's container listing, checking for
- # users
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"}])),
- # Account's container listing, checking for users
- # (continuation)
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]'),
- # GET the .services object
- ('200 Ok', {}, json.dumps(
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})),
- # DELETE the .services object
- ('204 No Content', {}, ''),
- # DELETE the .account_id mapping object
- ('204 No Content', {}, ''),
- # DELETE the account container
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act',
- environ={
- 'REQUEST_METHOD': 'DELETE',
- 'swift.cache': FakeMemcache()},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 6)
- self.assertEquals(conn.calls, 1)
-
- def test_get_user_success(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {}, json.dumps(
- {"groups": [{"name": "act:usr"}, {"name": "act"},
- {"name": ".admin"}],
- "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/usr',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertEquals(resp.body, json.dumps(
- {"groups": [{"name": "act:usr"}, {"name": "act"},
- {"name": ".admin"}],
- "auth": "plaintext:key"}))
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_get_user_fail_no_super_admin_key(self):
- local_auth = auth.filter_factory({})(FakeApp(iter([
- # GET of user object (but we should never get
- # here)
- ('200 Ok', {}, json.dumps(
- {"groups": [{"name": "act:usr"}, {"name": "act"},
- {"name": ".admin"}],
- "auth": "plaintext:key"}))])))
- resp = Request.blank('/auth/v2/act/usr',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(local_auth)
- self.assertEquals(resp.status_int, 404)
- self.assertEquals(local_auth.app.calls, 0)
-
- def test_get_user_groups_success(self):
- self.test_auth.app = FakeApp(iter([
- # GET of account container (list objects)
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"},
- {"name": "tester", "hash": "etag", "bytes": 104,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.736680"},
- {"name": "tester3", "hash": "etag", "bytes": 86,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:28.135530"}])),
- # GET of user object
- ('200 Ok', {}, json.dumps(
- {"groups": [{"name": "act:tester"}, {"name": "act"},
- {"name": ".admin"}],
- "auth": "plaintext:key"})),
- # GET of user object
- ('200 Ok', {}, json.dumps(
- {"groups": [{"name": "act:tester3"}, {"name": "act"}],
- "auth": "plaintext:key3"})),
- # GET of account container (list objects
- # continuation)
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]')]))
- resp = Request.blank('/auth/v2/act/.groups',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertEquals(resp.body, json.dumps(
- {"groups": [{"name": ".admin"}, {"name": "act"},
- {"name": "act:tester"}, {"name": "act:tester3"}]}))
- self.assertEquals(self.test_auth.app.calls, 4)
-
- def test_get_user_groups_success2(self):
- self.test_auth.app = FakeApp(iter([
- # GET of account container (list objects)
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"},
- {"name": "tester", "hash": "etag", "bytes": 104,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.736680"}])),
- # GET of user object
- ('200 Ok', {}, json.dumps(
- {"groups": [{"name": "act:tester"}, {"name": "act"},
- {"name": ".admin"}],
- "auth": "plaintext:key"})),
- # GET of account container (list objects
- # continuation)
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": "tester3", "hash": "etag", "bytes": 86,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:28.135530"}])),
- # GET of user object
- ('200 Ok', {}, json.dumps(
- {"groups": [{"name": "act:tester3"}, {"name": "act"}],
- "auth": "plaintext:key3"})),
- # GET of account container (list objects
- # continuation)
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, '[]')]))
- resp = Request.blank('/auth/v2/act/.groups',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertEquals(resp.body, json.dumps(
- {"groups": [{"name": ".admin"}, {"name": "act"},
- {"name": "act:tester"}, {"name": "act:tester3"}]}))
- self.assertEquals(self.test_auth.app.calls, 5)
-
- def test_get_user_fail_invalid_account(self):
- resp = Request.blank('/auth/v2/.invalid/usr',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
-
- def test_get_user_fail_invalid_user(self):
- resp = Request.blank('/auth/v2/act/.invalid',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
-
- def test_get_user_fail_bad_creds(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('404 Not Found', {}, '')]))
- resp = Request.blank('/auth/v2/act/usr',
- headers={
- 'X-Auth-Admin-User':
- 'super:admin',
- 'X-Auth-Admin-Key': 'supertest'},
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object (regular user)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:usr"},
- {"name": "test"}], "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/usr',
- headers={
- 'X-Auth-Admin-User':
- 'act:usr',
- 'X-Auth-Admin-Key': 'key'},
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_get_user_account_admin_success(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object (account admin, but not reseller
- # admin)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:adm"},
- {"name": "test"}, {"name": ".admin"}],
- "auth": "plaintext:key"})),
- # GET of requested user object
- ('200 Ok', {}, json.dumps(
- {"groups": [{"name": "act:usr"}, {"name": "act"}],
- "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/usr',
- headers={
- 'X-Auth-Admin-User':
- 'act:adm',
- 'X-Auth-Admin-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertEquals(resp.body, json.dumps(
- {"groups": [{"name": "act:usr"}, {"name": "act"}],
- "auth": "plaintext:key"}))
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_get_user_account_admin_fail_getting_account_admin(
- self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object (account admin check)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:adm"},
- {"name": "test"}, {"name": ".admin"}],
- "auth": "plaintext:key"})),
- # GET of requested user object [who is an .admin
- # as well]
- ('200 Ok', {}, json.dumps(
- {"groups": [{"name": "act:usr"}, {"name": "act"},
- {"name": ".admin"}],
- "auth": "plaintext:key"})),
- # GET of user object (reseller admin check [and fail
- # here])
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:adm"},
- {"name": "test"}, {"name": ".admin"}],
- "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/usr',
- headers={
- 'X-Auth-Admin-User':
- 'act:adm',
- 'X-Auth-Admin-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 3)
-
- def test_get_user_account_admin_fail_getting_reseller_admin(
- self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object (account admin check)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:adm"},
- {"name": "test"}, {"name": ".admin"}],
- "auth": "plaintext:key"})),
- # GET of requested user object [who is a
- # .reseller_admin]
- ('200 Ok', {}, json.dumps(
- {"groups": [{"name": "act:usr"}, {"name": "act"},
- {"name": ".reseller_admin"}],
- "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/usr',
- headers={
- 'X-Auth-Admin-User':
- 'act:adm',
- 'X-Auth-Admin-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_get_user_reseller_admin_fail_getting_reseller_admin(
- self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object (account admin check)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:adm"},
- {"name": "test"}, {"name": ".reseller_admin"}],
- "auth": "plaintext:key"})),
- # GET of requested user object [who also is a
- # .reseller_admin]
- ('200 Ok', {}, json.dumps(
- {"groups": [{"name": "act:usr"}, {"name": "act"},
- {"name": ".reseller_admin"}],
- "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/usr',
- headers={
- 'X-Auth-Admin-User':
- 'act:adm',
- 'X-Auth-Admin-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_get_user_super_admin_succeed_getting_reseller_admin(
- self):
- self.test_auth.app = FakeApp(iter([
- # GET of requested user object
- ('200 Ok', {}, json.dumps(
- {"groups": [{"name": "act:usr"}, {"name": "act"},
- {"name": ".reseller_admin"}],
- "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/usr',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assertEquals(resp.body, json.dumps(
- {"groups": [{"name": "act:usr"}, {"name": "act"},
- {"name": ".reseller_admin"}],
- "auth": "plaintext:key"}))
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_get_user_groups_not_found(self):
- self.test_auth.app = FakeApp(iter([
- # GET of account container (list objects)
- ('404 Not Found', {}, '')]))
- resp = Request.blank('/auth/v2/act/.groups',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 404)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_get_user_groups_fail_listing(self):
- self.test_auth.app = FakeApp(iter([
- # GET of account container (list objects)
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act/.groups',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_get_user_groups_fail_get_user(self):
- self.test_auth.app = FakeApp(iter([
- # GET of account container (list objects)
- ('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'},
- json.dumps([
- {"name": ".services", "hash": "etag", "bytes": 112,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.618110"},
- {"name": "tester", "hash": "etag", "bytes": 104,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:27.736680"},
- {"name": "tester3", "hash": "etag", "bytes": 86,
- "content_type":
- "application/octet-stream",
- "last_modified": "2010-12-03T17:16:28.135530"}])),
- # GET of user object
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act/.groups',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_get_user_not_found(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('404 Not Found', {}, '')]))
- resp = Request.blank('/auth/v2/act/usr',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 404)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_get_user_fail(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act/usr',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key':
- 'supertest',
- 'X-Auth-User-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_put_user_fail_invalid_account(self):
- resp = Request.blank('/auth/v2/.invalid/usr',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key':
- 'supertest',
- 'X-Auth-User-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
-
- def test_put_user_fail_invalid_user(self):
- resp = Request.blank('/auth/v2/act/.usr',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key':
- 'supertest',
- 'X-Auth-User-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
-
- def test_put_user_fail_no_user_key(self):
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
-
- def test_put_user_reseller_admin_fail_bad_creds(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object (reseller admin)
- # This shouldn't actually get called, checked
- # below
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:rdm"},
- {"name": "test"}, {"name": ".admin"},
- {"name": ".reseller_admin"}], "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- 'act:rdm',
- 'X-Auth-Admin-Key':
- 'key',
- 'X-Auth-User-Key':
- 'key',
- 'X-Auth-User-Reseller-Admin': 'true'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 0)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object (account admin, but not reseller admin)
- # This shouldn't actually get called, checked
- # below
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:adm"},
- {"name": "test"}, {"name": ".admin"}],
- "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- 'act:adm',
- 'X-Auth-Admin-Key':
- 'key',
- 'X-Auth-User-Key':
- 'key',
- 'X-Auth-User-Reseller-Admin': 'true'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 0)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object (regular user)
- # This shouldn't actually get called, checked
- # below
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:usr"},
- {"name": "test"}], "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- 'act:adm',
- 'X-Auth-Admin-Key':
- 'key',
- 'X-Auth-User-Key':
- 'key',
- 'X-Auth-User-Reseller-Admin': 'true'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 0)
-
- def test_put_user_account_admin_fail_bad_creds(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object (account admin, but wrong
- # account)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act2:adm"},
- {"name": "test"}, {"name": ".admin"}],
- "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- 'act2:adm',
- 'X-Auth-Admin-Key':
- 'key',
- 'X-Auth-User-Key':
- 'key',
- 'X-Auth-User-Admin': 'true'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object (regular user)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:usr"},
- {"name": "test"}], "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- 'act:usr',
- 'X-Auth-Admin-Key':
- 'key',
- 'X-Auth-User-Key':
- 'key',
- 'X-Auth-User-Admin': 'true'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_put_user_regular_fail_bad_creds(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object (account admin, but wrong
- # account)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act2:adm"},
- {"name": "test"}, {"name": ".admin"}],
- "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- 'act2:adm',
- 'X-Auth-Admin-Key':
- 'key',
- 'X-Auth-User-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object (regular user)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:usr"},
- {"name": "test"}], "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- 'act:usr',
- 'X-Auth-Admin-Key':
- 'key',
- 'X-Auth-User-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_put_user_regular_success(self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of user object
- ('201 Created', {}, '')]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key':
- 'supertest',
- 'X-Auth-User-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(self.test_auth.app.calls, 2)
- self.assertEquals(
- json.loads(self.test_auth.app.request.body),
- {"groups": [{"name": "act:usr"}, {"name": "act"}],
- "auth": "plaintext:key"})
-
- def test_put_user_special_chars_success(self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of user object
- ('201 Created', {}, '')]))
- resp = Request.blank('/auth/v2/act/u_s-r',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key':
- 'supertest',
- 'X-Auth-User-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(self.test_auth.app.calls, 2)
- self.assertEquals(
- json.loads(self.test_auth.app.request.body),
- {"groups": [{"name": "act:u_s-r"}, {"name": "act"}],
- "auth": "plaintext:key"})
-
- def test_put_user_account_admin_success(self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of user object
- ('201 Created', {}, '')]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key':
- 'supertest',
- 'X-Auth-User-Key': 'key',
- 'X-Auth-User-Admin': 'true'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(self.test_auth.app.calls, 2)
- self.assertEquals(
- json.loads(self.test_auth.app.request.body),
- {"groups": [{"name": "act:usr"}, {"name": "act"},
- {"name": ".admin"}],
- "auth": "plaintext:key"})
-
- def test_put_user_reseller_admin_success(self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of user object
- ('201 Created', {}, '')]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key':
- 'supertest',
- 'X-Auth-User-Key': 'key',
- 'X-Auth-User-Reseller-Admin': 'true'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(self.test_auth.app.calls, 2)
- self.assertEquals(
- json.loads(self.test_auth.app.request.body),
- {"groups": [{"name": "act:usr"}, {"name": "act"},
- {"name": ".admin"}, {"name": ".reseller_admin"}],
- "auth": "plaintext:key"})
-
- def test_put_user_fail_not_found(self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of user object
- ('404 Not Found', {}, '')]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key':
- 'supertest',
- 'X-Auth-User-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 404)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_put_user_fail(self):
- self.test_auth.app = FakeApp(iter([
- # PUT of user object
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'PUT'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key':
- 'supertest',
- 'X-Auth-User-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_delete_user_bad_creds(self):
- self.test_auth.app = FakeApp(iter([
- # GET of user object (account admin, but wrong
- # account)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act2:adm"},
- {"name": "test"}, {"name": ".admin"}],
- "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'X-Auth-Admin-User':
- 'act2:adm',
- 'X-Auth-Admin-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- self.test_auth.app = FakeApp(iter([
- # GET of user object (regular user)
- ('200 Ok', {}, json.dumps({"groups": [{"name": "act:usr"},
- {"name": "test"}], "auth": "plaintext:key"}))]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'X-Auth-Admin-User':
- 'act:usr',
- 'X-Auth-Admin-Key': 'key'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 403)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_delete_user_invalid_account(self):
- resp = Request.blank('/auth/v2/.invalid/usr',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
-
- def test_delete_user_invalid_user(self):
- resp = Request.blank('/auth/v2/act/.invalid',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
-
- def test_delete_user_not_found(self):
- self.test_auth.app = FakeApp(iter([
- # HEAD of user object
- ('404 Not Found', {}, '')]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 404)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_delete_user_fail_head_user(self):
- self.test_auth.app = FakeApp(iter([
- # HEAD of user object
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_delete_user_fail_delete_token(self):
- self.test_auth.app = FakeApp(iter([
- # HEAD of user object
- ('200 Ok',
- {'X-Object-Meta-Auth-Token': 'AUTH_tk'}, ''),
- # DELETE of token
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_delete_user_fail_delete_user(self):
- self.test_auth.app = FakeApp(iter([
- # HEAD of user object
- ('200 Ok',
- {'X-Object-Meta-Auth-Token': 'AUTH_tk'}, ''),
- # DELETE of token
- ('204 No Content', {}, ''),
- # DELETE of user object
- ('503 Service Unavailable', {}, '')]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(self.test_auth.app.calls, 3)
-
- def test_delete_user_success(self):
- self.test_auth.app = FakeApp(iter([
- # HEAD of user object
- ('200 Ok',
- {'X-Object-Meta-Auth-Token': 'AUTH_tk'}, ''),
- # DELETE of token
- ('204 No Content', {}, ''),
- # DELETE of user object
- ('204 No Content', {}, '')]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(self.test_auth.app.calls, 3)
-
- def test_delete_user_success_missing_user_at_end(self):
- self.test_auth.app = FakeApp(iter([
- # HEAD of user object
- ('200 Ok',
- {'X-Object-Meta-Auth-Token': 'AUTH_tk'}, ''),
- # DELETE of token
- ('204 No Content', {}, ''),
- # DELETE of user object
- ('404 Not Found', {}, '')]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(self.test_auth.app.calls, 3)
-
- def test_delete_user_success_missing_token(self):
- self.test_auth.app = FakeApp(iter([
- # HEAD of user object
- ('200 Ok',
- {'X-Object-Meta-Auth-Token': 'AUTH_tk'}, ''),
- # DELETE of token
- ('404 Not Found', {}, ''),
- # DELETE of user object
- ('204 No Content', {}, '')]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(self.test_auth.app.calls, 3)
-
- def test_delete_user_success_no_token(self):
- self.test_auth.app = FakeApp(iter([
- # HEAD of user object
- ('200 Ok', {}, ''),
- # DELETE of user object
- ('204 No Content', {}, '')]))
- resp = Request.blank('/auth/v2/act/usr',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_validate_token_bad_prefix(self):
- resp = Request.blank('/auth/v2/.token/BAD_token'
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
-
- def test_validate_token_tmi(self):
- resp = Request.blank(
- '/auth/v2/.token/AUTH_token/tmi').get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
-
- def test_validate_token_bad_memcache(self):
- fake_memcache = FakeMemcache()
- fake_memcache.set('AUTH_/auth/AUTH_token', 'bogus')
- resp = Request.blank(
- '/auth/v2/.token/AUTH_token',
- environ={'swift.cache': fake_memcache}).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 500)
-
- def test_validate_token_from_memcache(self):
- fake_memcache = FakeMemcache()
- fake_memcache.set(
- 'AUTH_/auth/AUTH_token',
- (time() + 1,
- 'act:usr,act'))
- resp = Request.blank(
- '/auth/v2/.token/AUTH_token',
- environ={'swift.cache': fake_memcache}).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(
- resp.headers.get('x-auth-groups'),
- 'act:usr,act')
- self.assert_(float(resp.headers['x-auth-ttl']) < 1,
- resp.headers['x-auth-ttl'])
-
- def test_validate_token_from_memcache_expired(self):
- fake_memcache = FakeMemcache()
- fake_memcache.set(
- 'AUTH_/auth/AUTH_token',
- (time() - 1,
- 'act:usr,act'))
- resp = Request.blank(
- '/auth/v2/.token/AUTH_token',
- environ={'swift.cache': fake_memcache}).get_response(
- self.test_auth)
- self.assertEquals(resp.status_int, 404)
- self.assert_('x-auth-groups' not in resp.headers)
- self.assert_('x-auth-ttl' not in resp.headers)
-
- def test_validate_token_from_object(self):
- self.test_auth.app = FakeApp(iter([
- # GET of token object
- ('200 Ok', {}, json.dumps({'groups': [{'name': 'act:usr'},
- {'name': 'act'}], 'expires': time() + 1}))]))
- resp = Request.blank('/auth/v2/.token/AUTH_token'
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(self.test_auth.app.calls, 1)
- self.assertEquals(
- resp.headers.get('x-auth-groups'),
- 'act:usr,act')
- self.assert_(float(resp.headers['x-auth-ttl']) < 1,
- resp.headers['x-auth-ttl'])
-
- def test_validate_token_from_object_expired(self):
- self.test_auth.app = FakeApp(iter([
- # GET of token object
- ('200 Ok', {}, json.dumps({'groups': 'act:usr,act',
- 'expires': time() - 1})),
- # DELETE of expired token object
- ('204 No Content', {}, '')]))
- resp = Request.blank('/auth/v2/.token/AUTH_token'
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 404)
- self.assertEquals(self.test_auth.app.calls, 2)
-
- def test_validate_token_from_object_with_admin(self):
- self.test_auth.app = FakeApp(iter([
- # GET of token object
- ('200 Ok', {}, json.dumps({'account_id': 'AUTH_cfa', 'groups':
- [{'name': 'act:usr'},
- {'name': 'act'},
- {'name': '.admin'}],
- 'expires': time() + 1}))]))
- resp = Request.blank('/auth/v2/.token/AUTH_token'
- ).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(self.test_auth.app.calls, 1)
- self.assertEquals(resp.headers.get('x-auth-groups'),
- 'act:usr,act,AUTH_cfa')
- self.assert_(float(resp.headers['x-auth-ttl']) < 1,
- resp.headers['x-auth-ttl'])
-
- def test_get_conn_default(self):
- conn = self.test_auth.get_conn()
- self.assertEquals(
- conn.__class__,
- auth.HTTPConnection)
- self.assertEquals(conn.host, '127.0.0.1')
- self.assertEquals(conn.port, 8080)
-
- def test_get_conn_default_https(self):
- local_auth = auth.filter_factory(
- {'super_admin_key': 'supertest',
- 'default_swift_cluster': 'local#https://1.2.3.4/v1'})(FakeApp())
- conn = local_auth.get_conn()
- self.assertEquals(
- conn.__class__,
- auth.HTTPSConnection)
- self.assertEquals(conn.host, '1.2.3.4')
- self.assertEquals(conn.port, 443)
-
- def test_get_conn_overridden(self):
- local_auth = auth.filter_factory(
- {'super_admin_key': 'supertest',
- 'default_swift_cluster': 'local#https://1.2.3.4/v1'})(FakeApp())
- conn = \
- local_auth.get_conn(
- urlparsed=auth.urlparse('http://5.6.7.8/v1'))
- self.assertEquals(
- conn.__class__,
- auth.HTTPConnection)
- self.assertEquals(conn.host, '5.6.7.8')
- self.assertEquals(conn.port, 80)
-
- def test_get_conn_overridden_https(self):
- local_auth = auth.filter_factory(
- {'super_admin_key': 'supertest',
- 'default_swift_cluster': 'local#http://1.2.3.4/v1'})(FakeApp())
- conn = \
- local_auth.get_conn(
- urlparsed=auth.urlparse(
- 'https://5.6.7.8/v1'))
- self.assertEquals(
- conn.__class__,
- auth.HTTPSConnection)
- self.assertEquals(conn.host, '5.6.7.8')
- self.assertEquals(conn.port, 443)
-
- def test_get_itoken_fail_no_memcache(self):
- exc = None
- try:
- self.test_auth.get_itoken({})
- except Exception as err:
- exc = err
- self.assertEquals(str(exc),
- 'No memcache set up; required for Swauth middleware')
-
- def test_get_itoken_success(self):
- fmc = FakeMemcache()
- itk = self.test_auth.get_itoken(
- {'swift.cache': fmc})
- self.assert_(itk.startswith('AUTH_itk'), itk)
- expires, groups = fmc.get('AUTH_/auth/%s' % itk)
- self.assert_(expires > time(), expires)
- self.assertEquals(
- groups,
- '.auth,.reseller_admin,AUTH_.auth')
-
- def test_get_admin_detail_fail_no_colon(self):
- self.test_auth.app = FakeApp(iter([]))
- self.assertEquals(
- self.test_auth.get_admin_detail(
- Request.blank('/')),
- None)
- self.assertEquals(
- self.test_auth.get_admin_detail(
- Request.blank('/',
- headers={'X-Auth-Admin-User': 'usr'})), None)
- self.assertRaises(
- StopIteration, self.test_auth.get_admin_detail,
- Request.blank('/', headers={'X-Auth-Admin-User': 'act:usr'}))
-
- def test_get_admin_detail_fail_user_not_found(self):
- self.test_auth.app = FakeApp(
- iter([('404 Not Found', {}, '')]))
- self.assertEquals(
- self.test_auth.get_admin_detail(
- Request.blank('/',
- headers={'X-Auth-Admin-User': 'act:usr'})), None)
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_get_admin_detail_fail_get_user_error(self):
- self.test_auth.app = FakeApp(iter([
- ('503 Service Unavailable', {}, '')]))
- exc = None
- try:
- self.test_auth.get_admin_detail(
- Request.blank('/',
- headers={'X-Auth-Admin-User': 'act:usr'}))
- except Exception as err:
- exc = err
- self.assertEquals(str(exc), 'Could not get admin user object: '
- '/v1/AUTH_.auth/act/usr 503 Service Unavailable')
- self.assertEquals(self.test_auth.app.calls, 1)
-
- def test_get_admin_detail_success(self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps({"auth": "plaintext:key",
- "groups": [{'name': "act:usr"}, {'name': "act"},
- {'name': ".admin"}]}))]))
- detail = self.test_auth.get_admin_detail(
- Request.blank('/',
- headers={'X-Auth-Admin-User': 'act:usr'}))
- self.assertEquals(self.test_auth.app.calls, 1)
- self.assertEquals(
- detail, {'account': 'act',
- 'auth': 'plaintext:key',
- 'groups': [{'name': 'act:usr'}, {'name': 'act'},
- {'name': '.admin'}]})
-
- def test_credentials_match_success(self):
- self.assert_(self.test_auth.credentials_match(
- {'auth': 'plaintext:key'}, 'key'))
-
- def test_credentials_match_fail_no_details(self):
- self.assert_(
- not self.test_auth.credentials_match(None, 'notkey'))
-
- def test_credentials_match_fail_plaintext(self):
- self.assert_(not self.test_auth.credentials_match(
- {'auth': 'plaintext:key'}, 'notkey'))
-
- def test_is_super_admin_success(self):
- self.assert_(
- self.test_auth.is_super_admin(
- Request.blank(
- '/',
- headers={'X-Auth-Admin-User': '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'})))
-
- def test_is_super_admin_fail_bad_key(self):
- self.assert_(
- not self.test_auth.is_super_admin(
- Request.blank('/',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'bad'})))
- self.assert_(
- not self.test_auth.is_super_admin(
- Request.blank('/',
- headers={'X-Auth-Admin-User': '.super_admin'})))
- self.assert_(
- not self.test_auth.is_super_admin(Request.blank('/')))
-
- def test_is_super_admin_fail_bad_user(self):
- self.assert_(
- not self.test_auth.is_super_admin(
- Request.blank('/',
- headers={
- 'X-Auth-Admin-User':
- 'bad',
- 'X-Auth-Admin-Key': 'supertest'})))
- self.assert_(
- not self.test_auth.is_super_admin(
- Request.blank('/',
- headers={'X-Auth-Admin-Key': 'supertest'})))
- self.assert_(
- not self.test_auth.is_super_admin(Request.blank('/')))
-
- def test_is_reseller_admin_success_is_super_admin(self):
- self.assert_(
- self.test_auth.is_reseller_admin(
- Request.blank('/',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'})))
-
- def test_is_reseller_admin_success_called_get_admin_detail(
- self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps({'auth': 'plaintext:key',
- 'groups': [{'name': 'act:rdm'}, {'name': 'act'},
- {'name': '.admin'},
- {'name': '.reseller_admin'}]}))]))
- self.assert_(
- self.test_auth.is_reseller_admin(
- Request.blank('/',
- headers={
- 'X-Auth-Admin-User':
- 'act:rdm',
- 'X-Auth-Admin-Key': 'key'})))
-
- def test_is_reseller_admin_fail_only_account_admin(
- self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps({'auth': 'plaintext:key',
- 'groups': [{'name': 'act:adm'}, {'name': 'act'},
- {'name': '.admin'}]}))]))
- self.assert_(
- not self.test_auth.is_reseller_admin(
- Request.blank('/',
- headers={
- 'X-Auth-Admin-User':
- 'act:adm',
- 'X-Auth-Admin-Key': 'key'})))
-
- def test_is_reseller_admin_fail_regular_user(self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps({'auth': 'plaintext:key',
- 'groups': [{'name': 'act:usr'}, {'name': 'act'}]}))]))
- self.assert_(
- not self.test_auth.is_reseller_admin(
- Request.blank('/',
- headers={
- 'X-Auth-Admin-User':
- 'act:usr',
- 'X-Auth-Admin-Key': 'key'})))
-
- def test_is_reseller_admin_fail_bad_key(self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps({'auth': 'plaintext:key',
- 'groups': [{'name': 'act:rdm'}, {'name': 'act'},
- {'name': '.admin'},
- {'name': '.reseller_admin'}]}))]))
- self.assert_(
- not self.test_auth.is_reseller_admin(
- Request.blank('/',
- headers={
- 'X-Auth-Admin-User':
- 'act:rdm',
- 'X-Auth-Admin-Key': 'bad'})))
-
- def test_is_account_admin_success_is_super_admin(self):
- self.assert_(
- self.test_auth.is_account_admin(
- Request.blank('/',
- headers={
- 'X-Auth-Admin-User':
- '.super_admin',
- 'X-Auth-Admin-Key': 'supertest'}), 'act'))
-
- def test_is_account_admin_success_is_reseller_admin(
- self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps({'auth': 'plaintext:key',
- 'groups': [{'name': 'act:rdm'}, {'name': 'act'},
- {'name': '.admin'},
- {'name': '.reseller_admin'}]}))]))
- self.assert_(
- self.test_auth.is_account_admin(
- Request.blank('/',
- headers={
- 'X-Auth-Admin-User':
- 'act:rdm',
- 'X-Auth-Admin-Key': 'key'}), 'act'))
-
- def test_is_account_admin_success(self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps({'auth': 'plaintext:key',
- 'groups': [{'name': 'act:adm'}, {'name': 'act'},
- {'name': '.admin'}]}))]))
- self.assert_(
- self.test_auth.is_account_admin(
- Request.blank('/',
- headers={
- 'X-Auth-Admin-User':
- 'act:adm',
- 'X-Auth-Admin-Key': 'key'}), 'act'))
-
- def test_is_account_admin_fail_account_admin_different_account(
- self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps({'auth': 'plaintext:key',
- 'groups': [{'name': 'act2:adm'}, {'name': 'act2'},
- {'name': '.admin'}]}))]))
- self.assert_(
- not self.test_auth.is_account_admin(
- Request.blank('/',
- headers={
- 'X-Auth-Admin-User':
- 'act2:adm',
- 'X-Auth-Admin-Key': 'key'}), 'act'))
-
- def test_is_account_admin_fail_regular_user(self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps({'auth': 'plaintext:key',
- 'groups': [{'name': 'act:usr'}, {'name': 'act'}]}))]))
- self.assert_(
- not self.test_auth.is_account_admin(
- Request.blank('/',
- headers={
- 'X-Auth-Admin-User':
- 'act:usr',
- 'X-Auth-Admin-Key': 'key'}), 'act'))
-
- def test_is_account_admin_fail_bad_key(self):
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps({'auth': 'plaintext:key',
- 'groups': [{'name': 'act:rdm'}, {'name': 'act'},
- {'name': '.admin'},
- {'name': '.reseller_admin'}]}))]))
- self.assert_(
- not self.test_auth.is_account_admin(
- Request.blank('/',
- headers={
- 'X-Auth-Admin-User':
- 'act:rdm',
- 'X-Auth-Admin-Key': 'bad'}), 'act'))
-
- def test_reseller_admin_but_account_is_internal_use_only(
- self):
- req = Request.blank('/v1/AUTH_.auth',
- environ={'REQUEST_METHOD': 'GET'})
- req.remote_user = 'act:usr,act,.reseller_admin'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
-
- def test_reseller_admin_but_account_is_exactly_reseller_prefix(
- self):
- req = Request.blank(
- '/v1/AUTH_',
- environ={'REQUEST_METHOD': 'GET'})
- req.remote_user = 'act:usr,act,.reseller_admin'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
-
- def _get_token_success_v1_0_encoded(
- self, saved_user, saved_key, sent_user,
- sent_key):
- self.test_auth.app = FakeApp(iter([
- # GET of user object
- ('200 Ok', {},
- json.dumps({"auth": "plaintext:%s" % saved_key,
- "groups": [{'name': saved_user}, {'name': "act"},
- {'name': ".admin"}]})),
- # GET of account
- ('204 Ok',
- {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
- # PUT of new token
- ('201 Created', {}, ''),
- # POST of token to user object
- ('204 No Content', {}, ''),
- # GET of services object
- ('200 Ok', {}, json.dumps({"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
- resp = Request.blank(
- '/auth/v1.0',
- headers={'X-Auth-User': sent_user,
- 'X-Auth-Key': sent_key}).get_response(self.test_auth)
- self.assertEquals(resp.status_int, 200)
- self.assert_(
- resp.headers.get('x-auth-token',
- '').startswith('AUTH_tk'),
- resp.headers.get('x-auth-token'))
- self.assertEquals(resp.headers.get('x-auth-token'),
- resp.headers.get('x-storage-token'))
- self.assertEquals(resp.headers.get('x-storage-url'),
- 'http://127.0.0.1:8080/v1/AUTH_cfa')
- self.assertEquals(
- json.loads(resp.body),
- {"storage": {"default": "local",
- "local": "http://127.0.0.1:8080/v1/AUTH_cfa"}})
- self.assertEquals(self.test_auth.app.calls, 5)
-
- def test_get_token_success_v1_0_encoded1(self):
- self._get_token_success_v1_0_encoded(
- 'act:usr', 'key', 'act%3ausr', 'key')
-
- def test_get_token_success_v1_0_encoded2(self):
- self._get_token_success_v1_0_encoded(
- 'act:u s r', 'key', 'act%3au%20s%20r', 'key')
-
- def test_get_token_success_v1_0_encoded3(self):
- self._get_token_success_v1_0_encoded(
- 'act:u s r', 'k:e:y', 'act%3au%20s%20r', 'k%3Ae%3ay')
-
- def test_allowed_sync_hosts(self):
- a = auth.filter_factory(
- {'super_admin_key': 'supertest'})(FakeApp())
- self.assertEquals(
- a.allowed_sync_hosts,
- ['127.0.0.1'])
- a = auth.filter_factory({
- 'super_admin_key': 'supertest',
- 'allowed_sync_hosts':
- '1.1.1.1,2.1.1.1, 3.1.1.1 , 4.1.1.1,, , 5.1.1.1'})(FakeApp())
- self.assertEquals(
- a.allowed_sync_hosts,
- ['1.1.1.1', '2.1.1.1', '3.1.1.1', '4.1.1.1', '5.1.1.1'])
-
- def test_reseller_admin_is_owner(self):
- orig_authorize = self.test_auth.authorize
- owner_values = []
-
- def mitm_authorize(req):
- rv = orig_authorize(req)
- owner_values.append(
- req.environ.get('swift_owner', False))
- return rv
-
- self.test_auth.authorize = mitm_authorize
-
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps(
- {'account': 'other', 'user': 'other:usr',
- 'account_id': 'AUTH_other',
- 'groups': [{'name': 'other:usr'}, {'name': 'other'},
- {'name': '.reseller_admin'}],
- 'expires': time() + 60})),
- ('204 No Content', {}, '')]))
- req = Request.blank(
- '/v1/AUTH_cfa',
- headers={'X-Auth-Token': 'AUTH_t'})
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(owner_values, [True])
-
- def test_admin_is_owner(self):
- orig_authorize = self.test_auth.authorize
- owner_values = []
-
- def mitm_authorize(req):
- rv = orig_authorize(req)
- owner_values.append(
- req.environ.get('swift_owner', False))
- return rv
-
- self.test_auth.authorize = mitm_authorize
-
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps(
- {'account': 'act', 'user': 'act:usr',
- 'account_id': 'AUTH_cfa',
- 'groups': [{'name': 'act:usr'}, {'name': 'act'},
- {'name': '.admin'}],
- 'expires': time() + 60})),
- ('204 No Content', {}, '')]))
- req = Request.blank(
- '/v1/AUTH_cfa',
- headers={'X-Auth-Token': 'AUTH_t'})
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(owner_values, [True])
-
- def test_regular_is_not_owner(self):
- orig_authorize = self.test_auth.authorize
- owner_values = []
-
- def mitm_authorize(req):
- rv = orig_authorize(req)
- owner_values.append(
- req.environ.get('swift_owner', False))
- return rv
-
- self.test_auth.authorize = mitm_authorize
-
- self.test_auth.app = FakeApp(iter([
- ('200 Ok', {},
- json.dumps(
- {'account': 'act', 'user': 'act:usr',
- 'account_id': 'AUTH_cfa',
- 'groups':
- [{'name': 'act:usr'}, {
- 'name': 'act'}],
- 'expires': time() + 60})),
- ('204 No Content', {}, '')]), acl='act:usr')
- req = Request.blank('/v1/AUTH_cfa/c',
- headers={'X-Auth-Token': 'AUTH_t'})
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
- self.assertEquals(owner_values, [False])
-
- def test_sync_request_success(self):
- self.test_auth.app = FakeApp(
- iter([('204 No Content', {}, '')]),
- sync_key='secret')
- req = Request.blank('/v1/AUTH_cfa/c/o',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'x-container-sync-key':
- 'secret',
- 'x-timestamp': '123.456'})
- req.remote_addr = '127.0.0.1'
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
-
- def test_sync_request_fail_key(self):
- self.test_auth.app = FakeApp(
- iter([('204 No Content', {}, '')]),
- sync_key='secret')
- req = Request.blank('/v1/AUTH_cfa/c/o',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'x-container-sync-key':
- 'wrongsecret',
- 'x-timestamp': '123.456'})
- req.remote_addr = '127.0.0.1'
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
-
- self.test_auth.app = FakeApp(
- iter([('204 No Content', {}, '')]),
- sync_key='othersecret')
- req = Request.blank('/v1/AUTH_cfa/c/o',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'x-container-sync-key':
- 'secret',
- 'x-timestamp': '123.456'})
- req.remote_addr = '127.0.0.1'
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
-
- self.test_auth.app = FakeApp(
- iter([('204 No Content', {}, '')]),
- sync_key=None)
- req = Request.blank('/v1/AUTH_cfa/c/o',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'x-container-sync-key':
- 'secret',
- 'x-timestamp': '123.456'})
- req.remote_addr = '127.0.0.1'
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
-
- def test_sync_request_fail_no_timestamp(self):
- self.test_auth.app = FakeApp(
- iter([('204 No Content', {}, '')]),
- sync_key='secret')
- req = Request.blank('/v1/AUTH_cfa/c/o',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={'x-container-sync-key': 'secret'})
- req.remote_addr = '127.0.0.1'
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
-
- def test_sync_request_fail_sync_host(self):
- self.test_auth.app = FakeApp(
- iter([('204 No Content', {}, '')]),
- sync_key='secret')
- req = Request.blank('/v1/AUTH_cfa/c/o',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'x-container-sync-key':
- 'secret',
- 'x-timestamp': '123.456'})
- req.remote_addr = '127.0.0.2'
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
-
- def test_sync_request_success_lb_sync_host(self):
- self.test_auth.app = FakeApp(
- iter([('204 No Content', {}, '')]),
- sync_key='secret')
- req = Request.blank('/v1/AUTH_cfa/c/o',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'x-container-sync-key':
- 'secret',
- 'x-timestamp':
- '123.456',
- 'x-forwarded-for': '127.0.0.1'})
- req.remote_addr = '127.0.0.2'
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
-
- self.test_auth.app = FakeApp(
- iter([('204 No Content', {}, '')]),
- sync_key='secret')
- req = Request.blank('/v1/AUTH_cfa/c/o',
- environ={
- 'REQUEST_METHOD': 'DELETE'},
- headers={
- 'x-container-sync-key':
- 'secret',
- 'x-timestamp':
- '123.456',
- 'x-cluster-client-ip': '127.0.0.1'})
- req.remote_addr = '127.0.0.2'
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 204)
-
- def _make_request(self, path, **kwargs):
- req = Request.blank(path, **kwargs)
- req.environ['swift.cache'] = FakeMemcache()
- return req
-
- def test_override_asked_for_but_not_allowed(self):
- self.test_auth = \
- auth.filter_factory(
- {'allow_overrides': 'false'})(FakeApp())
- req = self._make_request('/v1/AUTH_account',
- environ={'swift.authorize_override': True})
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
- self.assertEquals(resp.environ['swift.authorize'],
- self.test_auth.authorize)
-
- def test_override_asked_for_and_allowed(self):
- self.test_auth = \
- auth.filter_factory(
- {'allow_overrides': 'true'})(FakeApp())
- req = self._make_request('/v1/AUTH_account',
- environ={'swift.authorize_override': True})
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 404)
- self.assertTrue(
- 'swift.authorize' not in resp.environ)
-
- def test_override_default_allowed(self):
- req = self._make_request('/v1/AUTH_account',
- environ={'swift.authorize_override': True})
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 404)
- self.assertTrue(
- 'swift.authorize' not in resp.environ)
-
- def test_token_too_long(self):
- req = self._make_request('/v1/AUTH_account', headers={
- 'x-auth-token': 'a' * MAX_TOKEN_LENGTH})
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
- self.assertNotEquals(
- resp.body,
- 'Token exceeds maximum length.')
- req = self._make_request('/v1/AUTH_account', headers={
- 'x-auth-token': 'a' * (MAX_TOKEN_LENGTH + 1)})
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 400)
- self.assertEquals(
- resp.body,
- 'Token exceeds maximum length.')
-
- def test_crazy_authorization(self):
- req = self._make_request('/v1/AUTH_account', headers={
- 'authorization': 'somebody elses header value'})
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 401)
- self.assertEquals(resp.environ['swift.authorize'],
- self.test_auth.denied_response)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/gluster/swift/common/middleware/swiftkerbauth/__init__.py b/gluster/swift/common/middleware/swiftkerbauth/__init__.py
deleted file mode 100644
index c752df7..0000000
--- a/gluster/swift/common/middleware/swiftkerbauth/__init__.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2013 Red Hat, Inc.
-#
-# 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 swift.common.utils import readconf, config_true_value
-
-config_file = {}
-try:
- config_file = readconf("/etc/swift/proxy-server.conf",
- section_name="filter:cache")
-except SystemExit:
- pass
-
-MEMCACHE_SERVERS = config_file.get('memcache_servers', None)
-
-config_file = {}
-
-try:
- config_file = readconf("/etc/swift/proxy-server.conf",
- section_name="filter:kerbauth")
-except SystemExit:
- pass
-
-TOKEN_LIFE = int(config_file.get('token_life', 86400))
-RESELLER_PREFIX = config_file.get('reseller_prefix', "AUTH_")
-DEBUG_HEADERS = config_true_value(config_file.get('debug_headers', 'yes'))
diff --git a/gluster/swift/common/middleware/swiftkerbauth/apachekerbauth/etc/httpd/conf.d/swift-auth.conf b/gluster/swift/common/middleware/swiftkerbauth/apachekerbauth/etc/httpd/conf.d/swift-auth.conf
deleted file mode 100644
index 68472d8..0000000
--- a/gluster/swift/common/middleware/swiftkerbauth/apachekerbauth/etc/httpd/conf.d/swift-auth.conf
+++ /dev/null
@@ -1,12 +0,0 @@
-<Location /cgi-bin/swift-auth>
- AuthType Kerberos
- AuthName "Swift Authentication"
- KrbMethodNegotiate On
- KrbMethodK5Passwd On
- KrbSaveCredentials On
- KrbServiceName HTTP/client.example.com
- KrbAuthRealms EXAMPLE.COM
- Krb5KeyTab /etc/httpd/conf/http.keytab
- KrbVerifyKDC Off
- Require valid-user
-</Location>
diff --git a/gluster/swift/common/middleware/swiftkerbauth/apachekerbauth/var/www/cgi-bin/swift-auth b/gluster/swift/common/middleware/swiftkerbauth/apachekerbauth/var/www/cgi-bin/swift-auth
deleted file mode 100755
index 11fe0e2..0000000
--- a/gluster/swift/common/middleware/swiftkerbauth/apachekerbauth/var/www/cgi-bin/swift-auth
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/python
-
-# Copyright (c) 2013 Red Hat, Inc.
-#
-# 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.
-
-# Requires the following command to be run:
-# setsebool -P httpd_can_network_connect 1
-# setsebool -P httpd_can_network_memcache 1
-
-import os
-import cgi
-from swift.common.memcached import MemcacheRing
-from time import time, ctime
-from swiftkerbauth import MEMCACHE_SERVERS, TOKEN_LIFE, DEBUG_HEADERS
-from swiftkerbauth.kerbauth_utils import get_remote_user, get_auth_data, \
- generate_token, set_auth_data, get_groups_from_username
-
-
-def main():
- try:
- username = get_remote_user(os.environ)
- except RuntimeError:
- print "Status: 401 Unauthorized\n"
- print "Malformed REMOTE_USER"
- return
-
- if not MEMCACHE_SERVERS:
- print "Status: 500 Internal Server Error\n"
- print "Memcache not configured in /etc/swift/proxy-server.conf"
- return
-
- mc_servers = [s.strip() for s in MEMCACHE_SERVERS.split(',') if s.strip()]
- mc = MemcacheRing(mc_servers)
-
- token, expires, groups = get_auth_data(mc, username)
-
- if not token:
- token = generate_token()
- expires = time() + TOKEN_LIFE
- groups = get_groups_from_username(username)
- set_auth_data(mc, username, token, expires, groups)
-
- print "X-Auth-Token: %s" % token
- print "X-Storage-Token: %s" % token
-
- # For debugging.
- if DEBUG_HEADERS:
- print "X-Debug-Remote-User: %s" % username
- print "X-Debug-Groups: %s" % groups
- print "X-Debug-Token-Life: %ss" % TOKEN_LIFE
- print "X-Debug-Token-Expires: %s" % ctime(expires)
-
- print ""
-
-try:
- print("Content-Type: text/html")
- main()
-except:
- cgi.print_exception()
diff --git a/gluster/swift/common/middleware/swiftkerbauth/kerbauth.py b/gluster/swift/common/middleware/swiftkerbauth/kerbauth.py
deleted file mode 100644
index 1a63a40..0000000
--- a/gluster/swift/common/middleware/swiftkerbauth/kerbauth.py
+++ /dev/null
@@ -1,463 +0,0 @@
-# Copyright (c) 2013 Red Hat, Inc.
-#
-# 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.
-
-import errno
-from time import time, ctime
-from traceback import format_exc
-from eventlet import Timeout
-from urllib import unquote
-
-from swift.common.swob import Request, Response
-from swift.common.swob import HTTPBadRequest, HTTPForbidden, HTTPNotFound, \
- HTTPSeeOther, HTTPUnauthorized, HTTPServerError
-
-from swift.common.middleware.acl import clean_acl, parse_acl, referrer_allowed
-from swift.common.utils import cache_from_env, get_logger, \
- split_path, config_true_value
-from gluster.swift.common.middleware.swiftkerbauth.kerbauth_utils import \
- get_auth_data, generate_token, \
- set_auth_data, run_kinit, get_groups_from_username
-
-
-class KerbAuth(object):
- """
- Test authentication and authorization system.
-
- Add to your pipeline in proxy-server.conf, such as::
-
- [pipeline:main]
- pipeline = catch_errors cache kerbauth proxy-server
-
- Set account auto creation to true in proxy-server.conf::
-
- [app:proxy-server]
- account_autocreate = true
-
- And add a kerbauth filter section, such as::
-
- [filter:kerbauth]
- use = egg:swiftkerbauth#kerbauth
-
- See the proxy-server.conf-sample for more information.
-
- :param app: The next WSGI app in the pipeline
- :param conf: The dict of configuration values
- """
-
- def __init__(self, app, conf):
- self.app = app
- self.conf = conf
- self.logger = get_logger(conf, log_route='kerbauth')
- self.log_headers = config_true_value(conf.get('log_headers', 'f'))
- self.reseller_prefix = conf.get('reseller_prefix', 'AUTH').strip()
- if self.reseller_prefix and self.reseller_prefix[-1] != '_':
- self.reseller_prefix += '_'
- self.logger.set_statsd_prefix('kerbauth.%s' % (
- self.reseller_prefix if self.reseller_prefix else 'NONE',))
- self.auth_prefix = conf.get('auth_prefix', '/auth/')
- if not self.auth_prefix or not self.auth_prefix.strip('/'):
- self.logger.warning('Rewriting invalid auth prefix "%s" to '
- '"/auth/" (Non-empty auth prefix path '
- 'is required)' % self.auth_prefix)
- self.auth_prefix = '/auth/'
- if self.auth_prefix[0] != '/':
- self.auth_prefix = '/' + self.auth_prefix
- if self.auth_prefix[-1] != '/':
- self.auth_prefix += '/'
- self.token_life = int(conf.get('token_life', 86400))
- self.auth_method = conf.get('auth_method', 'passive')
- self.debug_headers = config_true_value(
- conf.get('debug_headers', 'yes'))
- self.realm_name = conf.get('realm_name', None)
- self.allow_overrides = config_true_value(
- conf.get('allow_overrides', 't'))
- self.storage_url_scheme = conf.get('storage_url_scheme', 'default')
- self.ext_authentication_url = conf.get('ext_authentication_url')
- if not self.ext_authentication_url:
- raise RuntimeError("Missing filter parameter ext_authentication_"
- "url in /etc/swift/proxy-server.conf")
-
- def __call__(self, env, start_response):
- """
- Accepts a standard WSGI application call, authenticating the request
- and installing callback hooks for authorization and ACL header
- validation. For an authenticated request, REMOTE_USER will be set to a
- comma separated list of the user's groups.
-
- If the request matches the self.auth_prefix, the request will be
- routed through the internal auth request handler (self.handle).
- This is to handle granting tokens, etc.
- """
- if self.allow_overrides and env.get('swift.authorize_override', False):
- return self.app(env, start_response)
- if env.get('PATH_INFO', '').startswith(self.auth_prefix):
- return self.handle(env, start_response)
- token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN'))
- if token and token.startswith(self.reseller_prefix):
- groups = self.get_groups(env, token)
- if groups:
- user = groups and groups.split(',', 1)[0] or ''
- trans_id = env.get('swift.trans_id')
- self.logger.debug('User: %s uses token %s (trans_id %s)' %
- (user, token, trans_id))
- env['REMOTE_USER'] = groups
- env['swift.authorize'] = self.authorize
- env['swift.clean_acl'] = clean_acl
- if '.reseller_admin' in groups:
- env['reseller_request'] = True
- else:
- # Invalid token (may be expired)
- if self.auth_method == "active":
- return HTTPSeeOther(
- location=self.ext_authentication_url)(env,
- start_response)
- elif self.auth_method == "passive":
- self.logger.increment('unauthorized')
- return HTTPUnauthorized()(env, start_response)
- else:
- # With a non-empty reseller_prefix, I would like to be called
- # back for anonymous access to accounts I know I'm the
- # definitive auth for.
- try:
- version, rest = split_path(env.get('PATH_INFO', ''),
- 1, 2, True)
- except ValueError:
- version, rest = None, None
- self.logger.increment('errors')
- # Not my token, not my account, I can't authorize this request,
- # deny all is a good idea if not already set...
- if 'swift.authorize' not in env:
- env['swift.authorize'] = self.denied_response
-
- return self.app(env, start_response)
-
- def get_groups(self, env, token):
- """
- Get groups for the given token.
-
- :param env: The current WSGI environment dictionary.
- :param token: Token to validate and return a group string for.
-
- :returns: None if the token is invalid or a string containing a comma
- separated list of groups the authenticated user is a member
- of. The first group in the list is also considered a unique
- identifier for that user.
- """
- groups = None
- memcache_client = cache_from_env(env)
- if not memcache_client:
- raise Exception('Memcache required')
- memcache_token_key = '%s/token/%s' % (self.reseller_prefix, token)
- cached_auth_data = memcache_client.get(memcache_token_key)
- if cached_auth_data:
- expires, groups = cached_auth_data
- if expires < time():
- groups = None
-
- return groups
-
- def authorize(self, req):
- """
- Returns None if the request is authorized to continue or a standard
- WSGI response callable if not.
-
- Assumes that user groups are all lower case, which is true when Red Hat
- Enterprise Linux Identity Management is used.
- """
- try:
- version, account, container, obj = req.split_path(1, 4, True)
- except ValueError:
- self.logger.increment('errors')
- return HTTPNotFound(request=req)
-
- if not account or not account.startswith(self.reseller_prefix):
- self.logger.debug("Account name: %s doesn't start with "
- "reseller_prefix: %s."
- % (account, self.reseller_prefix))
- return self.denied_response(req)
-
- user_groups = (req.remote_user or '').split(',')
- account_user = user_groups[1] if len(user_groups) > 1 else None
- # If the user is in the reseller_admin group for our prefix, he gets
- # full access to all accounts we manage. For the default reseller
- # prefix, the group name is auth_reseller_admin.
- admin_group = ("%sreseller_admin" % self.reseller_prefix).lower()
- if admin_group in user_groups and \
- account != self.reseller_prefix and \
- account[len(self.reseller_prefix)] != '.':
- req.environ['swift_owner'] = True
- return None
-
- # The "account" is part of the request URL, and already contains the
- # reseller prefix, like in "/v1/AUTH_vol1/pictures/pic1.png".
- if account.lower() in user_groups and \
- (req.method not in ('DELETE', 'PUT') or container):
- # If the user is admin for the account and is not trying to do an
- # account DELETE or PUT...
- req.environ['swift_owner'] = True
- self.logger.debug("User %s has admin authorizing."
- % account_user)
- return None
-
- if (req.environ.get('swift_sync_key')
- and (req.environ['swift_sync_key'] ==
- req.headers.get('x-container-sync-key', None))
- and 'x-timestamp' in req.headers):
- self.logger.debug("Allow request with container sync-key: %s."
- % req.environ['swift_sync_key'])
- return None
-
- if req.method == 'OPTIONS':
- #allow OPTIONS requests to proceed as normal
- self.logger.debug("Allow OPTIONS request.")
- return None
-
- referrers, groups = parse_acl(getattr(req, 'acl', None))
-
- if referrer_allowed(req.referer, referrers):
- if obj or '.rlistings' in groups:
- self.logger.debug("Allow authorizing %s via referer ACL."
- % req.referer)
- return None
-
- for user_group in user_groups:
- if user_group in groups:
- self.logger.debug("User %s allowed in ACL: %s authorizing."
- % (account_user, user_group))
- return None
-
- return self.denied_response(req)
-
- def denied_response(self, req):
- """
- Returns a standard WSGI response callable with the status of 403 or 401
- depending on whether the REMOTE_USER is set or not.
- """
- if req.remote_user:
- self.logger.increment('forbidden')
- return HTTPForbidden(request=req)
- else:
- if self.auth_method == "active":
- return HTTPSeeOther(location=self.ext_authentication_url)
- elif self.auth_method == "passive":
- self.logger.increment('unauthorized')
- return HTTPUnauthorized(request=req)
-
- def handle(self, env, start_response):
- """
- WSGI entry point for auth requests (ones that match the
- self.auth_prefix).
- Wraps env in swob.Request object and passes it down.
-
- :param env: WSGI environment dictionary
- :param start_response: WSGI callable
- """
- try:
- req = Request(env)
- if self.auth_prefix:
- req.path_info_pop()
- req.bytes_transferred = '-'
- req.client_disconnect = False
- if 'x-storage-token' in req.headers and \
- 'x-auth-token' not in req.headers:
- req.headers['x-auth-token'] = req.headers['x-storage-token']
- return self.handle_request(req)(env, start_response)
- except (Exception, Timeout):
- print "EXCEPTION IN handle: %s: %s" % (format_exc(), env)
- self.logger.increment('errors')
- start_response('500 Server Error',
- [('Content-Type', 'text/plain')])
- return ['Internal server error.\n']
-
- def handle_request(self, req):
- """
- Entry point for auth requests (ones that match the self.auth_prefix).
- Should return a WSGI-style callable (such as webob.Response).
-
- :param req: swob.Request object
- """
- req.start_time = time()
- handler = None
- try:
- version, account, user, _junk = req.split_path(1, 4, True)
- except ValueError:
- self.logger.increment('errors')
- return HTTPNotFound(request=req)
- if version in ('v1', 'v1.0', 'auth'):
- if req.method == 'GET':
- handler = self.handle_get_token
- if not handler:
- self.logger.increment('errors')
- req.response = HTTPBadRequest(request=req)
- else:
- req.response = handler(req)
- return req.response
-
- def handle_get_token(self, req):
- """
- Handles the various `request for token and service end point(s)` calls.
- There are various formats to support the various auth servers in the
- past.
-
- "Active Mode" usage:
- All formats require GSS (Kerberos) authentication.
-
- GET <auth-prefix>/v1/<act>/auth
- GET <auth-prefix>/auth
- GET <auth-prefix>/v1.0
-
- On successful authentication, the response will have X-Auth-Token
- and X-Storage-Token set to the token to use with Swift.
-
- "Passive Mode" usage::
-
- GET <auth-prefix>/v1/<act>/auth
- X-Auth-User: <act>:<usr> or X-Storage-User: <usr>
- X-Auth-Key: <key> or X-Storage-Pass: <key>
- GET <auth-prefix>/auth
- X-Auth-User: <act>:<usr> or X-Storage-User: <act>:<usr>
- X-Auth-Key: <key> or X-Storage-Pass: <key>
- GET <auth-prefix>/v1.0
- X-Auth-User: <act>:<usr> or X-Storage-User: <act>:<usr>
- X-Auth-Key: <key> or X-Storage-Pass: <key>
-
- Values should be url encoded, "act%3Ausr" instead of "act:usr" for
- example; however, for backwards compatibility the colon may be
- included unencoded.
-
- On successful authentication, the response will have X-Auth-Token
- and X-Storage-Token set to the token to use with Swift and
- X-Storage-URL set to the URL to the default Swift cluster to use.
-
- :param req: The swob.Request to process.
- :returns: swob.Response, 2xx on success with data set as explained
- above.
- """
- # Validate the request info
- try:
- pathsegs = split_path(req.path_info, 1, 3, True)
- except ValueError:
- self.logger.increment('errors')
- return HTTPNotFound(request=req)
- if not ((pathsegs[0] == 'v1' and pathsegs[2] == 'auth')
- or pathsegs[0] in ('auth', 'v1.0')):
- return HTTPBadRequest(request=req)
-
- # Client is inside the domain
- if self.auth_method == "active":
- return HTTPSeeOther(location=self.ext_authentication_url)
-
- # Client is outside the domain
- elif self.auth_method == "passive":
- account, user, key = None, None, None
- # Extract user, account and key from request
- if pathsegs[0] == 'v1' and pathsegs[2] == 'auth':
- account = pathsegs[1]
- user = req.headers.get('x-storage-user')
- if not user:
- user = unquote(req.headers.get('x-auth-user', ''))
- if user:
- if ':' not in user:
- return HTTPUnauthorized(request=req)
- else:
- account2, user = user.split(':', 1)
- if account != account2:
- return HTTPUnauthorized(request=req)
- key = req.headers.get('x-storage-pass')
- if not key:
- key = unquote(req.headers.get('x-auth-key', ''))
- elif pathsegs[0] in ('auth', 'v1.0'):
- user = unquote(req.headers.get('x-auth-user', ''))
- if not user:
- user = req.headers.get('x-storage-user')
- if user:
- if ':' not in user:
- return HTTPUnauthorized(request=req)
- else:
- account, user = user.split(':', 1)
- key = unquote(req.headers.get('x-auth-key', ''))
- if not key:
- key = req.headers.get('x-storage-pass')
-
- if not (account or user or key):
- # If all are not given, client may be part of the domain
- return HTTPSeeOther(location=self.ext_authentication_url)
- elif None in (key, user, account):
- # If only one or two of them is given, but not all
- return HTTPUnauthorized(request=req)
-
- # Run kinit on the user
- if self.realm_name and "@" not in user:
- user = user + "@" + self.realm_name
- try:
- ret = run_kinit(user, key)
- except OSError as e:
- if e.errno == errno.ENOENT:
- return HTTPServerError("kinit command not found\n")
- if ret != 0:
- self.logger.warning("Failed: kinit %s", user)
- if ret == -1:
- self.logger.warning("Failed: kinit: Password has probably "
- "expired.")
- return HTTPServerError("Kinit is taking too long.\n")
- return HTTPUnauthorized(request=req)
- self.logger.debug("kinit succeeded")
-
- if "@" in user:
- user = user.split("@")[0]
-
- # Check if user really belongs to the account
- groups_list = get_groups_from_username(user).strip().split(",")
- user_group = ("%s%s" % (self.reseller_prefix, account)).lower()
- reseller_admin_group = \
- ("%sreseller_admin" % self.reseller_prefix).lower()
- if user_group not in groups_list:
- # Check if user is reseller_admin. If not, return Unauthorized.
- # On AD/IdM server, auth_reseller_admin is a separate group
- if reseller_admin_group not in groups_list:
- return HTTPUnauthorized(request=req)
-
- mc = cache_from_env(req.environ)
- if not mc:
- raise Exception('Memcache required')
- token, expires, groups = get_auth_data(mc, user)
- if not token:
- token = generate_token()
- expires = time() + self.token_life
- groups = get_groups_from_username(user)
- set_auth_data(mc, user, token, expires, groups)
-
- headers = {'X-Auth-Token': token,
- 'X-Storage-Token': token}
-
- if self.debug_headers:
- headers.update({'X-Debug-Remote-User': user,
- 'X-Debug-Groups:': groups,
- 'X-Debug-Token-Life': self.token_life,
- 'X-Debug-Token-Expires': ctime(expires)})
-
- resp = Response(request=req, headers=headers)
- resp.headers['X-Storage-Url'] = \
- '%s/v1/%s%s' % (resp.host_url, self.reseller_prefix, account)
- return resp
-
-
-def filter_factory(global_conf, **local_conf):
- """Returns a WSGI filter app for use with paste.deploy."""
- conf = global_conf.copy()
- conf.update(local_conf)
-
- def auth_filter(app):
- return KerbAuth(app, conf)
- return auth_filter
diff --git a/gluster/swift/common/middleware/swiftkerbauth/kerbauth_utils.py b/gluster/swift/common/middleware/swiftkerbauth/kerbauth_utils.py
deleted file mode 100644
index 599ef99..0000000
--- a/gluster/swift/common/middleware/swiftkerbauth/kerbauth_utils.py
+++ /dev/null
@@ -1,137 +0,0 @@
-# Copyright (c) 2013 Red Hat, Inc.
-#
-# 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.
-
-import re
-import random
-import grp
-import signal
-from subprocess import Popen, PIPE
-from time import time
-from gluster.swift.common.middleware.swiftkerbauth \
- import TOKEN_LIFE, RESELLER_PREFIX
-
-
-def get_remote_user(env):
- """Retrieve REMOTE_USER set by Apache from environment."""
- remote_user = env.get('REMOTE_USER', "")
- matches = re.match('([^@]+)@.*', remote_user)
- if not matches:
- raise RuntimeError("Malformed REMOTE_USER \"%s\"" % remote_user)
- return matches.group(1)
-
-
-def get_auth_data(mc, username):
- """
- Returns the token, expiry time and groups for the user if it already exists
- on memcache. Returns None otherwise.
-
- :param mc: MemcacheRing object
- :param username: swift user
- """
- token, expires, groups = None, None, None
- memcache_user_key = '%s/user/%s' % (RESELLER_PREFIX, username)
- candidate_token = mc.get(memcache_user_key)
- if candidate_token:
- memcache_token_key = '%s/token/%s' % (RESELLER_PREFIX, candidate_token)
- cached_auth_data = mc.get(memcache_token_key)
- if cached_auth_data:
- expires, groups = cached_auth_data
- if expires > time():
- token = candidate_token
- else:
- expires, groups = None, None
- return (token, expires, groups)
-
-
-def set_auth_data(mc, username, token, expires, groups):
- """
- Stores the following key value pairs on Memcache:
- (token, expires+groups)
- (user, token)
- """
- auth_data = (expires, groups)
- memcache_token_key = "%s/token/%s" % (RESELLER_PREFIX, token)
- mc.set(memcache_token_key, auth_data, time=TOKEN_LIFE)
-
- # Record the token with the user info for future use.
- memcache_user_key = '%s/user/%s' % (RESELLER_PREFIX, username)
- mc.set(memcache_user_key, token, time=TOKEN_LIFE)
-
-
-def generate_token():
- """Generates a random token."""
- # We don't use uuid.uuid4() here because importing the uuid module
- # causes (harmless) SELinux denials in the audit log on RHEL 6. If this
- # is a security concern, a custom SELinux policy module could be
- # written to not log those denials.
- r = random.SystemRandom()
- token = '%stk%s' % \
- (RESELLER_PREFIX,
- ''.join(r.choice('abcdef0123456789') for x in range(32)))
- return token
-
-
-def get_groups_from_username(username):
- """Return a set of groups to which the user belongs to."""
- # Retrieve the numerical group IDs. We cannot list the group names
- # because group names from Active Directory may contain spaces, and
- # we wouldn't be able to split the list of group names into its
- # elements.
- p = Popen(['id', '-G', username], stdout=PIPE)
- if p.wait() != 0:
- raise RuntimeError("Failure running id -G for %s" % username)
- (p_stdout, p_stderr) = p.communicate()
-
- # Convert the group numbers into group names.
- groups = []
- for gid in p_stdout.strip().split(" "):
- groups.append(grp.getgrgid(int(gid))[0])
-
- # The first element of the list is considered a unique identifier
- # for the user. We add the username to accomplish this.
- if username in groups:
- groups.remove(username)
- groups = [username] + groups
- groups = ','.join(groups)
- return groups
-
-
-def run_kinit(username, password):
- """Runs kinit command as a child process and returns the status code."""
- kinit = Popen(['kinit', username],
- stdin=PIPE, stdout=PIPE, stderr=PIPE)
- kinit.stdin.write('%s\n' % password)
-
- # The following code handles a corner case where the Kerberos password
- # has expired and a prompt is displayed to enter new password. Ideally,
- # we would want to read from stdout but these are blocked reads. This is
- # a hack to kill the process if it's taking too long!
-
- class Alarm(Exception):
- pass
-
- def signal_handler(signum, frame):
- raise Alarm
- # Set the signal handler and a 1-second alarm
- signal.signal(signal.SIGALRM, signal_handler)
- signal.alarm(1)
- try:
- kinit.wait() # Wait for the child to exit
- signal.alarm(0) # Reset the alarm
- return kinit.returncode # Exit status of child on graceful exit
- except Alarm:
- # Taking too long, kill and return error
- kinit.kill()
- return -1
diff --git a/gluster/swift/common/utils.py b/gluster/swift/common/utils.py
index 145add3..95c8739 100644
--- a/gluster/swift/common/utils.py
+++ b/gluster/swift/common/utils.py
@@ -47,6 +47,7 @@ DIR_NON_OBJECT = 'dir'
DIR_OBJECT = 'marker_dir'
TEMP_DIR = 'tmp'
ASYNCDIR = 'async_pending' # Keep in sync with swift.obj.server.ASYNCDIR
+TRASHCAN = '.trashcan'
FILE = 'file'
FILE_TYPE = 'application/octet-stream'
OBJECT = 'Object'
@@ -212,7 +213,7 @@ def validate_account(metadata):
return False
-def validate_object(metadata):
+def validate_object(metadata, stat=None):
if not metadata:
return False
@@ -224,6 +225,12 @@ def validate_object(metadata):
X_OBJECT_TYPE not in metadata.keys():
return False
+ if stat and (int(metadata[X_CONTENT_LENGTH]) != stat.st_size):
+ # File length has changed.
+ # TODO: Handle case where file content has changed but the length
+ # remains the same.
+ return False
+
if metadata[X_TYPE] == OBJECT:
return True
@@ -242,8 +249,16 @@ def _update_list(path, cont_path, src_list, reg_file=True, object_count=0,
if not reg_file and not Glusterfs._implicit_dir_objects:
# Now check if this is a dir object or a gratuiously crated
# directory
- metadata = \
- read_metadata(os.path.join(cont_path, obj_path, obj_name))
+ try:
+ metadata = \
+ read_metadata(os.path.join(cont_path, obj_path, obj_name))
+ except GlusterFileSystemIOError as err:
+ if err.errno in (errno.ENOENT, errno.ESTALE):
+ # object might have been deleted by another process
+ # since the src_list was originally built
+ continue
+ else:
+ raise err
if not dir_is_object(metadata):
continue
@@ -304,6 +319,7 @@ def get_account_details(acc_path):
for name in do_listdir(acc_path):
if name.lower() == TEMP_DIR \
or name.lower() == ASYNCDIR \
+ or name.lower() == TRASHCAN \
or not do_isdir(os.path.join(acc_path, name)):
continue
container_count += 1
@@ -476,7 +492,7 @@ def rmobjdir(dir_path):
try:
do_rmdir(dir_path)
except OSError as err:
- if err.errno == errno.ENOENT:
+ if err.errno in (errno.ENOENT, errno.ESTALE):
# No such directory exists
return False
if err.errno != errno.ENOTEMPTY:
@@ -494,8 +510,8 @@ def rmobjdir(dir_path):
try:
metadata = read_metadata(fullpath)
- except OSError as err:
- if err.errno == errno.ENOENT:
+ except GlusterFileSystemIOError as err:
+ if err.errno in (errno.ENOENT, errno.ESTALE):
# Ignore removal from another entity.
continue
raise
@@ -513,7 +529,7 @@ def rmobjdir(dir_path):
if err.errno == errno.ENOTEMPTY:
# Directory is not empty, it might have objects in it
return False
- if err.errno == errno.ENOENT:
+ if err.errno in (errno.ENOENT, errno.ESTALE):
# No such directory exists, already removed, ignore
continue
raise
@@ -524,7 +540,7 @@ def rmobjdir(dir_path):
if err.errno == errno.ENOTEMPTY:
# Directory is not empty, race with object creation
return False
- if err.errno == errno.ENOENT:
+ if err.errno in (errno.ENOENT, errno.ESTALE):
# No such directory exists, already removed, ignore
return True
raise
diff --git a/gluster/swift/obj/diskfile.py b/gluster/swift/obj/diskfile.py
index 47cde89..eb180a2 100644
--- a/gluster/swift/obj/diskfile.py
+++ b/gluster/swift/obj/diskfile.py
@@ -447,9 +447,9 @@ class DiskFileWriter(object):
df._threadpool.force_run_in_thread(self._finalize_put, metadata)
- # Avoid the unlink() system call as part of the mkstemp context
- # cleanup
- self.tmppath = None
+ # Avoid the unlink() system call as part of the DiskFile.create()
+ # context cleanup
+ self._tmppath = None
class DiskFileReader(object):
@@ -509,7 +509,7 @@ class DiskFileReader(object):
bytes_read += len(chunk)
diff = bytes_read - dropped_cache
if diff > (1024 * 1024):
- self._drop_cache(self._fd, dropped_cache, diff)
+ self._drop_cache(dropped_cache, diff)
dropped_cache = bytes_read
yield chunk
if self._iter_hook:
@@ -604,6 +604,7 @@ class DiskFile(object):
self._logger = mgr.logger
self._metadata = None
self._fd = None
+ self._stat = None
# Don't store a value for data_file until we know it exists.
self._data_file = None
@@ -644,37 +645,49 @@ class DiskFile(object):
"""
# Writes are always performed to a temporary file
try:
- fd = do_open(self._data_file, os.O_RDONLY | O_CLOEXEC)
+ self._fd = do_open(self._data_file, os.O_RDONLY | O_CLOEXEC)
except GlusterFileSystemOSError as err:
if err.errno in (errno.ENOENT, errno.ENOTDIR):
# If the file does exist, or some part of the path does not
# exist, raise the expected DiskFileNotExist
raise DiskFileNotExist
raise
- else:
- stats = do_fstat(fd)
- if not stats:
- return
- self._is_dir = stat.S_ISDIR(stats.st_mode)
- obj_size = stats.st_size
-
- self._metadata = read_metadata(fd)
- if not validate_object(self._metadata):
- create_object_metadata(fd)
- self._metadata = read_metadata(fd)
- assert self._metadata is not None
- self._filter_metadata()
-
- if self._is_dir:
- do_close(fd)
- obj_size = 0
- self._fd = -1
- else:
- if self._is_object_expired(self._metadata):
- raise DiskFileExpired(metadata=self._metadata)
- self._fd = fd
-
- self._obj_size = obj_size
+ try:
+ self._stat = do_fstat(self._fd)
+ self._is_dir = stat.S_ISDIR(self._stat.st_mode)
+ obj_size = self._stat.st_size
+
+ self._metadata = read_metadata(self._fd)
+ if not validate_object(self._metadata, self._stat):
+ create_object_metadata(self._fd)
+ self._metadata = read_metadata(self._fd)
+ assert self._metadata is not None
+ self._filter_metadata()
+
+ if self._is_dir:
+ do_close(self._fd)
+ obj_size = 0
+ self._fd = -1
+ else:
+ if self._is_object_expired(self._metadata):
+ raise DiskFileExpired(metadata=self._metadata)
+ self._obj_size = obj_size
+ except (OSError, IOError, DiskFileExpired) as err:
+ # Something went wrong. Context manager will not call
+ # __exit__. So we close the fd manually here.
+ self._close_fd()
+ if hasattr(err, 'errno') and \
+ err.errno in (errno.ENOENT, errno.ESTALE):
+ # Handle races: ENOENT/ESTALE can be raised by read_metadata()
+ # call in GlusterFS if file gets deleted by another
+ # client after do_open() succeeds
+ logging.warn("open(%s) succeeded but one of the subsequent "
+ "syscalls failed with ENOENT/ESTALE. Raising "
+ "DiskFileNotExist." % (self._data_file))
+ raise DiskFileNotExist
+ else:
+ # Re-raise the original exception after fd has been closed
+ raise err
return self
def _is_object_expired(self, metadata):
@@ -712,6 +725,12 @@ class DiskFile(object):
raise DiskFileNotOpen()
return self
+ def _close_fd(self):
+ if self._fd is not None:
+ fd, self._fd = self._fd, None
+ if fd > -1:
+ do_close(fd)
+
def __exit__(self, t, v, tb):
"""
Context exit.
@@ -723,10 +742,7 @@ class DiskFile(object):
responsibility of the implementation to properly handle that.
"""
self._metadata = None
- if self._fd is not None:
- fd, self._fd = self._fd, None
- if fd > -1:
- do_close(fd)
+ self._close_fd()
def get_metadata(self):
"""
@@ -833,10 +849,6 @@ class DiskFile(object):
" on subpath: %s" % (full_path, cur_path))
child = stack.pop() if stack else None
return True, newmd
- # Exists, but as a file
- #raise DiskFileError('DiskFile.put(): directory creation failed'
- # ' since the target, %s, already exists as'
- # ' a file' % df._data_file)
@contextmanager
def create(self, size=None):
@@ -893,7 +905,7 @@ class DiskFile(object):
if attempts >= MAX_OPEN_ATTEMPTS:
# We failed after N attempts to create the temporary
# file.
- raise DiskFileError('DiskFile.mkstemp(): failed to'
+ raise DiskFileError('DiskFile.create(): failed to'
' successfully create a temporary file'
' without running into a name conflict'
' after %d of %d attempts for: %s' % (
@@ -906,7 +918,7 @@ class DiskFile(object):
# FIXME: Possible FUSE issue or race condition, let's
# sleep on it and retry the operation.
_random_sleep()
- logging.warn("DiskFile.mkstemp(): %s ... retrying in"
+ logging.warn("DiskFile.create(): %s ... retrying in"
" 0.1 secs", gerr)
attempts += 1
elif not self._obj_path:
@@ -915,7 +927,7 @@ class DiskFile(object):
# could be a FUSE issue or some race condition, so let's
# sleep a bit and retry.
_random_sleep()
- logging.warn("DiskFile.mkstemp(): %s ... retrying in"
+ logging.warn("DiskFile.create(): %s ... retrying in"
" 0.1 secs", gerr)
attempts += 1
elif attempts > 1:
@@ -923,7 +935,7 @@ class DiskFile(object):
# also be a FUSE issue or some race condition, nap and
# retry.
_random_sleep()
- logging.warn("DiskFile.mkstemp(): %s ... retrying in"
+ logging.warn("DiskFile.create(): %s ... retrying in"
" 0.1 secs" % gerr)
attempts += 1
else:
diff --git a/glusterfs-openstack-swift.spec b/glusterfs-openstack-swift.spec
index bca64c3..79defc4 100644
--- a/glusterfs-openstack-swift.spec
+++ b/glusterfs-openstack-swift.spec
@@ -24,11 +24,11 @@ Requires : memcached
Requires : openssl
Requires : python
Requires : python-prettytable
-Requires : openstack-swift = 1.10.0
-Requires : openstack-swift-account = 1.10.0
-Requires : openstack-swift-container = 1.10.0
-Requires : openstack-swift-object = 1.10.0
-Requires : openstack-swift-proxy = 1.10.0
+Requires : openstack-swift = 1.13.1
+Requires : openstack-swift-account = 1.13.1
+Requires : openstack-swift-container = 1.13.1
+Requires : openstack-swift-object = 1.13.1
+Requires : openstack-swift-proxy = 1.13.1
Requires : glusterfs-api >= 3.4.1
Obsoletes: glusterfs-swift-plugin
Obsoletes: glusterfs-swift
@@ -39,11 +39,11 @@ Obsoletes: glusterfs-swift-proxy
Obsoletes: glusterfs-swift-account
%description
-Gluster-For-Swift (G4S, pronounced "gee-force") integrates GlusterFS as an
-alternative back end for OpenStack Object Storage (Swift) leveraging the
-existing front end OpenStack Swift code. Gluster volumes are used to store
-objects in files, containers are maintained as top-level directories of volumes,
-where accounts are mapped one-to-one to gluster volumes.
+SwiftOnFile integrates GlusterFS as an alternative back end for OpenStack
+Object Storage (Swift) leveraging the existing front end OpenStack Swift code.
+Gluster volumes are used to store objects in files, containers are maintained
+as top-level directories of volumes, where accounts are mapped one-to-one to
+gluster volumes.
%prep
%setup -q -n gluster_swift-%{_version}
@@ -94,10 +94,14 @@ done
%config(noreplace) %{_confdir}/swift.conf-gluster
%config(noreplace) %{_confdir}/proxy-server.conf-gluster
%config(noreplace) %{_confdir}/fs.conf-gluster
+%config(noreplace) %{_confdir}/object-expirer.conf-gluster
%changelog
+* Fri May 23 2014 Thiago da Silva <thiago@redhat.com> - 1.13.1-1
+- Update to Icehouse release
+
* Mon Oct 28 2013 Luis Pabon <lpabon@redhat.com> - 1.10.1-0
-- IceHouse Release
+- Havana Release
* Wed Aug 21 2013 Luis Pabon <lpabon@redhat.com> - 1.8.0-7
- Update RPM spec file to support SRPMS
diff --git a/tools/requirements.txt b/requirements.txt
index bbac51a..bbac51a 100644
--- a/tools/requirements.txt
+++ b/requirements.txt
diff --git a/setup.py b/setup.py
index 0b52115..5673bdc 100644
--- a/setup.py
+++ b/setup.py
@@ -64,8 +64,6 @@ setup(
'paste.filter_factory': [
'gswauth=gluster.swift.common.middleware.gswauth.swauth.'
'middleware:filter_factory',
- 'kerbauth=gluster.swift.common.middleware.'
- 'swiftkerbauth.kerbauth:filter_factory',
],
},
)
diff --git a/tools/test-requires b/test-requirements.txt
index 63d499e..63d499e 100644
--- a/tools/test-requires
+++ b/test-requirements.txt
diff --git a/test/functional/gluster_swift_tests.py b/test/functional/gluster_swift_tests.py
index 2768f9d..b4514c9 100644
--- a/test/functional/gluster_swift_tests.py
+++ b/test/functional/gluster_swift_tests.py
@@ -58,44 +58,13 @@ class TestFile(Base):
data_read = file.read()
self.assertEquals(data,data_read)
- def testInvalidHeadersPUT(self):
- #TODO: Although we now support x-delete-at and x-delete-after,
- #retained this test case as we may add some other header to
- #unsupported list in future
- raise SkipTest()
- file = self.env.container.file(Utils.create_name())
- self.assertRaises(ResponseError,
- file.write_random,
- self.env.file_size,
- hdrs={'X-Delete-At': '9876545321'})
- self.assert_status(400)
- self.assertRaises(ResponseError,
- file.write_random,
- self.env.file_size,
- hdrs={'X-Delete-After': '60'})
- self.assert_status(400)
-
- def testInvalidHeadersPOST(self):
- #TODO: Although we now support x-delete-at and x-delete-after,
- #retained this test case as we may add some other header to
- #unsupported list in future
- raise SkipTest()
- file = self.env.container.file(Utils.create_name())
- file.write_random(self.env.file_size)
- headers = file.make_headers(cfg={})
- headers.update({ 'X-Delete-At' : '987654321'})
- # Need to call conn.make_request instead of file.sync_metadata
- # because sync_metadata calls make_headers. make_headers()
- # overwrites any headers in file.metadata as 'user' metadata
- # by appending 'X-Object-Meta-' to any of the headers
- # in file.metadata.
- file.conn.make_request('POST', file.path, hdrs=headers, cfg={})
- self.assertEqual(400, file.conn.response.status)
-
- headers = file.make_headers(cfg={})
- headers.update({ 'X-Delete-After' : '60'})
- file.conn.make_request('POST', file.path, hdrs=headers, cfg={})
- self.assertEqual(400, file.conn.response.status)
+ def test_PUT_large_object(self):
+ file_item = self.env.container.file(Utils.create_name())
+ data = File.random_data(1024 * 1024 * 2)
+ self.assertTrue(file_item.write(data))
+ self.assert_status(201)
+ self.assertTrue(data == file_item.read())
+ self.assert_status(200)
class TestFileUTF8(Base2, TestFile):
@@ -375,3 +344,49 @@ class TestMultiProtocolAccess(Base):
md5_returned = hashlib.md5(data_read_from_mountP).hexdigest()
self.assertEquals(md5_returned,file_info['etag'])
fhOnMountPoint.close()
+
+ def testObjectMetadataWhenFileModified(self):
+ data = "I'm whatever Gotham needs me to be "
+ data_hash = hashlib.md5(data).hexdigest()
+ # Create an object through object interface
+ object_name = Utils.create_name()
+ object_item = self.env.container.file(object_name)
+ object_item.write(data)
+ # Make sure GET works
+ self.assertEqual(data, object_item.read())
+ self.assert_status(200)
+ # Check Etag is right
+ self.assertEqual(data_hash, object_item.info()['etag'])
+ self.assert_status(200)
+
+ # Extend/append more data to file from filesystem interface
+ file_path = os.path.join(self.env.root_dir,
+ self.env.container.name,
+ object_name)
+ more_data = "- Batman"
+ with open(file_path, 'a') as f:
+ f.write(more_data)
+ total_data = data + more_data
+ total_data_hash = hashlib.md5(total_data).hexdigest()
+ # Make sure GET works
+ self.assertEqual(total_data, object_item.read())
+ self.assert_status(200)
+ # Check Etag and content-length is right
+ metadata = object_item.info()
+ self.assert_status(200)
+ self.assertEqual(total_data_hash, metadata['etag'])
+ self.assertEqual(len(total_data), int(metadata['content_length']))
+
+ # Re-write the file to be shorter
+ new_data = "I am Batman"
+ new_data_hash = hashlib.md5(new_data).hexdigest()
+ with open(file_path, 'w') as f:
+ f.write(new_data)
+ # Make sure GET works
+ self.assertEqual(new_data, object_item.read())
+ self.assert_status(200)
+ # Check Etag and content-length is right
+ metadata = object_item.info()
+ self.assert_status(200)
+ self.assertEqual(new_data_hash, metadata['etag'])
+ self.assertEqual(len(new_data), int(metadata['content_length']))
diff --git a/test/functional/swift_test_client.py b/test/functional/swift_test_client.py
index a7c7c96..27e025b 100644
--- a/test/functional/swift_test_client.py
+++ b/test/functional/swift_test_client.py
@@ -144,6 +144,7 @@ class Connection(object):
auth_scheme = 'https://' if self.auth_ssl else 'http://'
auth_netloc = "%s:%d" % (self.auth_host, self.auth_port)
auth_url = auth_scheme + auth_netloc + auth_path
+
(storage_url, storage_token) = get_auth(
auth_url, auth_user, self.password, snet=False,
tenant_name=self.account, auth_version=self.auth_version,
@@ -166,17 +167,29 @@ class Connection(object):
self.storage_host = x[2].split(':')[0]
if ':' in x[2]:
self.storage_port = int(x[2].split(':')[1])
- # Make sure storage_url and the storage_token are
- # strings and not unicode, since
+ # Make sure storage_url is a string and not unicode, since
# keystoneclient (called by swiftclient) returns them in
# unicode and this would cause troubles when doing
# no_safe_quote query.
self.storage_url = str('/%s/%s' % (x[3], x[4]))
- self.storage_token = str(storage_token)
+
+ self.storage_token = storage_token
self.http_connect()
return self.storage_url, self.storage_token
+ def cluster_info(self):
+ """
+ Retrieve the data in /info, or {} on 404
+ """
+ status = self.make_request('GET', '/info',
+ cfg={'absolute_path': True})
+ if status == 404:
+ return {}
+ if not 200 <= status <= 299:
+ raise ResponseError(self.response, 'GET', '/info')
+ return json.loads(self.response.read())
+
def http_connect(self):
self.connection = self.conn_class(self.storage_host,
port=self.storage_port)
@@ -207,8 +220,8 @@ class Connection(object):
def make_request(self, method, path=[], data='', hdrs={}, parms={},
cfg={}):
- if not cfg.get('verbatim_path'):
- # Set verbatim_path=True to make a request to exactly the given
+ if not cfg.get('absolute_path'):
+ # Set absolute_path=True to make a request to exactly the given
# path, not storage path + given path. Useful for
# non-account/container/object requests.
path = self.make_path(path, cfg=cfg)
@@ -305,7 +318,7 @@ class Connection(object):
return self.response.status
-class Base:
+class Base(object):
def __str__(self):
return self.name
@@ -339,6 +352,16 @@ class Account(Base):
self.conn = conn
self.name = str(name)
+ def update_metadata(self, metadata={}, cfg={}):
+ headers = dict(("X-Account-Meta-%s" % k, v)
+ for k, v in metadata.items())
+
+ self.conn.make_request('POST', self.path, hdrs=headers, cfg=cfg)
+ if not 200 <= self.conn.response.status <= 299:
+ raise ResponseError(self.conn.response, 'POST',
+ self.conn.make_path(self.path))
+ return True
+
def container(self, container_name):
return Container(self.conn, self.name, container_name)
@@ -532,6 +555,11 @@ class File(Base):
else:
headers['Content-Type'] = 'application/octet-stream'
+ if cfg.get('x_delete_at'):
+ headers['X-Delete-At'] = cfg.get('x_delete_at')
+ if cfg.get('x_delete_after'):
+ headers['X-Delete-After'] = cfg.get('x_delete_after')
+
for key in self.metadata:
headers['X-Object-Meta-' + key] = self.metadata[key]
@@ -588,7 +616,11 @@ class File(Base):
['last_modified', 'last-modified'],
['etag', 'etag']]
- header_fields = self.header_fields(fields)
+ optional_fields = [['x_delete_at', 'x-delete-at'],
+ ['x_delete_after', 'x-delete-after']]
+
+ header_fields = self.header_fields(fields,
+ optional_fields=optional_fields)
header_fields['etag'] = header_fields['etag'].strip('"')
return header_fields
@@ -705,7 +737,6 @@ class File(Base):
cfg.get('set_content_length')
else:
headers['Content-Length'] = 0
-
self.conn.make_request('POST', self.path, hdrs=headers, cfg=cfg)
if self.conn.response.status not in (201, 202):
diff --git a/test/functional/swift_testing.py b/test/functional/swift_testing.py
index f05cb48..2a1e1fa 100644
--- a/test/functional/swift_testing.py
+++ b/test/functional/swift_testing.py
@@ -19,10 +19,13 @@ import socket
import sys
from time import sleep
from urlparse import urlparse
+import functools
+from nose import SkipTest
from test import get_config
from swiftclient import get_auth, http_connection
+from test.functional.swift_test_client import Connection
conf = get_config('func_test')
web_front_end = conf.get('web_front_end', 'integral')
@@ -184,3 +187,45 @@ def check_response(conn):
resp.read()
raise InternalServerError()
return resp
+
+cluster_info = {}
+
+
+def get_cluster_info():
+ conn = Connection(conf)
+ conn.authenticate()
+ global cluster_info
+ cluster_info = conn.cluster_info()
+
+
+def reset_acl():
+ def post(url, token, parsed, conn):
+ conn.request('POST', parsed.path, '', {
+ 'X-Auth-Token': token,
+ 'X-Account-Access-Control': '{}'
+ })
+ return check_response(conn)
+ resp = retry(post, use_account=1)
+ resp.read()
+
+
+def requires_acls(f):
+ @functools.wraps(f)
+ def wrapper(*args, **kwargs):
+ if skip:
+ raise SkipTest
+ if not cluster_info:
+ get_cluster_info()
+ # Determine whether this cluster has account ACLs; if not, skip test
+ if not cluster_info.get('tempauth', {}).get('account_acls'):
+ raise SkipTest
+ if 'keystoneauth' in cluster_info:
+ # remove when keystoneauth supports account acls
+ raise SkipTest
+ reset_acl()
+ try:
+ rv = f(*args, **kwargs)
+ finally:
+ reset_acl()
+ return rv
+ return wrapper
diff --git a/test/functional/test_account.py b/test/functional/test_account.py
index d456090..1cc61bc 100755
--- a/test/functional/test_account.py
+++ b/test/functional/test_account.py
@@ -17,19 +17,57 @@
import unittest
import json
+from uuid import uuid4
from nose import SkipTest
+from string import letters
from swift.common.constraints import MAX_META_COUNT, MAX_META_NAME_LENGTH, \
MAX_META_OVERALL_SIZE, MAX_META_VALUE_LENGTH
from swift.common.middleware.acl import format_acl
-from test.functional.swift_test_client import Connection
-from test import get_config
-from swift_testing import check_response, retry, skip, web_front_end
+from swift_testing import (check_response, retry, skip, skip2, skip3,
+ web_front_end, requires_acls)
import swift_testing
+from test.functional.tests import load_constraint
class TestAccount(unittest.TestCase):
+ def setUp(self):
+ self.max_meta_count = load_constraint('max_meta_count')
+ self.max_meta_name_length = load_constraint('max_meta_name_length')
+ self.max_meta_overall_size = load_constraint('max_meta_overall_size')
+ self.max_meta_value_length = load_constraint('max_meta_value_length')
+
+ def head(url, token, parsed, conn):
+ conn.request('HEAD', parsed.path, '', {'X-Auth-Token': token})
+ return check_response(conn)
+ resp = retry(head)
+ self.existing_metadata = set([
+ k for k, v in resp.getheaders() if
+ k.lower().startswith('x-account-meta')])
+
+ def tearDown(self):
+ def head(url, token, parsed, conn):
+ conn.request('HEAD', parsed.path, '', {'X-Auth-Token': token})
+ return check_response(conn)
+ resp = retry(head)
+ resp.read()
+ new_metadata = set(
+ [k for k, v in resp.getheaders() if
+ k.lower().startswith('x-account-meta')])
+
+ def clear_meta(url, token, parsed, conn, remove_metadata_keys):
+ headers = {'X-Auth-Token': token}
+ headers.update((k, '') for k in remove_metadata_keys)
+ conn.request('POST', parsed.path, '', headers)
+ return check_response(conn)
+ extra_metadata = list(self.existing_metadata ^ new_metadata)
+ for i in range(0, len(extra_metadata), 90):
+ batch = extra_metadata[i:i + 90]
+ resp = retry(clear_meta, batch)
+ resp.read()
+ self.assertEqual(resp.status // 100, 2)
+
def test_metadata(self):
if skip:
raise SkipTest
@@ -49,49 +87,338 @@ class TestAccount(unittest.TestCase):
resp = retry(post, '')
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(head)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('x-account-meta-test'), None)
+ self.assertEqual(resp.getheader('x-account-meta-test'), None)
resp = retry(get)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('x-account-meta-test'), None)
+ self.assertEqual(resp.getheader('x-account-meta-test'), None)
resp = retry(post, 'Value')
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(head)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('x-account-meta-test'), 'Value')
+ self.assertEqual(resp.getheader('x-account-meta-test'), 'Value')
resp = retry(get)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('x-account-meta-test'), 'Value')
+ self.assertEqual(resp.getheader('x-account-meta-test'), 'Value')
- def test_tempauth_account_acls(self):
- if skip:
+ def test_invalid_acls(self):
+ def post(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ # needs to be an acceptable header size
+ num_keys = 8
+ max_key_size = load_constraint('max_header_size') / num_keys
+ acl = {'admin': [c * max_key_size for c in letters[:num_keys]]}
+ headers = {'x-account-access-control': format_acl(
+ version=2, acl_dict=acl)}
+ resp = retry(post, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 400)
+
+ # and again a touch smaller
+ acl = {'admin': [c * max_key_size for c in letters[:num_keys - 1]]}
+ headers = {'x-account-access-control': format_acl(
+ version=2, acl_dict=acl)}
+ resp = retry(post, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ @requires_acls
+ def test_invalid_acl_keys(self):
+ def post(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ # needs to be json
+ resp = retry(post, headers={'X-Account-Access-Control': 'invalid'},
+ use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 400)
+
+ acl_user = swift_testing.swift_test_user[1]
+ acl = {'admin': [acl_user], 'invalid_key': 'invalid_value'}
+ headers = {'x-account-access-control': format_acl(
+ version=2, acl_dict=acl)}
+
+ resp = retry(post, headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 400)
+ self.assertEqual(resp.getheader('X-Account-Access-Control'), None)
+
+ @requires_acls
+ def test_invalid_acl_values(self):
+ def post(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ acl = {'admin': 'invalid_value'}
+ headers = {'x-account-access-control': format_acl(
+ version=2, acl_dict=acl)}
+
+ resp = retry(post, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 400)
+ self.assertEqual(resp.getheader('X-Account-Access-Control'), None)
+
+ @requires_acls
+ def test_read_only_acl(self):
+ if skip3:
raise SkipTest
- # Determine whether this cluster has account ACLs; if not, skip test
- conn = Connection(get_config('func_test'))
- conn.authenticate()
- status = conn.make_request(
- 'GET', '/info', cfg={'verbatim_path': True})
- if status // 100 != 2:
- # Can't tell if account ACLs are enabled; skip tests proactively.
+ def get(url, token, parsed, conn):
+ conn.request('GET', parsed.path, '', {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def post(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ # cannot read account
+ resp = retry(get, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # grant read access
+ acl_user = swift_testing.swift_test_user[2]
+ acl = {'read-only': [acl_user]}
+ headers = {'x-account-access-control': format_acl(
+ version=2, acl_dict=acl)}
+ resp = retry(post, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # read-only can read account headers
+ resp = retry(get, use_account=3)
+ resp.read()
+ self.assert_(resp.status in (200, 204))
+ # but not acls
+ self.assertEqual(resp.getheader('X-Account-Access-Control'), None)
+
+ # read-only can not write metadata
+ headers = {'x-account-meta-test': 'value'}
+ resp = retry(post, headers=headers, use_account=3)
+ resp.read()
+ self.assertEqual(resp.status, 403)
+
+ # but they can read it
+ headers = {'x-account-meta-test': 'value'}
+ resp = retry(post, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+ resp = retry(get, use_account=3)
+ resp.read()
+ self.assert_(resp.status in (200, 204))
+ self.assertEqual(resp.getheader('X-Account-Meta-Test'), 'value')
+
+ @requires_acls
+ def test_read_write_acl(self):
+ if skip3:
+ raise SkipTest
+
+ def get(url, token, parsed, conn):
+ conn.request('GET', parsed.path, '', {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def post(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ # cannot read account
+ resp = retry(get, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # grant read-write access
+ acl_user = swift_testing.swift_test_user[2]
+ acl = {'read-write': [acl_user]}
+ headers = {'x-account-access-control': format_acl(
+ version=2, acl_dict=acl)}
+ resp = retry(post, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # read-write can read account headers
+ resp = retry(get, use_account=3)
+ resp.read()
+ self.assert_(resp.status in (200, 204))
+ # but not acls
+ self.assertEqual(resp.getheader('X-Account-Access-Control'), None)
+
+ # read-write can not write account metadata
+ headers = {'x-account-meta-test': 'value'}
+ resp = retry(post, headers=headers, use_account=3)
+ resp.read()
+ self.assertEqual(resp.status, 403)
+
+ @requires_acls
+ def test_admin_acl(self):
+ if skip3:
+ raise SkipTest
+
+ def get(url, token, parsed, conn):
+ conn.request('GET', parsed.path, '', {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def post(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ # cannot read account
+ resp = retry(get, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # grant admin access
+ acl_user = swift_testing.swift_test_user[2]
+ acl = {'admin': [acl_user]}
+ acl_json_str = format_acl(version=2, acl_dict=acl)
+ headers = {'x-account-access-control': acl_json_str}
+ resp = retry(post, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # admin can read account headers
+ resp = retry(get, use_account=3)
+ resp.read()
+ self.assert_(resp.status in (200, 204))
+ # including acls
+ self.assertEqual(resp.getheader('X-Account-Access-Control'),
+ acl_json_str)
+
+ # admin can write account metadata
+ value = str(uuid4())
+ headers = {'x-account-meta-test': value}
+ resp = retry(post, headers=headers, use_account=3)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+ resp = retry(get, use_account=3)
+ resp.read()
+ self.assert_(resp.status in (200, 204))
+ self.assertEqual(resp.getheader('X-Account-Meta-Test'), value)
+
+ # admin can even revoke their own access
+ headers = {'x-account-access-control': '{}'}
+ resp = retry(post, headers=headers, use_account=3)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # and again, cannot read account
+ resp = retry(get, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ @requires_acls
+ def test_protected_tempurl(self):
+ if skip3:
+ raise SkipTest
+
+ def get(url, token, parsed, conn):
+ conn.request('GET', parsed.path, '', {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def post(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ # add a account metadata, and temp-url-key to account
+ value = str(uuid4())
+ headers = {
+ 'x-account-meta-temp-url-key': 'secret',
+ 'x-account-meta-test': value,
+ }
+ resp = retry(post, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # grant read-only access to tester3
+ acl_user = swift_testing.swift_test_user[2]
+ acl = {'read-only': [acl_user]}
+ acl_json_str = format_acl(version=2, acl_dict=acl)
+ headers = {'x-account-access-control': acl_json_str}
+ resp = retry(post, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # read-only tester3 can read account metadata
+ resp = retry(get, use_account=3)
+ resp.read()
+ self.assert_(resp.status in (200, 204),
+ 'Expected status in (200, 204), got %s' % resp.status)
+ self.assertEqual(resp.getheader('X-Account-Meta-Test'), value)
+ # but not temp-url-key
+ self.assertEqual(resp.getheader('X-Account-Meta-Temp-Url-Key'), None)
+
+ # grant read-write access to tester3
+ acl_user = swift_testing.swift_test_user[2]
+ acl = {'read-write': [acl_user]}
+ acl_json_str = format_acl(version=2, acl_dict=acl)
+ headers = {'x-account-access-control': acl_json_str}
+ resp = retry(post, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # read-write tester3 can read account metadata
+ resp = retry(get, use_account=3)
+ resp.read()
+ self.assert_(resp.status in (200, 204),
+ 'Expected status in (200, 204), got %s' % resp.status)
+ self.assertEqual(resp.getheader('X-Account-Meta-Test'), value)
+ # but not temp-url-key
+ self.assertEqual(resp.getheader('X-Account-Meta-Temp-Url-Key'), None)
+
+ # grant admin access to tester3
+ acl_user = swift_testing.swift_test_user[2]
+ acl = {'admin': [acl_user]}
+ acl_json_str = format_acl(version=2, acl_dict=acl)
+ headers = {'x-account-access-control': acl_json_str}
+ resp = retry(post, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # admin tester3 can read account metadata
+ resp = retry(get, use_account=3)
+ resp.read()
+ self.assert_(resp.status in (200, 204),
+ 'Expected status in (200, 204), got %s' % resp.status)
+ self.assertEqual(resp.getheader('X-Account-Meta-Test'), value)
+ # including temp-url-key
+ self.assertEqual(resp.getheader('X-Account-Meta-Temp-Url-Key'),
+ 'secret')
+
+ # admin tester3 can even change temp-url-key
+ secret = str(uuid4())
+ headers = {
+ 'x-account-meta-temp-url-key': secret,
+ }
+ resp = retry(post, headers=headers, use_account=3)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+ resp = retry(get, use_account=3)
+ resp.read()
+ self.assert_(resp.status in (200, 204),
+ 'Expected status in (200, 204), got %s' % resp.status)
+ self.assertEqual(resp.getheader('X-Account-Meta-Temp-Url-Key'),
+ secret)
+
+ @requires_acls
+ def test_account_acls(self):
+ if skip2:
raise SkipTest
- else:
- cluster_info = json.loads(conn.response.read())
- if not cluster_info.get('tempauth', {}).get('account_acls'):
- raise SkipTest
- if 'keystoneauth' in cluster_info:
- # Unfortunate hack -- tempauth (with account ACLs) is expected
- # to play nice with Keystone (without account ACLs), but Zuul
- # functest framework doesn't give us an easy way to get a
- # tempauth user.
- raise SkipTest
def post(url, token, parsed, conn, headers):
new_headers = dict({'X-Auth-Token': token}, **headers)
@@ -212,6 +539,137 @@ class TestAccount(unittest.TestCase):
use_account=1)
resp.read()
+ @requires_acls
+ def test_swift_account_acls(self):
+ if skip:
+ raise SkipTest
+
+ def post(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ def head(url, token, parsed, conn):
+ conn.request('HEAD', parsed.path, '', {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def get(url, token, parsed, conn):
+ conn.request('GET', parsed.path, '', {'X-Auth-Token': token})
+ return check_response(conn)
+
+ try:
+ # User1 can POST to their own account
+ resp = retry(post, headers={'X-Account-Access-Control': '{}'})
+ resp.read()
+ self.assertEqual(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Account-Access-Control'), None)
+
+ # User1 can GET their own empty account
+ resp = retry(get)
+ resp.read()
+ self.assertEqual(resp.status // 100, 2)
+ self.assertEqual(resp.getheader('X-Account-Access-Control'), None)
+
+ # User1 can POST non-empty data
+ acl_json = '{"admin":["bob"]}'
+ resp = retry(post, headers={'X-Account-Access-Control': acl_json})
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # User1 can GET the non-empty data
+ resp = retry(get)
+ resp.read()
+ self.assertEqual(resp.status // 100, 2)
+ self.assertEqual(resp.getheader('X-Account-Access-Control'),
+ acl_json)
+
+ # POST non-JSON ACL should fail
+ resp = retry(post, headers={'X-Account-Access-Control': 'yuck'})
+ resp.read()
+ # resp.status will be 400 if tempauth or some other ACL-aware
+ # auth middleware rejects it, or 200 (but silently swallowed by
+ # core Swift) if ACL-unaware auth middleware approves it.
+
+ # A subsequent GET should show the old, valid data, not the garbage
+ resp = retry(get)
+ resp.read()
+ self.assertEqual(resp.status // 100, 2)
+ self.assertEqual(resp.getheader('X-Account-Access-Control'),
+ acl_json)
+
+ finally:
+ # Make sure to clean up even if tests fail -- User2 should not
+ # have access to User1's account in other functional tests!
+ resp = retry(post, headers={'X-Account-Access-Control': '{}'})
+ resp.read()
+
+ def test_swift_prohibits_garbage_account_acls(self):
+ if skip:
+ raise SkipTest
+
+ def post(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ def get(url, token, parsed, conn):
+ conn.request('GET', parsed.path, '', {'X-Auth-Token': token})
+ return check_response(conn)
+
+ try:
+ # User1 can POST to their own account
+ resp = retry(post, headers={'X-Account-Access-Control': '{}'})
+ resp.read()
+ self.assertEqual(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Account-Access-Control'), None)
+
+ # User1 can GET their own empty account
+ resp = retry(get)
+ resp.read()
+ self.assertEqual(resp.status // 100, 2)
+ self.assertEqual(resp.getheader('X-Account-Access-Control'), None)
+
+ # User1 can POST non-empty data
+ acl_json = '{"admin":["bob"]}'
+ resp = retry(post, headers={'X-Account-Access-Control': acl_json})
+ resp.read()
+ self.assertEqual(resp.status, 204)
+ # If this request is handled by ACL-aware auth middleware, then the
+ # ACL will be persisted. If it is handled by ACL-unaware auth
+ # middleware, then the header will be thrown out. But the request
+ # should return successfully in any case.
+
+ # User1 can GET the non-empty data
+ resp = retry(get)
+ resp.read()
+ self.assertEqual(resp.status // 100, 2)
+ # ACL will be set if some ACL-aware auth middleware (e.g. tempauth)
+ # propagates it to sysmeta; if no ACL-aware auth middleware does,
+ # then X-Account-Access-Control will still be empty.
+
+ # POST non-JSON ACL should fail
+ resp = retry(post, headers={'X-Account-Access-Control': 'yuck'})
+ resp.read()
+ # resp.status will be 400 if tempauth or some other ACL-aware
+ # auth middleware rejects it, or 200 (but silently swallowed by
+ # core Swift) if ACL-unaware auth middleware approves it.
+
+ # A subsequent GET should either show the old, valid data (if
+ # ACL-aware auth middleware is propagating it) or show nothing
+ # (if no auth middleware in the pipeline is ACL-aware), but should
+ # never return the garbage ACL.
+ resp = retry(get)
+ resp.read()
+ self.assertEqual(resp.status // 100, 2)
+ self.assertNotEqual(resp.getheader('X-Account-Access-Control'),
+ 'yuck')
+
+ finally:
+ # Make sure to clean up even if tests fail -- User2 should not
+ # have access to User1's account in other functional tests!
+ resp = retry(post, headers={'X-Account-Access-Control': '{}'})
+ resp.read()
+
def test_unicode_metadata(self):
if skip:
raise SkipTest
@@ -233,24 +691,24 @@ class TestAccount(unittest.TestCase):
resp = retry(head)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader(uni_key.encode('utf-8')), '1')
+ self.assertEqual(resp.getheader(uni_key.encode('utf-8')), '1')
resp = retry(post, 'X-Account-Meta-uni', uni_value)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(head)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('X-Account-Meta-uni'),
- uni_value.encode('utf-8'))
+ self.assertEqual(resp.getheader('X-Account-Meta-uni'),
+ uni_value.encode('utf-8'))
if (web_front_end == 'integral'):
resp = retry(post, uni_key, uni_value)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(head)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader(uni_key.encode('utf-8')),
- uni_value.encode('utf-8'))
+ self.assertEqual(resp.getheader(uni_key.encode('utf-8')),
+ uni_value.encode('utf-8'))
def test_multi_metadata(self):
if skip:
@@ -267,19 +725,19 @@ class TestAccount(unittest.TestCase):
resp = retry(post, 'X-Account-Meta-One', '1')
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(head)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('x-account-meta-one'), '1')
+ self.assertEqual(resp.getheader('x-account-meta-one'), '1')
resp = retry(post, 'X-Account-Meta-Two', '2')
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(head)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('x-account-meta-one'), '1')
- self.assertEquals(resp.getheader('x-account-meta-two'), '2')
+ self.assertEqual(resp.getheader('x-account-meta-one'), '1')
+ self.assertEqual(resp.getheader('x-account-meta-two'), '2')
def test_bad_metadata(self):
if skip:
@@ -294,35 +752,65 @@ class TestAccount(unittest.TestCase):
resp = retry(post,
{'X-Account-Meta-' + ('k' * MAX_META_NAME_LENGTH): 'v'})
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(
post,
{'X-Account-Meta-' + ('k' * (MAX_META_NAME_LENGTH + 1)): 'v'})
resp.read()
- self.assertEquals(resp.status, 400)
+ self.assertEqual(resp.status, 400)
resp = retry(post,
{'X-Account-Meta-Too-Long': 'k' * MAX_META_VALUE_LENGTH})
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(
post,
{'X-Account-Meta-Too-Long': 'k' * (MAX_META_VALUE_LENGTH + 1)})
resp.read()
- self.assertEquals(resp.status, 400)
+ self.assertEqual(resp.status, 400)
+
+ def test_bad_metadata2(self):
+ if skip:
+ raise SkipTest
+
+ def post(url, token, parsed, conn, extra_headers):
+ headers = {'X-Auth-Token': token}
+ headers.update(extra_headers)
+ conn.request('POST', parsed.path, '', headers)
+ return check_response(conn)
+
+ # TODO: Find the test that adds these and remove them.
+ headers = {'x-remove-account-meta-temp-url-key': 'remove',
+ 'x-remove-account-meta-temp-url-key-2': 'remove'}
+ resp = retry(post, headers)
headers = {}
for x in xrange(MAX_META_COUNT):
headers['X-Account-Meta-%d' % x] = 'v'
resp = retry(post, headers)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
headers = {}
for x in xrange(MAX_META_COUNT + 1):
headers['X-Account-Meta-%d' % x] = 'v'
resp = retry(post, headers)
resp.read()
- self.assertEquals(resp.status, 400)
+ self.assertEqual(resp.status, 400)
+
+ def test_bad_metadata3(self):
+ if skip:
+ raise SkipTest
+
+ def post(url, token, parsed, conn, extra_headers):
+ headers = {'X-Auth-Token': token}
+ headers.update(extra_headers)
+ conn.request('POST', parsed.path, '', headers)
+ return check_response(conn)
+
+ # TODO: Find the test that adds these and remove them.
+ headers = {'x-remove-account-meta-temp-url-key': 'remove',
+ 'x-remove-account-meta-temp-url-key-2': 'remove'}
+ resp = retry(post, headers)
headers = {}
header_value = 'k' * MAX_META_VALUE_LENGTH
@@ -337,12 +825,12 @@ class TestAccount(unittest.TestCase):
'v' * (MAX_META_OVERALL_SIZE - size - 1)
resp = retry(post, headers)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
headers['X-Account-Meta-k'] = \
'v' * (MAX_META_OVERALL_SIZE - size)
resp = retry(post, headers)
resp.read()
- self.assertEquals(resp.status, 400)
+ self.assertEqual(resp.status, 400)
if __name__ == '__main__':
diff --git a/test/functional/test_container.py b/test/functional/test_container.py
index 15f7fc1..91702e9 100755
--- a/test/functional/test_container.py
+++ b/test/functional/test_container.py
@@ -24,7 +24,7 @@ from swift.common.constraints import MAX_META_COUNT, MAX_META_NAME_LENGTH, \
MAX_META_OVERALL_SIZE, MAX_META_VALUE_LENGTH
from swift_testing import check_response, retry, skip, skip2, skip3, \
- swift_test_perm, web_front_end
+ swift_test_perm, web_front_end, requires_acls, swift_test_user
class TestContainer(unittest.TestCase):
@@ -41,7 +41,7 @@ class TestContainer(unittest.TestCase):
resp = retry(put)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
def tearDown(self):
if skip:
@@ -68,7 +68,7 @@ class TestContainer(unittest.TestCase):
for obj in objs:
resp = retry(delete, obj)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
def delete(url, token, parsed, conn):
conn.request('DELETE', parsed.path + '/' + self.name, '',
@@ -77,7 +77,7 @@ class TestContainer(unittest.TestCase):
resp = retry(delete)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
def test_multi_metadata(self):
if skip:
@@ -95,19 +95,19 @@ class TestContainer(unittest.TestCase):
resp = retry(post, 'X-Container-Meta-One', '1')
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(head)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('x-container-meta-one'), '1')
+ self.assertEqual(resp.getheader('x-container-meta-one'), '1')
resp = retry(post, 'X-Container-Meta-Two', '2')
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(head)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('x-container-meta-one'), '1')
- self.assertEquals(resp.getheader('x-container-meta-two'), '2')
+ self.assertEqual(resp.getheader('x-container-meta-one'), '1')
+ self.assertEqual(resp.getheader('x-container-meta-two'), '2')
def test_unicode_metadata(self):
if skip:
@@ -128,28 +128,28 @@ class TestContainer(unittest.TestCase):
if (web_front_end == 'integral'):
resp = retry(post, uni_key, '1')
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(head)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader(uni_key.encode('utf-8')), '1')
+ self.assertEqual(resp.getheader(uni_key.encode('utf-8')), '1')
resp = retry(post, 'X-Container-Meta-uni', uni_value)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(head)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('X-Container-Meta-uni'),
- uni_value.encode('utf-8'))
+ self.assertEqual(resp.getheader('X-Container-Meta-uni'),
+ uni_value.encode('utf-8'))
if (web_front_end == 'integral'):
resp = retry(post, uni_key, uni_value)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(head)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader(uni_key.encode('utf-8')),
- uni_value.encode('utf-8'))
+ self.assertEqual(resp.getheader(uni_key.encode('utf-8')),
+ uni_value.encode('utf-8'))
def test_PUT_metadata(self):
if skip:
@@ -179,34 +179,34 @@ class TestContainer(unittest.TestCase):
name = uuid4().hex
resp = retry(put, name, 'Value')
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
resp = retry(head, name)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('x-container-meta-test'), 'Value')
+ self.assertEqual(resp.getheader('x-container-meta-test'), 'Value')
resp = retry(get, name)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('x-container-meta-test'), 'Value')
+ self.assertEqual(resp.getheader('x-container-meta-test'), 'Value')
resp = retry(delete, name)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
name = uuid4().hex
resp = retry(put, name, '')
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
resp = retry(head, name)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('x-container-meta-test'), None)
+ self.assertEqual(resp.getheader('x-container-meta-test'), None)
resp = retry(get, name)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('x-container-meta-test'), None)
+ self.assertEqual(resp.getheader('x-container-meta-test'), None)
resp = retry(delete, name)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
def test_POST_metadata(self):
if skip:
@@ -231,22 +231,22 @@ class TestContainer(unittest.TestCase):
resp = retry(head)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('x-container-meta-test'), None)
+ self.assertEqual(resp.getheader('x-container-meta-test'), None)
resp = retry(get)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('x-container-meta-test'), None)
+ self.assertEqual(resp.getheader('x-container-meta-test'), None)
resp = retry(post, 'Value')
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(head)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('x-container-meta-test'), 'Value')
+ self.assertEqual(resp.getheader('x-container-meta-test'), 'Value')
resp = retry(get)
resp.read()
self.assert_(resp.status in (200, 204), resp.status)
- self.assertEquals(resp.getheader('x-container-meta-test'), 'Value')
+ self.assertEqual(resp.getheader('x-container-meta-test'), 'Value')
def test_PUT_bad_metadata(self):
if skip:
@@ -268,38 +268,38 @@ class TestContainer(unittest.TestCase):
put, name,
{'X-Container-Meta-' + ('k' * MAX_META_NAME_LENGTH): 'v'})
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
resp = retry(delete, name)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
name = uuid4().hex
resp = retry(
put, name,
{'X-Container-Meta-' + ('k' * (MAX_META_NAME_LENGTH + 1)): 'v'})
resp.read()
- self.assertEquals(resp.status, 400)
+ self.assertEqual(resp.status, 400)
resp = retry(delete, name)
resp.read()
- self.assertEquals(resp.status, 404)
+ self.assertEqual(resp.status, 404)
name = uuid4().hex
resp = retry(
put, name,
{'X-Container-Meta-Too-Long': 'k' * MAX_META_VALUE_LENGTH})
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
resp = retry(delete, name)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
name = uuid4().hex
resp = retry(
put, name,
{'X-Container-Meta-Too-Long': 'k' * (MAX_META_VALUE_LENGTH + 1)})
resp.read()
- self.assertEquals(resp.status, 400)
+ self.assertEqual(resp.status, 400)
resp = retry(delete, name)
resp.read()
- self.assertEquals(resp.status, 404)
+ self.assertEqual(resp.status, 404)
name = uuid4().hex
headers = {}
@@ -307,20 +307,20 @@ class TestContainer(unittest.TestCase):
headers['X-Container-Meta-%d' % x] = 'v'
resp = retry(put, name, headers)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
resp = retry(delete, name)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
name = uuid4().hex
headers = {}
for x in xrange(MAX_META_COUNT + 1):
headers['X-Container-Meta-%d' % x] = 'v'
resp = retry(put, name, headers)
resp.read()
- self.assertEquals(resp.status, 400)
+ self.assertEqual(resp.status, 400)
resp = retry(delete, name)
resp.read()
- self.assertEquals(resp.status, 404)
+ self.assertEqual(resp.status, 404)
name = uuid4().hex
headers = {}
@@ -336,19 +336,19 @@ class TestContainer(unittest.TestCase):
'v' * (MAX_META_OVERALL_SIZE - size - 1)
resp = retry(put, name, headers)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
resp = retry(delete, name)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
name = uuid4().hex
headers['X-Container-Meta-k'] = \
'v' * (MAX_META_OVERALL_SIZE - size)
resp = retry(put, name, headers)
resp.read()
- self.assertEquals(resp.status, 400)
+ self.assertEqual(resp.status, 400)
resp = retry(delete, name)
resp.read()
- self.assertEquals(resp.status, 404)
+ self.assertEqual(resp.status, 404)
def test_POST_bad_metadata(self):
if skip:
@@ -364,36 +364,56 @@ class TestContainer(unittest.TestCase):
post,
{'X-Container-Meta-' + ('k' * MAX_META_NAME_LENGTH): 'v'})
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(
post,
{'X-Container-Meta-' + ('k' * (MAX_META_NAME_LENGTH + 1)): 'v'})
resp.read()
- self.assertEquals(resp.status, 400)
+ self.assertEqual(resp.status, 400)
resp = retry(
post,
{'X-Container-Meta-Too-Long': 'k' * MAX_META_VALUE_LENGTH})
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(
post,
{'X-Container-Meta-Too-Long': 'k' * (MAX_META_VALUE_LENGTH + 1)})
resp.read()
- self.assertEquals(resp.status, 400)
+ self.assertEqual(resp.status, 400)
+
+ def test_POST_bad_metadata2(self):
+ if skip:
+ raise SkipTest
+
+ def post(url, token, parsed, conn, extra_headers):
+ headers = {'X-Auth-Token': token}
+ headers.update(extra_headers)
+ conn.request('POST', parsed.path + '/' + self.name, '', headers)
+ return check_response(conn)
headers = {}
for x in xrange(MAX_META_COUNT):
headers['X-Container-Meta-%d' % x] = 'v'
resp = retry(post, headers)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
headers = {}
for x in xrange(MAX_META_COUNT + 1):
headers['X-Container-Meta-%d' % x] = 'v'
resp = retry(post, headers)
resp.read()
- self.assertEquals(resp.status, 400)
+ self.assertEqual(resp.status, 400)
+
+ def test_POST_bad_metadata3(self):
+ if skip:
+ raise SkipTest
+
+ def post(url, token, parsed, conn, extra_headers):
+ headers = {'X-Auth-Token': token}
+ headers.update(extra_headers)
+ conn.request('POST', parsed.path + '/' + self.name, '', headers)
+ return check_response(conn)
headers = {}
header_value = 'k' * MAX_META_VALUE_LENGTH
@@ -408,12 +428,12 @@ class TestContainer(unittest.TestCase):
'v' * (MAX_META_OVERALL_SIZE - size - 1)
resp = retry(post, headers)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
headers['X-Container-Meta-k'] = \
'v' * (MAX_META_OVERALL_SIZE - size)
resp = retry(post, headers)
resp.read()
- self.assertEquals(resp.status, 400)
+ self.assertEqual(resp.status, 400)
def test_public_container(self):
if skip:
@@ -437,10 +457,10 @@ class TestContainer(unittest.TestCase):
resp = retry(post)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(get)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
def post(url, token, parsed, conn):
conn.request('POST', parsed.path + '/' + self.name, '',
@@ -449,7 +469,7 @@ class TestContainer(unittest.TestCase):
resp = retry(post)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
try:
resp = retry(get)
raise Exception('Should not have been able to GET')
@@ -479,7 +499,7 @@ class TestContainer(unittest.TestCase):
resp = retry(get2, use_account=2)
resp.read()
- self.assertEquals(resp.status, 403)
+ self.assertEqual(resp.status, 403)
# Make the container accessible by the second account
def post(url, token, parsed, conn):
@@ -491,11 +511,11 @@ class TestContainer(unittest.TestCase):
resp = retry(post)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# Ensure we can now use the container with the second account
resp = retry(get2, use_account=2)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# Make the container private again
def post(url, token, parsed, conn):
@@ -506,11 +526,11 @@ class TestContainer(unittest.TestCase):
resp = retry(post)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# Ensure we can't access the container with the second account again
resp = retry(get2, use_account=2)
resp.read()
- self.assertEquals(resp.status, 403)
+ self.assertEqual(resp.status, 403)
def test_cross_account_public_container(self):
if skip or skip2:
@@ -535,7 +555,7 @@ class TestContainer(unittest.TestCase):
resp = retry(get2, use_account=2)
resp.read()
- self.assertEquals(resp.status, 403)
+ self.assertEqual(resp.status, 403)
# Make the container completely public
def post(url, token, parsed, conn):
@@ -546,11 +566,11 @@ class TestContainer(unittest.TestCase):
resp = retry(post)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# Ensure we can now read the container with the second account
resp = retry(get2, use_account=2)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# But we shouldn't be able to write with the second account
def put2(url, token, parsed, conn):
@@ -560,7 +580,7 @@ class TestContainer(unittest.TestCase):
resp = retry(put2, use_account=2)
resp.read()
- self.assertEquals(resp.status, 403)
+ self.assertEqual(resp.status, 403)
# Now make the container also writeable by the second account
def post(url, token, parsed, conn):
@@ -571,15 +591,15 @@ class TestContainer(unittest.TestCase):
resp = retry(post)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# Ensure we can still read the container with the second account
resp = retry(get2, use_account=2)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# And that we can now write with the second account
resp = retry(put2, use_account=2)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
def test_nonadmin_user(self):
if skip or skip3:
@@ -604,7 +624,7 @@ class TestContainer(unittest.TestCase):
resp = retry(get3, use_account=3)
resp.read()
- self.assertEquals(resp.status, 403)
+ self.assertEqual(resp.status, 403)
# Make the container accessible by the third account
def post(url, token, parsed, conn):
@@ -615,11 +635,11 @@ class TestContainer(unittest.TestCase):
resp = retry(post)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# Ensure we can now read the container with the third account
resp = retry(get3, use_account=3)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# But we shouldn't be able to write with the third account
def put3(url, token, parsed, conn):
@@ -629,7 +649,7 @@ class TestContainer(unittest.TestCase):
resp = retry(put3, use_account=3)
resp.read()
- self.assertEquals(resp.status, 403)
+ self.assertEqual(resp.status, 403)
# Now make the container also writeable by the third account
def post(url, token, parsed, conn):
@@ -640,15 +660,666 @@ class TestContainer(unittest.TestCase):
resp = retry(post)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# Ensure we can still read the container with the third account
resp = retry(get3, use_account=3)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# And that we can now write with the third account
resp = retry(put3, use_account=3)
resp.read()
+ self.assertEqual(resp.status, 201)
+
+ @requires_acls
+ def test_read_only_acl_listings(self):
+ if skip3:
+ raise SkipTest
+
+ def get(url, token, parsed, conn):
+ conn.request('GET', parsed.path, '', {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def post_account(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ def put(url, token, parsed, conn, name):
+ conn.request('PUT', parsed.path + '/%s' % name, '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ # cannot list containers
+ resp = retry(get, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # grant read-only access
+ acl_user = swift_test_user[2]
+ acl = {'read-only': [acl_user]}
+ headers = {'x-account-access-control': json.dumps(acl)}
+ resp = retry(post_account, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # read-only can list containers
+ resp = retry(get, use_account=3)
+ listing = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assert_(self.name in listing)
+
+ # read-only can not create containers
+ new_container_name = str(uuid4())
+ resp = retry(put, new_container_name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # but it can see newly created ones
+ resp = retry(put, new_container_name, use_account=1)
+ resp.read()
self.assertEquals(resp.status, 201)
+ resp = retry(get, use_account=3)
+ listing = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assert_(new_container_name in listing)
+
+ @requires_acls
+ def test_read_only_acl_metadata(self):
+ if skip3:
+ raise SkipTest
+
+ def get(url, token, parsed, conn, name):
+ conn.request('GET', parsed.path + '/%s' % name, '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def post_account(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ def post(url, token, parsed, conn, name, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path + '/%s' % name, '', new_headers)
+ return check_response(conn)
+
+ # add some metadata
+ value = str(uuid4())
+ headers = {'x-container-meta-test': value}
+ resp = retry(post, self.name, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+ resp = retry(get, self.name, use_account=1)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), value)
+
+ # cannot see metadata
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # grant read-only access
+ acl_user = swift_test_user[2]
+ acl = {'read-only': [acl_user]}
+ headers = {'x-account-access-control': json.dumps(acl)}
+ resp = retry(post_account, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # read-only can NOT write container metadata
+ new_value = str(uuid4())
+ headers = {'x-container-meta-test': new_value}
+ resp = retry(post, self.name, headers=headers, use_account=3)
+ resp.read()
+ self.assertEqual(resp.status, 403)
+
+ # read-only can read container metadata
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), value)
+
+ @requires_acls
+ def test_read_write_acl_listings(self):
+ if skip3:
+ raise SkipTest
+
+ def get(url, token, parsed, conn):
+ conn.request('GET', parsed.path, '', {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def post(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ def put(url, token, parsed, conn, name):
+ conn.request('PUT', parsed.path + '/%s' % name, '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def delete(url, token, parsed, conn, name):
+ conn.request('DELETE', parsed.path + '/%s' % name, '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ # cannot list containers
+ resp = retry(get, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # grant read-write access
+ acl_user = swift_test_user[2]
+ acl = {'read-write': [acl_user]}
+ headers = {'x-account-access-control': json.dumps(acl)}
+ resp = retry(post, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # can list containers
+ resp = retry(get, use_account=3)
+ listing = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assert_(self.name in listing)
+
+ # can create new containers
+ new_container_name = str(uuid4())
+ resp = retry(put, new_container_name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 201)
+ resp = retry(get, use_account=3)
+ listing = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assert_(new_container_name in listing)
+
+ # can also delete them
+ resp = retry(delete, new_container_name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ resp = retry(get, use_account=3)
+ listing = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assert_(new_container_name not in listing)
+
+ # even if they didn't create them
+ empty_container_name = str(uuid4())
+ resp = retry(put, empty_container_name, use_account=1)
+ resp.read()
+ self.assertEquals(resp.status, 201)
+ resp = retry(delete, empty_container_name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+
+ @requires_acls
+ def test_read_write_acl_metadata(self):
+ if skip3:
+ raise SkipTest
+
+ def get(url, token, parsed, conn, name):
+ conn.request('GET', parsed.path + '/%s' % name, '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def post_account(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ def post(url, token, parsed, conn, name, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path + '/%s' % name, '', new_headers)
+ return check_response(conn)
+
+ # add some metadata
+ value = str(uuid4())
+ headers = {'x-container-meta-test': value}
+ resp = retry(post, self.name, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+ resp = retry(get, self.name, use_account=1)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), value)
+
+ # cannot see metadata
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # grant read-write access
+ acl_user = swift_test_user[2]
+ acl = {'read-write': [acl_user]}
+ headers = {'x-account-access-control': json.dumps(acl)}
+ resp = retry(post_account, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # read-write can read container metadata
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), value)
+
+ # read-write can also write container metadata
+ new_value = str(uuid4())
+ headers = {'x-container-meta-test': new_value}
+ resp = retry(post, self.name, headers=headers, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), new_value)
+
+ # and remove it
+ headers = {'x-remove-container-meta-test': 'true'}
+ resp = retry(post, self.name, headers=headers, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), None)
+
+ @requires_acls
+ def test_admin_acl_listing(self):
+ if skip3:
+ raise SkipTest
+
+ def get(url, token, parsed, conn):
+ conn.request('GET', parsed.path, '', {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def post(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ def put(url, token, parsed, conn, name):
+ conn.request('PUT', parsed.path + '/%s' % name, '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def delete(url, token, parsed, conn, name):
+ conn.request('DELETE', parsed.path + '/%s' % name, '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ # cannot list containers
+ resp = retry(get, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # grant admin access
+ acl_user = swift_test_user[2]
+ acl = {'admin': [acl_user]}
+ headers = {'x-account-access-control': json.dumps(acl)}
+ resp = retry(post, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # can list containers
+ resp = retry(get, use_account=3)
+ listing = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assert_(self.name in listing)
+
+ # can create new containers
+ new_container_name = str(uuid4())
+ resp = retry(put, new_container_name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 201)
+ resp = retry(get, use_account=3)
+ listing = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assert_(new_container_name in listing)
+
+ # can also delete them
+ resp = retry(delete, new_container_name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ resp = retry(get, use_account=3)
+ listing = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assert_(new_container_name not in listing)
+
+ # even if they didn't create them
+ empty_container_name = str(uuid4())
+ resp = retry(put, empty_container_name, use_account=1)
+ resp.read()
+ self.assertEquals(resp.status, 201)
+ resp = retry(delete, empty_container_name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+
+ @requires_acls
+ def test_admin_acl_metadata(self):
+ if skip3:
+ raise SkipTest
+
+ def get(url, token, parsed, conn, name):
+ conn.request('GET', parsed.path + '/%s' % name, '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def post_account(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ def post(url, token, parsed, conn, name, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path + '/%s' % name, '', new_headers)
+ return check_response(conn)
+
+ # add some metadata
+ value = str(uuid4())
+ headers = {'x-container-meta-test': value}
+ resp = retry(post, self.name, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+ resp = retry(get, self.name, use_account=1)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), value)
+
+ # cannot see metadata
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # grant access
+ acl_user = swift_test_user[2]
+ acl = {'admin': [acl_user]}
+ headers = {'x-account-access-control': json.dumps(acl)}
+ resp = retry(post_account, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # can read container metadata
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), value)
+
+ # can also write container metadata
+ new_value = str(uuid4())
+ headers = {'x-container-meta-test': new_value}
+ resp = retry(post, self.name, headers=headers, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), new_value)
+
+ # and remove it
+ headers = {'x-remove-container-meta-test': 'true'}
+ resp = retry(post, self.name, headers=headers, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), None)
+
+ @requires_acls
+ def test_protected_container_sync(self):
+ if skip3:
+ raise SkipTest
+
+ def get(url, token, parsed, conn, name):
+ conn.request('GET', parsed.path + '/%s' % name, '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def post_account(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ def post(url, token, parsed, conn, name, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path + '/%s' % name, '', new_headers)
+ return check_response(conn)
+
+ # add some metadata
+ value = str(uuid4())
+ headers = {
+ 'x-container-sync-key': 'secret',
+ 'x-container-meta-test': value,
+ }
+ resp = retry(post, self.name, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+ resp = retry(get, self.name, use_account=1)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Sync-Key'), 'secret')
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), value)
+
+ # grant read-only access
+ acl_user = swift_test_user[2]
+ acl = {'read-only': [acl_user]}
+ headers = {'x-account-access-control': json.dumps(acl)}
+ resp = retry(post_account, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # can read container metadata
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), value)
+ # but not sync-key
+ self.assertEqual(resp.getheader('X-Container-Sync-Key'), None)
+
+ # and can not write
+ headers = {'x-container-sync-key': str(uuid4())}
+ resp = retry(post, self.name, headers=headers, use_account=3)
+ resp.read()
+ self.assertEqual(resp.status, 403)
+
+ # grant read-write access
+ acl_user = swift_test_user[2]
+ acl = {'read-write': [acl_user]}
+ headers = {'x-account-access-control': json.dumps(acl)}
+ resp = retry(post_account, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # can read container metadata
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), value)
+ # but not sync-key
+ self.assertEqual(resp.getheader('X-Container-Sync-Key'), None)
+
+ # sanity check sync-key w/ account1
+ resp = retry(get, self.name, use_account=1)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Sync-Key'), 'secret')
+
+ # and can write
+ new_value = str(uuid4())
+ headers = {
+ 'x-container-sync-key': str(uuid4()),
+ 'x-container-meta-test': new_value,
+ }
+ resp = retry(post, self.name, headers=headers, use_account=3)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+ resp = retry(get, self.name, use_account=1) # validate w/ account1
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), new_value)
+ # but can not write sync-key
+ self.assertEqual(resp.getheader('X-Container-Sync-Key'), 'secret')
+
+ # grant admin access
+ acl_user = swift_test_user[2]
+ acl = {'admin': [acl_user]}
+ headers = {'x-account-access-control': json.dumps(acl)}
+ resp = retry(post_account, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # admin can read container metadata
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), new_value)
+ # and ALSO sync-key
+ self.assertEqual(resp.getheader('X-Container-Sync-Key'), 'secret')
+
+ # admin tester3 can even change sync-key
+ new_secret = str(uuid4())
+ headers = {'x-container-sync-key': new_secret}
+ resp = retry(post, self.name, headers=headers, use_account=3)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Sync-Key'), new_secret)
+
+ @requires_acls
+ def test_protected_container_acl(self):
+ if skip3:
+ raise SkipTest
+
+ def get(url, token, parsed, conn, name):
+ conn.request('GET', parsed.path + '/%s' % name, '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def post_account(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ def post(url, token, parsed, conn, name, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path + '/%s' % name, '', new_headers)
+ return check_response(conn)
+
+ # add some container acls
+ value = str(uuid4())
+ headers = {
+ 'x-container-read': 'jdoe',
+ 'x-container-write': 'jdoe',
+ 'x-container-meta-test': value,
+ }
+ resp = retry(post, self.name, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+ resp = retry(get, self.name, use_account=1)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Read'), 'jdoe')
+ self.assertEqual(resp.getheader('X-Container-Write'), 'jdoe')
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), value)
+
+ # grant read-only access
+ acl_user = swift_test_user[2]
+ acl = {'read-only': [acl_user]}
+ headers = {'x-account-access-control': json.dumps(acl)}
+ resp = retry(post_account, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # can read container metadata
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), value)
+ # but not container acl
+ self.assertEqual(resp.getheader('X-Container-Read'), None)
+ self.assertEqual(resp.getheader('X-Container-Write'), None)
+
+ # and can not write
+ headers = {
+ 'x-container-read': 'frank',
+ 'x-container-write': 'frank',
+ }
+ resp = retry(post, self.name, headers=headers, use_account=3)
+ resp.read()
+ self.assertEqual(resp.status, 403)
+
+ # grant read-write access
+ acl_user = swift_test_user[2]
+ acl = {'read-write': [acl_user]}
+ headers = {'x-account-access-control': json.dumps(acl)}
+ resp = retry(post_account, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # can read container metadata
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), value)
+ # but not container acl
+ self.assertEqual(resp.getheader('X-Container-Read'), None)
+ self.assertEqual(resp.getheader('X-Container-Write'), None)
+
+ # sanity check container acls with account1
+ resp = retry(get, self.name, use_account=1)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Read'), 'jdoe')
+ self.assertEqual(resp.getheader('X-Container-Write'), 'jdoe')
+
+ # and can write
+ new_value = str(uuid4())
+ headers = {
+ 'x-container-read': 'frank',
+ 'x-container-write': 'frank',
+ 'x-container-meta-test': new_value,
+ }
+ resp = retry(post, self.name, headers=headers, use_account=3)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+ resp = retry(get, self.name, use_account=1) # validate w/ account1
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), new_value)
+ # but can not write container acls
+ self.assertEqual(resp.getheader('X-Container-Read'), 'jdoe')
+ self.assertEqual(resp.getheader('X-Container-Write'), 'jdoe')
+
+ # grant admin access
+ acl_user = swift_test_user[2]
+ acl = {'admin': [acl_user]}
+ headers = {'x-account-access-control': json.dumps(acl)}
+ resp = retry(post_account, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # admin can read container metadata
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Meta-Test'), new_value)
+ # and ALSO container acls
+ self.assertEqual(resp.getheader('X-Container-Read'), 'jdoe')
+ self.assertEqual(resp.getheader('X-Container-Write'), 'jdoe')
+
+ # admin tester3 can even change container acls
+ new_value = str(uuid4())
+ headers = {
+ 'x-container-read': '.r:*',
+ }
+ resp = retry(post, self.name, headers=headers, use_account=3)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+ resp = retry(get, self.name, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.getheader('X-Container-Read'), '.r:*')
def test_long_name_content_type(self):
if skip:
@@ -662,9 +1333,9 @@ class TestContainer(unittest.TestCase):
resp = retry(put)
resp.read()
- self.assertEquals(resp.status, 400)
- self.assertEquals(resp.getheader('Content-Type'),
- 'text/html; charset=UTF-8')
+ self.assertEqual(resp.status, 400)
+ self.assertEqual(resp.getheader('Content-Type'),
+ 'text/html; charset=UTF-8')
def test_null_name(self):
if skip:
@@ -677,10 +1348,10 @@ class TestContainer(unittest.TestCase):
resp = retry(put)
if (web_front_end == 'apache2'):
- self.assertEquals(resp.status, 404)
+ self.assertEqual(resp.status, 404)
else:
- self.assertEquals(resp.read(), 'Invalid UTF8 or contains NULL')
- self.assertEquals(resp.status, 412)
+ self.assertEqual(resp.read(), 'Invalid UTF8 or contains NULL')
+ self.assertEqual(resp.status, 412)
if __name__ == '__main__':
diff --git a/test/functional/test_object.py b/test/functional/test_object.py
index dad8635..675de30 100755
--- a/test/functional/test_object.py
+++ b/test/functional/test_object.py
@@ -19,8 +19,10 @@ import unittest
from nose import SkipTest
from uuid import uuid4
+from swift.common.utils import json
+
from swift_testing import check_response, retry, skip, skip3, \
- swift_test_perm, web_front_end
+ swift_test_perm, web_front_end, requires_acls, swift_test_user
class TestObject(unittest.TestCase):
@@ -36,7 +38,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(put)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
self.obj = uuid4().hex
def put(url, token, parsed, conn):
@@ -46,7 +48,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(put)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
def tearDown(self):
if skip:
@@ -66,13 +68,13 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(list)
object_listing = resp.read()
- self.assertEquals(resp.status, 200)
+ self.assertEqual(resp.status, 200)
# iterate over object listing and delete all objects
for obj in object_listing.splitlines():
resp = retry(delete, obj)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# delete the container
def delete(url, token, parsed, conn):
@@ -81,7 +83,33 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(delete)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
+
+ def test_if_none_match(self):
+ def put(url, token, parsed, conn):
+ conn.request('PUT', '%s/%s/%s' % (
+ parsed.path, self.container, 'if_none_match_test'), '',
+ {'X-Auth-Token': token,
+ 'Content-Length': '0',
+ 'If-None-Match': '*'})
+ return check_response(conn)
+ resp = retry(put)
+ resp.read()
+ self.assertEquals(resp.status, 201)
+ resp = retry(put)
+ resp.read()
+ self.assertEquals(resp.status, 412)
+
+ def put(url, token, parsed, conn):
+ conn.request('PUT', '%s/%s/%s' % (
+ parsed.path, self.container, 'if_none_match_test'), '',
+ {'X-Auth-Token': token,
+ 'Content-Length': '0',
+ 'If-None-Match': 'somethingelse'})
+ return check_response(conn)
+ resp = retry(put)
+ resp.read()
+ self.assertEquals(resp.status, 400)
def test_copy_object(self):
if skip:
@@ -98,8 +126,8 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(get_source)
source_contents = resp.read()
- self.assertEquals(resp.status, 200)
- self.assertEquals(source_contents, 'test')
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(source_contents, 'test')
# copy source to dest with X-Copy-From
def put(url, token, parsed, conn):
@@ -110,7 +138,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(put)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
# contents of dest should be the same as source
def get_dest(url, token, parsed, conn):
@@ -120,8 +148,8 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(get_dest)
dest_contents = resp.read()
- self.assertEquals(resp.status, 200)
- self.assertEquals(dest_contents, source_contents)
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(dest_contents, source_contents)
# delete the copy
def delete(url, token, parsed, conn):
@@ -130,11 +158,11 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(delete)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# verify dest does not exist
resp = retry(get_dest)
resp.read()
- self.assertEquals(resp.status, 404)
+ self.assertEqual(resp.status, 404)
# copy source to dest with COPY
def copy(url, token, parsed, conn):
@@ -144,18 +172,18 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(copy)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
# contents of dest should be the same as source
resp = retry(get_dest)
dest_contents = resp.read()
- self.assertEquals(resp.status, 200)
- self.assertEquals(dest_contents, source_contents)
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(dest_contents, source_contents)
# delete the copy
resp = retry(delete)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
def test_public_object(self):
if skip:
@@ -178,10 +206,10 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(post)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
resp = retry(get)
resp.read()
- self.assertEquals(resp.status, 200)
+ self.assertEqual(resp.status, 200)
def post(url, token, parsed, conn):
conn.request('POST', parsed.path + '/' + self.container, '',
@@ -189,7 +217,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(post)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
try:
resp = retry(get)
raise Exception('Should not have been able to GET')
@@ -208,7 +236,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(get, use_account=3)
resp.read()
- self.assertEquals(resp.status, 403)
+ self.assertEqual(resp.status, 403)
# create a shared container writable by account3
shared_container = uuid4().hex
@@ -222,7 +250,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(put)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
# verify third account can not copy from private container
def copy(url, token, parsed, conn):
@@ -234,7 +262,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(copy, use_account=3)
resp.read()
- self.assertEquals(resp.status, 403)
+ self.assertEqual(resp.status, 403)
# verify third account can write "obj1" to shared container
def put(url, token, parsed, conn):
@@ -244,7 +272,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(put, use_account=3)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
# verify third account can copy "obj1" to shared container
def copy2(url, token, parsed, conn):
@@ -255,7 +283,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(copy2, use_account=3)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
# verify third account STILL can not copy from private container
def copy3(url, token, parsed, conn):
@@ -267,7 +295,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(copy3, use_account=3)
resp.read()
- self.assertEquals(resp.status, 403)
+ self.assertEqual(resp.status, 403)
# clean up "obj1"
def delete(url, token, parsed, conn):
@@ -277,7 +305,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(delete)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# clean up shared_container
def delete(url, token, parsed, conn):
@@ -287,8 +315,251 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(delete)
resp.read()
+ self.assertEqual(resp.status, 204)
+
+ @requires_acls
+ def test_read_only(self):
+ if skip3:
+ raise SkipTest
+
+ def get_listing(url, token, parsed, conn):
+ conn.request('GET', '%s/%s' % (parsed.path, self.container), '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def post_account(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ def get(url, token, parsed, conn, name):
+ conn.request('GET', '%s/%s/%s' % (
+ parsed.path, self.container, name), '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def put(url, token, parsed, conn, name):
+ conn.request('PUT', '%s/%s/%s' % (
+ parsed.path, self.container, name), 'test',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def delete(url, token, parsed, conn, name):
+ conn.request('PUT', '%s/%s/%s' % (
+ parsed.path, self.container, name), '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ # cannot list objects
+ resp = retry(get_listing, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # cannot get object
+ resp = retry(get, self.obj, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # grant read-only access
+ acl_user = swift_test_user[2]
+ acl = {'read-only': [acl_user]}
+ headers = {'x-account-access-control': json.dumps(acl)}
+ resp = retry(post_account, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # can list objects
+ resp = retry(get_listing, use_account=3)
+ listing = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assert_(self.obj in listing)
+
+ # can get object
+ resp = retry(get, self.obj, use_account=3)
+ body = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assertEquals(body, 'test')
+
+ # can not put an object
+ obj_name = str(uuid4())
+ resp = retry(put, obj_name, use_account=3)
+ body = resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # can not delete an object
+ resp = retry(delete, self.obj, use_account=3)
+ body = resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # sanity with account1
+ resp = retry(get_listing, use_account=3)
+ listing = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assert_(obj_name not in listing)
+ self.assert_(self.obj in listing)
+
+ @requires_acls
+ def test_read_write(self):
+ if skip3:
+ raise SkipTest
+
+ def get_listing(url, token, parsed, conn):
+ conn.request('GET', '%s/%s' % (parsed.path, self.container), '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def post_account(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ def get(url, token, parsed, conn, name):
+ conn.request('GET', '%s/%s/%s' % (
+ parsed.path, self.container, name), '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def put(url, token, parsed, conn, name):
+ conn.request('PUT', '%s/%s/%s' % (
+ parsed.path, self.container, name), 'test',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def delete(url, token, parsed, conn, name):
+ conn.request('DELETE', '%s/%s/%s' % (
+ parsed.path, self.container, name), '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ # cannot list objects
+ resp = retry(get_listing, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # cannot get object
+ resp = retry(get, self.obj, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # grant read-write access
+ acl_user = swift_test_user[2]
+ acl = {'read-write': [acl_user]}
+ headers = {'x-account-access-control': json.dumps(acl)}
+ resp = retry(post_account, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # can list objects
+ resp = retry(get_listing, use_account=3)
+ listing = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assert_(self.obj in listing)
+
+ # can get object
+ resp = retry(get, self.obj, use_account=3)
+ body = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assertEquals(body, 'test')
+
+ # can put an object
+ obj_name = str(uuid4())
+ resp = retry(put, obj_name, use_account=3)
+ body = resp.read()
+ self.assertEquals(resp.status, 201)
+
+ # can delete an object
+ resp = retry(delete, self.obj, use_account=3)
+ body = resp.read()
self.assertEquals(resp.status, 204)
+ # sanity with account1
+ resp = retry(get_listing, use_account=3)
+ listing = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assert_(obj_name in listing)
+ self.assert_(self.obj not in listing)
+
+ @requires_acls
+ def test_admin(self):
+ if skip3:
+ raise SkipTest
+
+ def get_listing(url, token, parsed, conn):
+ conn.request('GET', '%s/%s' % (parsed.path, self.container), '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def post_account(url, token, parsed, conn, headers):
+ new_headers = dict({'X-Auth-Token': token}, **headers)
+ conn.request('POST', parsed.path, '', new_headers)
+ return check_response(conn)
+
+ def get(url, token, parsed, conn, name):
+ conn.request('GET', '%s/%s/%s' % (
+ parsed.path, self.container, name), '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def put(url, token, parsed, conn, name):
+ conn.request('PUT', '%s/%s/%s' % (
+ parsed.path, self.container, name), 'test',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def delete(url, token, parsed, conn, name):
+ conn.request('DELETE', '%s/%s/%s' % (
+ parsed.path, self.container, name), '',
+ {'X-Auth-Token': token})
+ return check_response(conn)
+
+ # cannot list objects
+ resp = retry(get_listing, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # cannot get object
+ resp = retry(get, self.obj, use_account=3)
+ resp.read()
+ self.assertEquals(resp.status, 403)
+
+ # grant admin access
+ acl_user = swift_test_user[2]
+ acl = {'admin': [acl_user]}
+ headers = {'x-account-access-control': json.dumps(acl)}
+ resp = retry(post_account, headers=headers, use_account=1)
+ resp.read()
+ self.assertEqual(resp.status, 204)
+
+ # can list objects
+ resp = retry(get_listing, use_account=3)
+ listing = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assert_(self.obj in listing)
+
+ # can get object
+ resp = retry(get, self.obj, use_account=3)
+ body = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assertEquals(body, 'test')
+
+ # can put an object
+ obj_name = str(uuid4())
+ resp = retry(put, obj_name, use_account=3)
+ body = resp.read()
+ self.assertEquals(resp.status, 201)
+
+ # can delete an object
+ resp = retry(delete, self.obj, use_account=3)
+ body = resp.read()
+ self.assertEquals(resp.status, 204)
+
+ # sanity with account1
+ resp = retry(get_listing, use_account=3)
+ listing = resp.read()
+ self.assertEquals(resp.status, 200)
+ self.assert_(obj_name in listing)
+ self.assert_(self.obj not in listing)
+
def test_manifest(self):
if skip:
raise SkipTest
@@ -306,7 +577,7 @@ class TestObject(unittest.TestCase):
for objnum in xrange(len(segments1)):
resp = retry(put, objnum)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
# Upload the manifest
def put(url, token, parsed, conn):
@@ -318,7 +589,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(put)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
# Get the manifest (should get all the segments as the body)
def get(url, token, parsed, conn):
@@ -326,9 +597,9 @@ class TestObject(unittest.TestCase):
parsed.path, self.container), '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get)
- self.assertEquals(resp.read(), ''.join(segments1))
- self.assertEquals(resp.status, 200)
- self.assertEquals(resp.getheader('content-type'), 'text/jibberish')
+ self.assertEqual(resp.read(), ''.join(segments1))
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(resp.getheader('content-type'), 'text/jibberish')
# Get with a range at the start of the second segment
def get(url, token, parsed, conn):
@@ -337,8 +608,8 @@ class TestObject(unittest.TestCase):
'X-Auth-Token': token, 'Range': 'bytes=3-'})
return check_response(conn)
resp = retry(get)
- self.assertEquals(resp.read(), ''.join(segments1[1:]))
- self.assertEquals(resp.status, 206)
+ self.assertEqual(resp.read(), ''.join(segments1[1:]))
+ self.assertEqual(resp.status, 206)
# Get with a range in the middle of the second segment
def get(url, token, parsed, conn):
@@ -347,8 +618,8 @@ class TestObject(unittest.TestCase):
'X-Auth-Token': token, 'Range': 'bytes=5-'})
return check_response(conn)
resp = retry(get)
- self.assertEquals(resp.read(), ''.join(segments1)[5:])
- self.assertEquals(resp.status, 206)
+ self.assertEqual(resp.read(), ''.join(segments1)[5:])
+ self.assertEqual(resp.status, 206)
# Get with a full start and stop range
def get(url, token, parsed, conn):
@@ -357,8 +628,8 @@ class TestObject(unittest.TestCase):
'X-Auth-Token': token, 'Range': 'bytes=5-10'})
return check_response(conn)
resp = retry(get)
- self.assertEquals(resp.read(), ''.join(segments1)[5:11])
- self.assertEquals(resp.status, 206)
+ self.assertEqual(resp.read(), ''.join(segments1)[5:11])
+ self.assertEqual(resp.status, 206)
# Upload the second set of segments
def put(url, token, parsed, conn, objnum):
@@ -369,7 +640,7 @@ class TestObject(unittest.TestCase):
for objnum in xrange(len(segments2)):
resp = retry(put, objnum)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
# Get the manifest (should still be the first segments of course)
def get(url, token, parsed, conn):
@@ -377,8 +648,8 @@ class TestObject(unittest.TestCase):
parsed.path, self.container), '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get)
- self.assertEquals(resp.read(), ''.join(segments1))
- self.assertEquals(resp.status, 200)
+ self.assertEqual(resp.read(), ''.join(segments1))
+ self.assertEqual(resp.status, 200)
# Update the manifest
def put(url, token, parsed, conn):
@@ -390,7 +661,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(put)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
# Get the manifest (should be the second set of segments now)
def get(url, token, parsed, conn):
@@ -398,8 +669,8 @@ class TestObject(unittest.TestCase):
parsed.path, self.container), '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get)
- self.assertEquals(resp.read(), ''.join(segments2))
- self.assertEquals(resp.status, 200)
+ self.assertEqual(resp.read(), ''.join(segments2))
+ self.assertEqual(resp.status, 200)
if not skip3:
@@ -410,7 +681,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(get, use_account=3)
resp.read()
- self.assertEquals(resp.status, 403)
+ self.assertEqual(resp.status, 403)
# Grant access to the third account
def post(url, token, parsed, conn):
@@ -420,7 +691,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(post)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# The third account should be able to get the manifest now
def get(url, token, parsed, conn):
@@ -428,8 +699,8 @@ class TestObject(unittest.TestCase):
parsed.path, self.container), '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get, use_account=3)
- self.assertEquals(resp.read(), ''.join(segments2))
- self.assertEquals(resp.status, 200)
+ self.assertEqual(resp.read(), ''.join(segments2))
+ self.assertEqual(resp.status, 200)
# Create another container for the third set of segments
acontainer = uuid4().hex
@@ -440,7 +711,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(put)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
# Upload the third set of segments in the other container
def put(url, token, parsed, conn, objnum):
@@ -451,7 +722,7 @@ class TestObject(unittest.TestCase):
for objnum in xrange(len(segments3)):
resp = retry(put, objnum)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
# Update the manifest
def put(url, token, parsed, conn):
@@ -463,7 +734,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(put)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
# Get the manifest to ensure it's the third set of segments
def get(url, token, parsed, conn):
@@ -471,8 +742,8 @@ class TestObject(unittest.TestCase):
parsed.path, self.container), '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get)
- self.assertEquals(resp.read(), ''.join(segments3))
- self.assertEquals(resp.status, 200)
+ self.assertEqual(resp.read(), ''.join(segments3))
+ self.assertEqual(resp.status, 200)
if not skip3:
@@ -486,7 +757,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(get, use_account=3)
resp.read()
- self.assertEquals(resp.status, 403)
+ self.assertEqual(resp.status, 403)
# Grant access to the third account
def post(url, token, parsed, conn):
@@ -496,7 +767,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(post)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# The third account should be able to get the manifest now
def get(url, token, parsed, conn):
@@ -504,8 +775,8 @@ class TestObject(unittest.TestCase):
parsed.path, self.container), '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get, use_account=3)
- self.assertEquals(resp.read(), ''.join(segments3))
- self.assertEquals(resp.status, 200)
+ self.assertEqual(resp.read(), ''.join(segments3))
+ self.assertEqual(resp.status, 200)
# Delete the manifest
def delete(url, token, parsed, conn, objnum):
@@ -515,7 +786,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(delete, objnum)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# Delete the third set of segments
def delete(url, token, parsed, conn, objnum):
@@ -526,7 +797,7 @@ class TestObject(unittest.TestCase):
for objnum in xrange(len(segments3)):
resp = retry(delete, objnum)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# Delete the second set of segments
def delete(url, token, parsed, conn, objnum):
@@ -537,7 +808,7 @@ class TestObject(unittest.TestCase):
for objnum in xrange(len(segments2)):
resp = retry(delete, objnum)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# Delete the first set of segments
def delete(url, token, parsed, conn, objnum):
@@ -548,7 +819,7 @@ class TestObject(unittest.TestCase):
for objnum in xrange(len(segments1)):
resp = retry(delete, objnum)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
# Delete the extra container
def delete(url, token, parsed, conn):
@@ -557,7 +828,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(delete)
resp.read()
- self.assertEquals(resp.status, 204)
+ self.assertEqual(resp.status, 204)
def test_delete_content_type(self):
if skip:
@@ -569,7 +840,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(put)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
def delete(url, token, parsed, conn):
conn.request('DELETE', '%s/%s/hi' % (parsed.path, self.container),
@@ -577,9 +848,9 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(delete)
resp.read()
- self.assertEquals(resp.status, 204)
- self.assertEquals(resp.getheader('Content-Type'),
- 'text/html; charset=UTF-8')
+ self.assertEqual(resp.status, 204)
+ self.assertEqual(resp.getheader('Content-Type'),
+ 'text/html; charset=UTF-8')
def test_delete_if_delete_at_bad(self):
if skip:
@@ -592,7 +863,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(put)
resp.read()
- self.assertEquals(resp.status, 201)
+ self.assertEqual(resp.status, 201)
def delete(url, token, parsed, conn):
conn.request('DELETE', '%s/%s/hi' % (parsed.path, self.container),
@@ -601,7 +872,7 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(delete)
resp.read()
- self.assertEquals(resp.status, 400)
+ self.assertEqual(resp.status, 400)
def test_null_name(self):
if skip:
@@ -614,10 +885,121 @@ class TestObject(unittest.TestCase):
return check_response(conn)
resp = retry(put)
if (web_front_end == 'apache2'):
- self.assertEquals(resp.status, 404)
+ self.assertEqual(resp.status, 404)
else:
- self.assertEquals(resp.read(), 'Invalid UTF8 or contains NULL')
- self.assertEquals(resp.status, 412)
+ self.assertEqual(resp.read(), 'Invalid UTF8 or contains NULL')
+ self.assertEqual(resp.status, 412)
+
+ def test_cors(self):
+ if skip:
+ raise SkipTest
+
+ def is_strict_mode(url, token, parsed, conn):
+ conn.request('GET', '/info')
+ resp = conn.getresponse()
+ if resp.status // 100 == 2:
+ info = json.loads(resp.read())
+ return info.get('swift', {}).get('strict_cors_mode', False)
+ return False
+
+ def put_cors_cont(url, token, parsed, conn, orig):
+ conn.request(
+ 'PUT', '%s/%s' % (parsed.path, self.container),
+ '', {'X-Auth-Token': token,
+ 'X-Container-Meta-Access-Control-Allow-Origin': orig})
+ return check_response(conn)
+
+ def put_obj(url, token, parsed, conn, obj):
+ conn.request(
+ 'PUT', '%s/%s/%s' % (parsed.path, self.container, obj),
+ 'test', {'X-Auth-Token': token})
+ return check_response(conn)
+
+ def check_cors(url, token, parsed, conn,
+ method, obj, headers):
+ if method != 'OPTIONS':
+ headers['X-Auth-Token'] = token
+ conn.request(
+ method, '%s/%s/%s' % (parsed.path, self.container, obj),
+ '', headers)
+ return conn.getresponse()
+
+ strict_cors = retry(is_strict_mode)
+
+ resp = retry(put_cors_cont, '*')
+ resp.read()
+ self.assertEquals(resp.status // 100, 2)
+
+ resp = retry(put_obj, 'cat')
+ resp.read()
+ self.assertEquals(resp.status // 100, 2)
+
+ resp = retry(check_cors,
+ 'OPTIONS', 'cat', {'Origin': 'http://m.com'})
+ self.assertEquals(resp.status, 401)
+
+ resp = retry(check_cors,
+ 'OPTIONS', 'cat',
+ {'Origin': 'http://m.com',
+ 'Access-Control-Request-Method': 'GET'})
+
+ self.assertEquals(resp.status, 200)
+ resp.read()
+ headers = dict((k.lower(), v) for k, v in resp.getheaders())
+ self.assertEquals(headers.get('access-control-allow-origin'),
+ '*')
+
+ resp = retry(check_cors,
+ 'GET', 'cat', {'Origin': 'http://m.com'})
+ self.assertEquals(resp.status, 200)
+ headers = dict((k.lower(), v) for k, v in resp.getheaders())
+ self.assertEquals(headers.get('access-control-allow-origin'),
+ '*')
+
+ resp = retry(check_cors,
+ 'GET', 'cat', {'Origin': 'http://m.com',
+ 'X-Web-Mode': 'True'})
+ self.assertEquals(resp.status, 200)
+ headers = dict((k.lower(), v) for k, v in resp.getheaders())
+ self.assertEquals(headers.get('access-control-allow-origin'),
+ '*')
+
+ ####################
+
+ resp = retry(put_cors_cont, 'http://secret.com')
+ resp.read()
+ self.assertEquals(resp.status // 100, 2)
+
+ resp = retry(check_cors,
+ 'OPTIONS', 'cat',
+ {'Origin': 'http://m.com',
+ 'Access-Control-Request-Method': 'GET'})
+ resp.read()
+ self.assertEquals(resp.status, 401)
+
+ if strict_cors:
+ resp = retry(check_cors,
+ 'GET', 'cat', {'Origin': 'http://m.com'})
+ resp.read()
+ self.assertEquals(resp.status, 200)
+ headers = dict((k.lower(), v) for k, v in resp.getheaders())
+ self.assertTrue('access-control-allow-origin' not in headers)
+
+ resp = retry(check_cors,
+ 'GET', 'cat', {'Origin': 'http://secret.com'})
+ resp.read()
+ self.assertEquals(resp.status, 200)
+ headers = dict((k.lower(), v) for k, v in resp.getheaders())
+ self.assertEquals(headers.get('access-control-allow-origin'),
+ 'http://secret.com')
+ else:
+ resp = retry(check_cors,
+ 'GET', 'cat', {'Origin': 'http://m.com'})
+ resp.read()
+ self.assertEquals(resp.status, 200)
+ headers = dict((k.lower(), v) for k, v in resp.getheaders())
+ self.assertEquals(headers.get('access-control-allow-origin'),
+ 'http://m.com')
if __name__ == '__main__':
diff --git a/test/functional/tests.py b/test/functional/tests.py
index 0d9a9ef..ad87d7e 100644
--- a/test/functional/tests.py
+++ b/test/functional/tests.py
@@ -19,14 +19,16 @@
from datetime import datetime
import os
import hashlib
+import hmac
import json
import locale
import random
import StringIO
import time
import threading
-import uuid
import unittest
+import urllib
+import uuid
from nose import SkipTest
from ConfigParser import ConfigParser
@@ -36,7 +38,7 @@ from test.functional.swift_test_client import Account, Connection, File, \
from swift.common.constraints import MAX_FILE_SIZE, MAX_META_NAME_LENGTH, \
MAX_META_VALUE_LENGTH, MAX_META_COUNT, MAX_META_OVERALL_SIZE, \
MAX_OBJECT_NAME_LENGTH, CONTAINER_LISTING_LIMIT, ACCOUNT_LISTING_LIMIT, \
- MAX_ACCOUNT_NAME_LENGTH, MAX_CONTAINER_NAME_LENGTH
+ MAX_ACCOUNT_NAME_LENGTH, MAX_CONTAINER_NAME_LENGTH, MAX_HEADER_SIZE
from gluster.swift.common.constraints import \
set_object_name_component_length, get_object_name_component_length
@@ -50,7 +52,8 @@ default_constraints = dict((
('container_listing_limit', CONTAINER_LISTING_LIMIT),
('account_listing_limit', ACCOUNT_LISTING_LIMIT),
('max_account_name_length', MAX_ACCOUNT_NAME_LENGTH),
- ('max_container_name_length', MAX_CONTAINER_NAME_LENGTH)))
+ ('max_container_name_length', MAX_CONTAINER_NAME_LENGTH),
+ ('max_header_size', MAX_HEADER_SIZE)))
constraints_conf = ConfigParser()
conf_exists = constraints_conf.read('/etc/swift/swift.conf')
# Constraints are set first from the test config, then from
@@ -285,7 +288,7 @@ class TestAccount(Base):
if try_count < 5:
time.sleep(1)
- self.assertEquals(info['container_count'], len(self.env.containers))
+ self.assertEqual(info['container_count'], len(self.env.containers))
self.assert_status(204)
def testContainerSerializedInfo(self):
@@ -309,11 +312,11 @@ class TestAccount(Base):
headers = dict(self.env.conn.response.getheaders())
if format_type == 'json':
- self.assertEquals(headers['content-type'],
- 'application/json; charset=utf-8')
+ self.assertEqual(headers['content-type'],
+ 'application/json; charset=utf-8')
elif format_type == 'xml':
- self.assertEquals(headers['content-type'],
- 'application/xml; charset=utf-8')
+ self.assertEqual(headers['content-type'],
+ 'application/xml; charset=utf-8')
def testListingLimit(self):
limit = load_constraint('account_listing_limit')
@@ -337,7 +340,7 @@ class TestAccount(Base):
if isinstance(b[0], dict):
b = [x['name'] for x in b]
- self.assertEquals(a, b)
+ self.assertEqual(a, b)
def testInvalidAuthToken(self):
hdrs = {'X-Auth-Token': 'bogus_auth_token'}
@@ -347,12 +350,12 @@ class TestAccount(Base):
def testLastContainerMarker(self):
for format_type in [None, 'json', 'xml']:
containers = self.env.account.containers({'format': format_type})
- self.assertEquals(len(containers), len(self.env.containers))
+ self.assertEqual(len(containers), len(self.env.containers))
self.assert_status(200)
containers = self.env.account.containers(
parms={'format': format_type, 'marker': containers[-1]})
- self.assertEquals(len(containers), 0)
+ self.assertEqual(len(containers), 0)
if format_type is None:
self.assert_status(204)
else:
@@ -380,8 +383,8 @@ class TestAccount(Base):
parms={'format': format_type})
if isinstance(containers[0], dict):
containers = [x['name'] for x in containers]
- self.assertEquals(sorted(containers, cmp=locale.strcoll),
- containers)
+ self.assertEqual(sorted(containers, cmp=locale.strcoll),
+ containers)
class TestAccountUTF8(Base2, TestAccount):
@@ -518,13 +521,13 @@ class TestContainer(Base):
for format_type in [None, 'json', 'xml']:
for prefix in prefixs:
files = cont.files(parms={'prefix': prefix})
- self.assertEquals(files, sorted(prefix_files[prefix]))
+ self.assertEqual(files, sorted(prefix_files[prefix]))
for format_type in [None, 'json', 'xml']:
for prefix in prefixs:
files = cont.files(parms={'limit': limit_count,
'prefix': prefix})
- self.assertEquals(len(files), limit_count)
+ self.assertEqual(len(files), limit_count)
for file_item in files:
self.assert_(file_item.startswith(prefix))
@@ -548,7 +551,7 @@ class TestContainer(Base):
container = self.env.account.container(valid_utf8)
self.assert_(container.create(cfg={'no_path_quote': True}))
self.assert_(container.name in self.env.account.containers())
- self.assertEquals(container.files(), [])
+ self.assertEqual(container.files(), [])
self.assert_(container.delete())
container = self.env.account.container(invalid_utf8)
@@ -614,12 +617,12 @@ class TestContainer(Base):
def testLastFileMarker(self):
for format_type in [None, 'json', 'xml']:
files = self.env.container.files({'format': format_type})
- self.assertEquals(len(files), len(self.env.files))
+ self.assertEqual(len(files), len(self.env.files))
self.assert_status(200)
files = self.env.container.files(
parms={'format': format_type, 'marker': files[-1]})
- self.assertEquals(len(files), 0)
+ self.assertEqual(len(files), 0)
if format_type is None:
self.assert_status(204)
@@ -665,14 +668,14 @@ class TestContainer(Base):
files = self.env.container.files(parms={'format': format_type})
if isinstance(files[0], dict):
files = [x['name'] for x in files]
- self.assertEquals(sorted(files, cmp=locale.strcoll), files)
+ self.assertEqual(sorted(files, cmp=locale.strcoll), files)
def testContainerInfo(self):
info = self.env.container.info()
self.assert_status(204)
- self.assertEquals(info['object_count'], self.env.file_count)
- self.assertEquals(info['bytes_used'],
- self.env.file_count * self.env.file_size)
+ self.assertEqual(info['object_count'], self.env.file_count)
+ self.assertEqual(info['bytes_used'],
+ self.env.file_count * self.env.file_size)
def testContainerInfoOnContainerThatDoesNotExist(self):
container = self.env.account.container(Utils.create_name())
@@ -683,7 +686,7 @@ class TestContainer(Base):
for format_type in [None, 'json', 'xml']:
files = self.env.container.files(parms={'format': format_type,
'limit': 2})
- self.assertEquals(len(files), 2)
+ self.assertEqual(len(files), 2)
def testTooLongName(self):
cont = self.env.account.container('x' * 257)
@@ -838,7 +841,7 @@ class TestContainerPaths(Base):
if isinstance(files[0], dict):
files = [str(x['name']) for x in files]
- self.assertEquals(files, self.env.stored_files)
+ self.assertEqual(files, self.env.stored_files)
for format_type in ('json', 'xml'):
for file_item in self.env.container.files(parms={'format':
@@ -846,13 +849,13 @@ class TestContainerPaths(Base):
self.assert_(int(file_item['bytes']) >= 0)
self.assert_('last_modified' in file_item)
if file_item['name'].endswith('/'):
- self.assertEquals(file_item['content_type'],
- 'application/directory')
+ self.assertEqual(file_item['content_type'],
+ 'application/directory')
def testStructure(self):
def assert_listing(path, file_list):
files = self.env.container.files(parms={'path': path})
- self.assertEquals(sorted(file_list, cmp=locale.strcoll), files)
+ self.assertEqual(sorted(file_list, cmp=locale.strcoll), files)
if not normalized_urls:
assert_listing('/', ['/dir1/', '/dir2/', '/file1', '/file A'])
assert_listing('/dir1',
@@ -1176,7 +1179,7 @@ class TestFile(Base):
for i in container.files(parms={'format': 'json'}):
file_types_read[i['name'].split('.')[1]] = i['content_type']
- self.assertEquals(file_types, file_types_read)
+ self.assertEqual(file_types, file_types_read)
def testRangedGets(self):
file_length = 10000
@@ -1201,7 +1204,7 @@ class TestFile(Base):
self.assertRaises(ResponseError, file_item.read, hdrs=hdrs)
self.assert_status(416)
else:
- self.assertEquals(file_item.read(hdrs=hdrs), data[-i:])
+ self.assertEqual(file_item.read(hdrs=hdrs), data[-i:])
range_string = 'bytes=%d-' % (i)
hdrs = {'Range': range_string}
@@ -1350,9 +1353,9 @@ class TestFile(Base):
info = file_item.info()
self.assert_status(200)
- self.assertEquals(info['content_length'], self.env.file_size)
- self.assertEquals(info['etag'], md5)
- self.assertEquals(info['content_type'], content_type)
+ self.assertEqual(info['content_length'], self.env.file_size)
+ self.assertEqual(info['etag'], md5)
+ self.assertEqual(info['content_type'], content_type)
self.assert_('last_modified' in info)
def testDeleteOfFileThatDoesNotExist(self):
@@ -1395,7 +1398,7 @@ class TestFile(Base):
file_item = self.env.container.file(file_item.name)
self.assert_(file_item.initialize())
self.assert_status(200)
- self.assertEquals(file_item.metadata, metadata)
+ self.assertEqual(file_item.metadata, metadata)
def testGetContentType(self):
file_name = Utils.create_name()
@@ -1408,7 +1411,7 @@ class TestFile(Base):
file_item = self.env.container.file(file_name)
file_item.read()
- self.assertEquals(content_type, file_item.content_type)
+ self.assertEqual(content_type, file_item.content_type)
def testGetOnFileThatDoesNotExist(self):
# in container that exists
@@ -1449,7 +1452,7 @@ class TestFile(Base):
file_item = self.env.container.file(file_item.name)
self.assert_(file_item.initialize())
self.assert_status(200)
- self.assertEquals(file_item.metadata, metadata)
+ self.assertEqual(file_item.metadata, metadata)
def testSerialization(self):
container = self.env.account.container(Utils.create_name())
@@ -1478,9 +1481,9 @@ class TestFile(Base):
if f['name'] != file_item['name']:
continue
- self.assertEquals(file_item['content_type'],
- f['content_type'])
- self.assertEquals(int(file_item['bytes']), f['bytes'])
+ self.assertEqual(file_item['content_type'],
+ f['content_type'])
+ self.assertEqual(int(file_item['bytes']), f['bytes'])
d = datetime.strptime(
file_item['last_modified'].split('.')[0],
@@ -1488,7 +1491,7 @@ class TestFile(Base):
lm = time.mktime(d.timetuple())
if 'last_modified' in f:
- self.assertEquals(f['last_modified'], lm)
+ self.assertEqual(f['last_modified'], lm)
else:
f['last_modified'] = lm
@@ -1500,11 +1503,11 @@ class TestFile(Base):
headers = dict(self.env.conn.response.getheaders())
if format_type == 'json':
- self.assertEquals(headers['content-type'],
- 'application/json; charset=utf-8')
+ self.assertEqual(headers['content-type'],
+ 'application/json; charset=utf-8')
elif format_type == 'xml':
- self.assertEquals(headers['content-type'],
- 'application/xml; charset=utf-8')
+ self.assertEqual(headers['content-type'],
+ 'application/xml; charset=utf-8')
lm_diff = max([f['last_modified'] for f in files]) -\
min([f['last_modified'] for f in files])
@@ -1547,7 +1550,7 @@ class TestFile(Base):
self.assert_('etag' in headers.keys())
header_etag = headers['etag'].strip('"')
- self.assertEquals(etag, header_etag)
+ self.assertEqual(etag, header_etag)
def testChunkedPut(self):
if (web_front_end == 'apache2'):
@@ -1565,7 +1568,7 @@ class TestFile(Base):
self.assert_(data == file_item.read())
info = file_item.info()
- self.assertEquals(etag, info['etag'])
+ self.assertEqual(etag, info['etag'])
class TestFileUTF8(Base2, TestFile):
@@ -1677,12 +1680,30 @@ class TestDlo(Base):
file_contents,
"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffffffffff")
+ def test_copy_manifest(self):
+ # Copying the manifest should result in another manifest
+ try:
+ man1_item = self.env.container.file('man1')
+ man1_item.copy(self.env.container.name, "copied-man1",
+ parms={'multipart-manifest': 'get'})
+
+ copied = self.env.container.file("copied-man1")
+ copied_contents = copied.read(parms={'multipart-manifest': 'get'})
+ self.assertEqual(copied_contents, "man1-contents")
+
+ copied_contents = copied.read()
+ self.assertEqual(
+ copied_contents,
+ "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee")
+ finally:
+ # try not to leave this around for other tests to stumble over
+ self.env.container.file("copied-man1").delete()
class TestDloUTF8(Base2, TestDlo):
set_up = False
-class TestFileComparisonEnv:
+class TestFileComparisonEnv(object):
@classmethod
def setUp(cls):
cls.conn = Connection(config)
@@ -1806,19 +1827,8 @@ class TestSloEnv(object):
cls.conn.authenticate()
if cls.slo_enabled is None:
- status = cls.conn.make_request('GET', '/info',
- cfg={'verbatim_path': True})
- if not (200 <= status <= 299):
- # Can't tell if SLO is enabled or not since we're running
- # against an old cluster, so let's skip the tests instead of
- # possibly having spurious failures.
- cls.slo_enabled = False
- else:
- # Don't bother looking for ValueError here. If something is
- # responding to a GET /info request with invalid JSON, then
- # the cluster is broken and a test failure will let us know.
- cluster_info = json.loads(cls.conn.response.read())
- cls.slo_enabled = 'slo' in cluster_info
+ cluster_info = cls.conn.cluster_info()
+ cls.slo_enabled = 'slo' in cluster_info
if not cls.slo_enabled:
return
@@ -2034,5 +2044,347 @@ class TestSloUTF8(Base2, TestSlo):
set_up = False
+class TestObjectVersioningEnv(object):
+ versioning_enabled = None # tri-state: None initially, then True/False
+
+ @classmethod
+ def setUp(cls):
+ cls.conn = Connection(config)
+ cls.conn.authenticate()
+
+ cls.account = Account(cls.conn, config.get('account',
+ config['username']))
+
+ # avoid getting a prefix that stops halfway through an encoded
+ # character
+ prefix = Utils.create_name().decode("utf-8")[:10].encode("utf-8")
+
+ cls.versions_container = cls.account.container(prefix + "-versions")
+ if not cls.versions_container.create():
+ raise ResponseError(cls.conn.response)
+
+ cls.container = cls.account.container(prefix + "-objs")
+ if not cls.container.create(
+ hdrs={'X-Versions-Location': cls.versions_container.name}):
+ raise ResponseError(cls.conn.response)
+
+ container_info = cls.container.info()
+ # if versioning is off, then X-Versions-Location won't persist
+ cls.versioning_enabled = 'versions' in container_info
+
+
+class TestObjectVersioning(Base):
+ env = TestObjectVersioningEnv
+ set_up = False
+
+ def setUp(self):
+ super(TestObjectVersioning, self).setUp()
+ if self.env.versioning_enabled is False:
+ raise SkipTest("Object versioning not enabled")
+ elif self.env.versioning_enabled is not True:
+ # just some sanity checking
+ raise Exception(
+ "Expected versioning_enabled to be True/False, got %r" %
+ (self.env.versioning_enabled,))
+
+ def test_overwriting(self):
+ container = self.env.container
+ versions_container = self.env.versions_container
+ obj_name = Utils.create_name()
+
+ versioned_obj = container.file(obj_name)
+ versioned_obj.write("aaaaa")
+
+ self.assertEqual(0, versions_container.info()['object_count'])
+
+ versioned_obj.write("bbbbb")
+
+ # the old version got saved off
+ self.assertEqual(1, versions_container.info()['object_count'])
+ versioned_obj_name = versions_container.files()[0]
+ self.assertEqual(
+ "aaaaa", versions_container.file(versioned_obj_name).read())
+
+ # if we overwrite it again, there are two versions
+ versioned_obj.write("ccccc")
+ self.assertEqual(2, versions_container.info()['object_count'])
+
+ # as we delete things, the old contents return
+ self.assertEqual("ccccc", versioned_obj.read())
+ versioned_obj.delete()
+ self.assertEqual("bbbbb", versioned_obj.read())
+ versioned_obj.delete()
+ self.assertEqual("aaaaa", versioned_obj.read())
+ versioned_obj.delete()
+ self.assertRaises(ResponseError, versioned_obj.read)
+
+
+class TestObjectVersioningUTF8(Base2, TestObjectVersioning):
+ set_up = False
+
+
+class TestTempurlEnv(object):
+ tempurl_enabled = None # tri-state: None initially, then True/False
+
+ @classmethod
+ def setUp(cls):
+ cls.conn = Connection(config)
+ cls.conn.authenticate()
+
+ if cls.tempurl_enabled is None:
+ cluster_info = cls.conn.cluster_info()
+ cls.tempurl_enabled = 'tempurl' in cluster_info
+ if not cls.tempurl_enabled:
+ return
+ cls.tempurl_methods = cluster_info['tempurl']['methods']
+
+ cls.tempurl_key = Utils.create_name()
+ cls.tempurl_key2 = Utils.create_name()
+
+ cls.account = Account(
+ cls.conn, config.get('account', config['username']))
+ cls.account.delete_containers()
+ cls.account.update_metadata({
+ 'temp-url-key': cls.tempurl_key,
+ 'temp-url-key-2': cls.tempurl_key2
+ })
+
+ cls.container = cls.account.container(Utils.create_name())
+ if not cls.container.create():
+ raise ResponseError(cls.conn.response)
+
+ cls.obj = cls.container.file(Utils.create_name())
+ cls.obj.write("obj contents")
+ cls.other_obj = cls.container.file(Utils.create_name())
+ cls.other_obj.write("other obj contents")
+
+
+class TestTempurl(Base):
+ env = TestTempurlEnv
+ set_up = False
+
+ def setUp(self):
+ super(TestTempurl, self).setUp()
+ if self.env.tempurl_enabled is False:
+ raise SkipTest("TempURL not enabled")
+ elif self.env.tempurl_enabled is not True:
+ # just some sanity checking
+ raise Exception(
+ "Expected tempurl_enabled to be True/False, got %r" %
+ (self.env.tempurl_enabled,))
+
+ expires = int(time.time()) + 86400
+ sig = self.tempurl_sig(
+ 'GET', expires, self.env.conn.make_path(self.env.obj.path),
+ self.env.tempurl_key)
+ self.obj_tempurl_parms = {'temp_url_sig': sig,
+ 'temp_url_expires': str(expires)}
+
+ def tempurl_sig(self, method, expires, path, key):
+ return hmac.new(
+ key,
+ '%s\n%s\n%s' % (method, expires, urllib.unquote(path)),
+ hashlib.sha1).hexdigest()
+
+ def test_GET(self):
+ contents = self.env.obj.read(
+ parms=self.obj_tempurl_parms,
+ cfg={'no_auth_token': True})
+ self.assertEqual(contents, "obj contents")
+
+ # GET tempurls also allow HEAD requests
+ self.assert_(self.env.obj.info(parms=self.obj_tempurl_parms,
+ cfg={'no_auth_token': True}))
+
+ def test_GET_with_key_2(self):
+ expires = int(time.time()) + 86400
+ sig = self.tempurl_sig(
+ 'GET', expires, self.env.conn.make_path(self.env.obj.path),
+ self.env.tempurl_key2)
+ parms = {'temp_url_sig': sig,
+ 'temp_url_expires': str(expires)}
+
+ contents = self.env.obj.read(parms=parms, cfg={'no_auth_token': True})
+ self.assertEqual(contents, "obj contents")
+
+ def test_PUT(self):
+ new_obj = self.env.container.file(Utils.create_name())
+
+ expires = int(time.time()) + 86400
+ sig = self.tempurl_sig(
+ 'PUT', expires, self.env.conn.make_path(new_obj.path),
+ self.env.tempurl_key)
+ put_parms = {'temp_url_sig': sig,
+ 'temp_url_expires': str(expires)}
+
+ new_obj.write('new obj contents',
+ parms=put_parms, cfg={'no_auth_token': True})
+ self.assertEqual(new_obj.read(), "new obj contents")
+
+ # PUT tempurls also allow HEAD requests
+ self.assert_(new_obj.info(parms=put_parms,
+ cfg={'no_auth_token': True}))
+
+ def test_HEAD(self):
+ expires = int(time.time()) + 86400
+ sig = self.tempurl_sig(
+ 'HEAD', expires, self.env.conn.make_path(self.env.obj.path),
+ self.env.tempurl_key)
+ head_parms = {'temp_url_sig': sig,
+ 'temp_url_expires': str(expires)}
+
+ self.assert_(self.env.obj.info(parms=head_parms,
+ cfg={'no_auth_token': True}))
+ # HEAD tempurls don't allow PUT or GET requests, despite the fact that
+ # PUT and GET tempurls both allow HEAD requests
+ self.assertRaises(ResponseError, self.env.other_obj.read,
+ cfg={'no_auth_token': True},
+ parms=self.obj_tempurl_parms)
+ self.assert_status([401])
+
+ self.assertRaises(ResponseError, self.env.other_obj.write,
+ 'new contents',
+ cfg={'no_auth_token': True},
+ parms=self.obj_tempurl_parms)
+ self.assert_status([401])
+
+ def test_different_object(self):
+ contents = self.env.obj.read(
+ parms=self.obj_tempurl_parms,
+ cfg={'no_auth_token': True})
+ self.assertEqual(contents, "obj contents")
+
+ self.assertRaises(ResponseError, self.env.other_obj.read,
+ cfg={'no_auth_token': True},
+ parms=self.obj_tempurl_parms)
+ self.assert_status([401])
+
+ def test_changing_sig(self):
+ contents = self.env.obj.read(
+ parms=self.obj_tempurl_parms,
+ cfg={'no_auth_token': True})
+ self.assertEqual(contents, "obj contents")
+
+ parms = self.obj_tempurl_parms.copy()
+ if parms['temp_url_sig'][0] == 'a':
+ parms['temp_url_sig'] = 'b' + parms['temp_url_sig'][1:]
+ else:
+ parms['temp_url_sig'] = 'a' + parms['temp_url_sig'][1:]
+
+ self.assertRaises(ResponseError, self.env.obj.read,
+ cfg={'no_auth_token': True},
+ parms=parms)
+ self.assert_status([401])
+
+ def test_changing_expires(self):
+ contents = self.env.obj.read(
+ parms=self.obj_tempurl_parms,
+ cfg={'no_auth_token': True})
+ self.assertEqual(contents, "obj contents")
+
+ parms = self.obj_tempurl_parms.copy()
+ if parms['temp_url_expires'][-1] == '0':
+ parms['temp_url_expires'] = parms['temp_url_expires'][:-1] + '1'
+ else:
+ parms['temp_url_expires'] = parms['temp_url_expires'][:-1] + '0'
+
+ self.assertRaises(ResponseError, self.env.obj.read,
+ cfg={'no_auth_token': True},
+ parms=parms)
+ self.assert_status([401])
+
+
+class TestTempurlUTF8(Base2, TestTempurl):
+ set_up = False
+
+
+class TestSloTempurlEnv(object):
+ enabled = None # tri-state: None initially, then True/False
+
+ @classmethod
+ def setUp(cls):
+ cls.conn = Connection(config)
+ cls.conn.authenticate()
+
+ if cls.enabled is None:
+ cluster_info = cls.conn.cluster_info()
+ cls.enabled = 'tempurl' in cluster_info and 'slo' in cluster_info
+
+ cls.tempurl_key = Utils.create_name()
+
+ cls.account = Account(
+ cls.conn, config.get('account', config['username']))
+ cls.account.delete_containers()
+ cls.account.update_metadata({'temp-url-key': cls.tempurl_key})
+
+ cls.manifest_container = cls.account.container(Utils.create_name())
+ cls.segments_container = cls.account.container(Utils.create_name())
+ if not cls.manifest_container.create():
+ raise ResponseError(cls.conn.response)
+ if not cls.segments_container.create():
+ raise ResponseError(cls.conn.response)
+
+ seg1 = cls.segments_container.file(Utils.create_name())
+ seg1.write('1' * 1024 * 1024)
+
+ seg2 = cls.segments_container.file(Utils.create_name())
+ seg2.write('2' * 1024 * 1024)
+
+ cls.manifest_data = [{'size_bytes': 1024 * 1024,
+ 'etag': seg1.md5,
+ 'path': '/%s/%s' % (cls.segments_container.name,
+ seg1.name)},
+ {'size_bytes': 1024 * 1024,
+ 'etag': seg2.md5,
+ 'path': '/%s/%s' % (cls.segments_container.name,
+ seg2.name)}]
+
+ cls.manifest = cls.manifest_container.file(Utils.create_name())
+ cls.manifest.write(
+ json.dumps(cls.manifest_data),
+ parms={'multipart-manifest': 'put'})
+
+
+class TestSloTempurl(Base):
+ env = TestSloTempurlEnv
+ set_up = False
+
+ def setUp(self):
+ super(TestSloTempurl, self).setUp()
+ if self.env.enabled is False:
+ raise SkipTest("TempURL and SLO not both enabled")
+ elif self.env.enabled is not True:
+ # just some sanity checking
+ raise Exception(
+ "Expected enabled to be True/False, got %r" %
+ (self.env.enabled,))
+
+ def tempurl_sig(self, method, expires, path, key):
+ return hmac.new(
+ key,
+ '%s\n%s\n%s' % (method, expires, urllib.unquote(path)),
+ hashlib.sha1).hexdigest()
+
+ def test_GET(self):
+ expires = int(time.time()) + 86400
+ sig = self.tempurl_sig(
+ 'GET', expires, self.env.conn.make_path(self.env.manifest.path),
+ self.env.tempurl_key)
+ parms = {'temp_url_sig': sig, 'temp_url_expires': str(expires)}
+
+ contents = self.env.manifest.read(
+ parms=parms,
+ cfg={'no_auth_token': True})
+ self.assertEqual(len(contents), 2 * 1024 * 1024)
+
+ # GET tempurls also allow HEAD requests
+ self.assert_(self.env.manifest.info(
+ parms=parms, cfg={'no_auth_token': True}))
+
+
+class TestSloTempurlUTF8(Base2, TestSloTempurl):
+ set_up = False
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/test/functional_auth/tempauth/conf/account-server.conf b/test/functional_auth/common_conf/account-server.conf
index 4996367..549a094 100644
--- a/test/functional_auth/tempauth/conf/account-server.conf
+++ b/test/functional_auth/common_conf/account-server.conf
@@ -30,3 +30,7 @@ log_level = WARN
# normal request logging for the account server to unclutter the log
# files. Warnings and errors will still be logged.
log_requests = off
+
+# The following parameter is used by object-expirer and needs to be same
+# across all conf files!
+auto_create_account_prefix = gs
diff --git a/test/functional_auth/gswauth/conf/container-server.conf b/test/functional_auth/common_conf/container-server.conf
index 122d97e..487ccb8 100644
--- a/test/functional_auth/gswauth/conf/container-server.conf
+++ b/test/functional_auth/common_conf/container-server.conf
@@ -33,3 +33,7 @@ log_requests = off
#enable object versioning for functional test
allow_versions = on
+
+# The following parameter is used by object-expirer and needs to be same
+# across all conf files!
+auto_create_account_prefix = gs
diff --git a/test/functional_auth/gswauth/conf/fs.conf b/test/functional_auth/common_conf/fs.conf
index b06a854..b06a854 100644
--- a/test/functional_auth/gswauth/conf/fs.conf
+++ b/test/functional_auth/common_conf/fs.conf
diff --git a/test/functional_auth/common_conf/object-expirer.conf b/test/functional_auth/common_conf/object-expirer.conf
new file mode 100644
index 0000000..4449ee2
--- /dev/null
+++ b/test/functional_auth/common_conf/object-expirer.conf
@@ -0,0 +1,27 @@
+#TODO: Add documentation to explain various options
+#For now, refer: https://github.com/openstack/swift/blob/master/etc/object-expirer.conf-sample
+
+[DEFAULT]
+
+[object-expirer]
+user = root
+log_facility = LOG_LOCAL2
+log_level = DEBUG
+# The following parameters are used by object-expirer and needs to be same
+# across all conf files!
+auto_create_account_prefix = gs
+expiring_objects_account_name = expiring
+
+interval = 30
+
+[pipeline:main]
+pipeline = catch_errors cache proxy-server
+
+[app:proxy-server]
+use = egg:gluster_swift#proxy
+
+[filter:cache]
+use = egg:swift#memcache
+
+[filter:catch_errors]
+use = egg:swift#catch_errors
diff --git a/test/functional_auth/gswauth/conf/object-server.conf b/test/functional_auth/common_conf/object-server.conf
index 3cb9ead..2599c87 100644
--- a/test/functional_auth/gswauth/conf/object-server.conf
+++ b/test/functional_auth/common_conf/object-server.conf
@@ -46,3 +46,8 @@ disk_chunk_size = 65536
# Adjust this value match whatever is set for the disk_chunk_size initially.
# This will provide a reasonable starting point for tuning this value.
network_chunk_size = 65556
+
+# The following parameter is used by object-expirer and needs to be same
+# across all conf files!
+auto_create_account_prefix = gs
+expiring_objects_account_name = expiring
diff --git a/test/functional_auth/gswauth/conf/swift.conf b/test/functional_auth/common_conf/swift.conf
index f64ba5a..f64ba5a 100644
--- a/test/functional_auth/gswauth/conf/swift.conf
+++ b/test/functional_auth/common_conf/swift.conf
diff --git a/test/functional_auth/swiftkerbauth/conf/test.conf b/test/functional_auth/common_conf/test.conf
index 643f2d1..15c9aea 100644
--- a/test/functional_auth/swiftkerbauth/conf/test.conf
+++ b/test/functional_auth/common_conf/test.conf
@@ -1,16 +1,25 @@
[func_test]
-# Swiftkerbauth configuration
+# sample config
auth_host = 127.0.0.1
auth_port = 8080
+auth_ssl = no
auth_prefix = /auth/
-auth_scheme = http://
-auth_mode = passive
-auth_version = 1
-domain_name = RHELBOX.COM
+## sample config for Swift with Keystone
+#auth_version = 2
+#auth_host = localhost
+#auth_port = 5000
+#auth_ssl = no
+#auth_prefix = /v2.0/
+
+# GSWauth internal admin user configuration information
+admin_key = gswauthkey
+admin_user = .super_admin
+
+# Gluster setup information
+devices = /mnt/gluster-object
+gsmetadata_volume = gsmetadata
-#All the accounts, users & passwords to be prepared on kerberos server.
# Primary functional test account (needs admin access to the account)
-# Note: Account name to be prepared on kerberos server 'AUTH_accoun'
account = test
username = tester
password = testing
diff --git a/test/functional_auth/gswauth/conf/account-server.conf b/test/functional_auth/gswauth/conf/account-server.conf
deleted file mode 100644
index 4996367..0000000
--- a/test/functional_auth/gswauth/conf/account-server.conf
+++ /dev/null
@@ -1,32 +0,0 @@
-[DEFAULT]
-devices = /mnt/gluster-object
-#
-# Once you are confident that your startup processes will always have your
-# gluster volumes properly mounted *before* the account-server workers start,
-# you can *consider* setting this value to "false" to reduce the per-request
-# overhead it can incur.
-#
-# *** Keep false for Functional Tests ***
-mount_check = false
-bind_port = 6012
-#
-# Override swift's default behaviour for fallocate.
-disable_fallocate = true
-#
-# One or two workers should be sufficient for almost any installation of
-# Gluster.
-workers = 1
-
-[pipeline:main]
-pipeline = account-server
-
-[app:account-server]
-use = egg:gluster_swift#account
-user = root
-log_facility = LOG_LOCAL2
-log_level = WARN
-#
-# After ensuring things are running in a stable manner, you can turn off
-# normal request logging for the account server to unclutter the log
-# files. Warnings and errors will still be logged.
-log_requests = off
diff --git a/test/functional_auth/gswauth/conf/object-expirer.conf b/test/functional_auth/gswauth/conf/object-expirer.conf
deleted file mode 100644
index b75963c..0000000
--- a/test/functional_auth/gswauth/conf/object-expirer.conf
+++ /dev/null
@@ -1,17 +0,0 @@
-[DEFAULT]
-
-[object-expirer]
-# auto_create_account_prefix = .
-
-[pipeline:main]
-pipeline = catch_errors cache proxy-server
-
-[app:proxy-server]
-use = egg:swift#proxy
-
-[filter:cache]
-use = egg:swift#memcache
-memcache_servers = 127.0.0.1:11211
-
-[filter:catch_errors]
-use = egg:swift#catch_errors
diff --git a/test/functional_auth/gswauth/conf/proxy-server.conf b/test/functional_auth/gswauth/conf/proxy-server.conf
index 165cb0c..0a3c1f6 100644
--- a/test/functional_auth/gswauth/conf/proxy-server.conf
+++ b/test/functional_auth/gswauth/conf/proxy-server.conf
@@ -5,7 +5,7 @@ user = root
workers = 1
[pipeline:main]
-pipeline = catch_errors healthcheck proxy-logging cache gswauth proxy-logging proxy-server
+pipeline = catch_errors healthcheck proxy-logging cache tempurl gswauth proxy-logging proxy-server
[app:proxy-server]
use = egg:gluster_swift#proxy
@@ -48,6 +48,10 @@ object_chunk_size = 65536
# amount of memory available on the system can accommodate increased values
# for object_chunk_size.
put_queue_depth = 10
+# The following parameter is used by object-expirer and needs to be same
+# across all conf files!
+auto_create_account_prefix = gs
+expiring_objects_account_name = expiring
[filter:catch_errors]
use = egg:swift#catch_errors
@@ -58,21 +62,17 @@ use = egg:swift#proxy_logging
[filter:healthcheck]
use = egg:swift#healthcheck
-[filter:tempauth]
-use = egg:swift#tempauth
-user_admin_admin = admin .admin .reseller_admin
-user_test_tester = testing .admin
-user_test2_tester2 = testing2 .admin
-user_test_tester3 = testing3
+[filter:cache]
+use = egg:swift#memcache
+# Update this line to contain a comma separated list of memcache servers
+# shared by all nodes running the proxy-server service.
+memcache_servers = localhost:11211
+
+[filter:tempurl]
+use = egg:swift#tempurl
[filter:gswauth]
use = egg:gluster_swift#gswauth
set log_name = gswauth
super_admin_key = gswauthkey
metadata_volume = gsmetadata
-
-[filter:cache]
-use = egg:swift#memcache
-# Update this line to contain a comma separated list of memcache servers
-# shared by all nodes running the proxy-server service.
-memcache_servers = localhost:11211
diff --git a/test/functional_auth/keystone/conf/account-server.conf b/test/functional_auth/keystone/conf/account-server.conf
deleted file mode 100644
index 4996367..0000000
--- a/test/functional_auth/keystone/conf/account-server.conf
+++ /dev/null
@@ -1,32 +0,0 @@
-[DEFAULT]
-devices = /mnt/gluster-object
-#
-# Once you are confident that your startup processes will always have your
-# gluster volumes properly mounted *before* the account-server workers start,
-# you can *consider* setting this value to "false" to reduce the per-request
-# overhead it can incur.
-#
-# *** Keep false for Functional Tests ***
-mount_check = false
-bind_port = 6012
-#
-# Override swift's default behaviour for fallocate.
-disable_fallocate = true
-#
-# One or two workers should be sufficient for almost any installation of
-# Gluster.
-workers = 1
-
-[pipeline:main]
-pipeline = account-server
-
-[app:account-server]
-use = egg:gluster_swift#account
-user = root
-log_facility = LOG_LOCAL2
-log_level = WARN
-#
-# After ensuring things are running in a stable manner, you can turn off
-# normal request logging for the account server to unclutter the log
-# files. Warnings and errors will still be logged.
-log_requests = off
diff --git a/test/functional_auth/keystone/conf/container-server.conf b/test/functional_auth/keystone/conf/container-server.conf
deleted file mode 100644
index 122d97e..0000000
--- a/test/functional_auth/keystone/conf/container-server.conf
+++ /dev/null
@@ -1,35 +0,0 @@
-[DEFAULT]
-devices = /mnt/gluster-object
-#
-# Once you are confident that your startup processes will always have your
-# gluster volumes properly mounted *before* the container-server workers
-# start, you can *consider* setting this value to "false" to reduce the
-# per-request overhead it can incur.
-#
-# *** Keep false for Functional Tests ***
-mount_check = false
-bind_port = 6011
-#
-# Override swift's default behaviour for fallocate.
-disable_fallocate = true
-#
-# One or two workers should be sufficient for almost any installation of
-# Gluster.
-workers = 1
-
-[pipeline:main]
-pipeline = container-server
-
-[app:container-server]
-use = egg:gluster_swift#container
-user = root
-log_facility = LOG_LOCAL2
-log_level = WARN
-#
-# After ensuring things are running in a stable manner, you can turn off
-# normal request logging for the container server to unclutter the log
-# files. Warnings and errors will still be logged.
-log_requests = off
-
-#enable object versioning for functional test
-allow_versions = on
diff --git a/test/functional_auth/keystone/conf/fs.conf b/test/functional_auth/keystone/conf/fs.conf
deleted file mode 100644
index b06a854..0000000
--- a/test/functional_auth/keystone/conf/fs.conf
+++ /dev/null
@@ -1,19 +0,0 @@
-[DEFAULT]
-#
-# IP address of a node in the GlusterFS server cluster hosting the
-# volumes to be served via Swift API.
-mount_ip = localhost
-
-# Performance optimization parameter. When turned off, the filesystem will
-# see a reduced number of stat calls, resulting in substantially faster
-# response time for GET and HEAD container requests on containers with large
-# numbers of objects, at the expense of an accurate count of combined bytes
-# used by all objects in the container. For most installations "off" works
-# fine.
-#
-# *** Keep on for Functional Tests ***
-accurate_size_in_listing = on
-
-# *** Keep on for Functional Tests ***
-container_update_object_count = on
-account_update_container_count = on
diff --git a/test/functional_auth/keystone/conf/object-server.conf b/test/functional_auth/keystone/conf/object-server.conf
deleted file mode 100644
index 3cb9ead..0000000
--- a/test/functional_auth/keystone/conf/object-server.conf
+++ /dev/null
@@ -1,48 +0,0 @@
-[DEFAULT]
-devices = /mnt/gluster-object
-#
-# Once you are confident that your startup processes will always have your
-# gluster volumes properly mounted *before* the object-server workers start,
-# you can *consider* setting this value to "false" to reduce the per-request
-# overhead it can incur.
-#
-# *** Keep false for Functional Tests ***
-mount_check = false
-bind_port = 6010
-#
-# Maximum number of clients one worker can process simultaneously (it will
-# actually accept N + 1). Setting this to one (1) will only handle one request
-# at a time, without accepting another request concurrently. By increasing the
-# number of workers to a much higher value, one can prevent slow file system
-# operations for one request from starving other requests.
-max_clients = 1024
-#
-# If not doing the above, setting this value initially to match the number of
-# CPUs is a good starting point for determining the right value.
-workers = 1
-# Override swift's default behaviour for fallocate.
-disable_fallocate = true
-
-[pipeline:main]
-pipeline = object-server
-
-[app:object-server]
-use = egg:gluster_swift#object
-user = root
-log_facility = LOG_LOCAL2
-log_level = WARN
-#
-# For performance, after ensuring things are running in a stable manner, you
-# can turn off normal request logging for the object server to reduce the
-# per-request overhead and unclutter the log files. Warnings and errors will
-# still be logged.
-log_requests = off
-#
-# Adjust this value to match the stripe width of the underlying storage array
-# (not the stripe element size). This will provide a reasonable starting point
-# for tuning this value.
-disk_chunk_size = 65536
-#
-# Adjust this value match whatever is set for the disk_chunk_size initially.
-# This will provide a reasonable starting point for tuning this value.
-network_chunk_size = 65556
diff --git a/test/functional_auth/keystone/conf/proxy-server.conf b/test/functional_auth/keystone/conf/proxy-server.conf
index 084e6a5..72a84da 100644
--- a/test/functional_auth/keystone/conf/proxy-server.conf
+++ b/test/functional_auth/keystone/conf/proxy-server.conf
@@ -6,7 +6,7 @@ workers = 1
[pipeline:main]
#pipeline = catch_errors healthcheck proxy-logging cache tempauth proxy-logging proxy-server
-pipeline = catch_errors healthcheck proxy-logging cache authtoken keystoneauth proxy-logging proxy-server
+pipeline = catch_errors healthcheck proxy-logging cache tempurl authtoken keystoneauth proxy-logging proxy-server
[app:proxy-server]
use = egg:gluster_swift#proxy
@@ -49,6 +49,10 @@ object_chunk_size = 65536
# amount of memory available on the system can accommodate increased values
# for object_chunk_size.
put_queue_depth = 10
+# The following parameter is used by object-expirer and needs to be same
+# across all conf files!
+auto_create_account_prefix = gs
+expiring_objects_account_name = expiring
[filter:catch_errors]
use = egg:swift#catch_errors
@@ -59,13 +63,14 @@ use = egg:swift#proxy_logging
[filter:healthcheck]
use = egg:swift#healthcheck
-[filter:tempauth]
-use = egg:swift#tempauth
-user_admin_admin = admin .admin .reseller_admin
-user_d4dde08c621a4f0fb4cde0ac6a62aa0c_tester = testing .admin
-user_test_tester = testing .admin
-user_test2_tester2 = testing2 .admin
-user_test_tester3 = testing3
+[filter:cache]
+use = egg:swift#memcache
+# Update this line to contain a comma separated list of memcache servers
+# shared by all nodes running the proxy-server service.
+memcache_servers = localhost:11211
+
+[filter:tempurl]
+use = egg:swift#tempurl
[filter:authtoken]
paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory
@@ -87,8 +92,4 @@ operator_roles = admin
is_admin = true
cache = swift.cache
-[filter:cache]
-use = egg:swift#memcache
-# Update this line to contain a comma separated list of memcache servers
-# shared by all nodes running the proxy-server service.
-memcache_servers = localhost:11211
+
diff --git a/test/functional_auth/keystone/conf/swift.conf b/test/functional_auth/keystone/conf/swift.conf
deleted file mode 100644
index ce9a4d0..0000000
--- a/test/functional_auth/keystone/conf/swift.conf
+++ /dev/null
@@ -1,85 +0,0 @@
-[DEFAULT]
-
-
-[swift-hash]
-# random unique string that can never change (DO NOT LOSE)
-swift_hash_path_suffix = gluster
-
-
-# The swift-constraints section sets the basic constraints on data
-# saved in the swift cluster.
-
-[swift-constraints]
-
-# max_file_size is the largest "normal" object that can be saved in
-# the cluster. This is also the limit on the size of each segment of
-# a "large" object when using the large object manifest support.
-# This value is set in bytes. Setting it to lower than 1MiB will cause
-# some tests to fail.
-# Default is 1 TiB = 2**30*1024
-max_file_size = 1099511627776
-
-
-# max_meta_name_length is the max number of bytes in the utf8 encoding
-# of the name portion of a metadata header.
-
-#max_meta_name_length = 128
-
-
-# max_meta_value_length is the max number of bytes in the utf8 encoding
-# of a metadata value
-
-#max_meta_value_length = 256
-
-
-# max_meta_count is the max number of metadata keys that can be stored
-# on a single account, container, or object
-
-#max_meta_count = 90
-
-
-# max_meta_overall_size is the max number of bytes in the utf8 encoding
-# of the metadata (keys + values)
-
-#max_meta_overall_size = 4096
-
-
-# max_object_name_length is the max number of bytes in the utf8 encoding of an
-# object name: Gluster FS can handle much longer file names, but the length
-# between the slashes of the URL is handled below. Remember that most web
-# clients can't handle anything greater than 2048, and those that do are
-# rather clumsy.
-
-max_object_name_length = 2048
-
-# max_object_name_component_length (GlusterFS) is the max number of bytes in
-# the utf8 encoding of an object name component (the part between the
-# slashes); this is a limit imposed by the underlying file system (for XFS it
-# is 255 bytes).
-
-max_object_name_component_length = 255
-
-# container_listing_limit is the default (and max) number of items
-# returned for a container listing request
-
-#container_listing_limit = 10000
-
-
-# account_listing_limit is the default (and max) number of items returned
-# for an account listing request
-
-#account_listing_limit = 10000
-
-
-# max_account_name_length is the max number of bytes in the utf8 encoding of
-# an account name: Gluster FS Filename limit (XFS limit?), must be the same
-# size as max_object_name_component_length above.
-
-max_account_name_length = 255
-
-
-# max_container_name_length is the max number of bytes in the utf8 encoding
-# of a container name: Gluster FS Filename limit (XFS limit?), must be the same
-# size as max_object_name_component_length above.
-
-max_container_name_length = 255
diff --git a/test/functional_auth/swiftkerbauth/__init__.py b/test/functional_auth/swiftkerbauth/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/test/functional_auth/swiftkerbauth/__init__.py
+++ /dev/null
diff --git a/test/functional_auth/swiftkerbauth/conf/account-server.conf b/test/functional_auth/swiftkerbauth/conf/account-server.conf
deleted file mode 100644
index 9ad458a..0000000
--- a/test/functional_auth/swiftkerbauth/conf/account-server.conf
+++ /dev/null
@@ -1,36 +0,0 @@
-[DEFAULT]
-#
-# Default gluster mount point to be used for object store,can be changed by
-# setting the following value in {account,container,object}-server.conf files.
-# It is recommended to keep this value same for all the three services but can
-# be kept different if environment demands.
-devices = /mnt/gluster-object
-#
-# Once you are confident that your startup processes will always have your
-# gluster volumes properly mounted *before* the account-server workers start,
-# you can *consider* setting this value to "false" to reduce the per-request
-# overhead it can incur.
-mount_check = true
-bind_port = 6012
-#
-# Override swift's default behaviour for fallocate.
-disable_fallocate = true
-#
-# One or two workers should be sufficient for almost any installation of
-# Gluster.
-workers = 1
-
-[pipeline:main]
-pipeline = account-server
-
-[app:account-server]
-use = egg:gluster_swift#account
-user = root
-log_facility = LOG_LOCAL2
-log_level = WARN
-#
-# After ensuring things are running in a stable manner, you can turn off
-# normal request logging for the account server to unclutter the log
-# files. Warnings and errors will still be logged.
-log_requests = off
-
diff --git a/test/functional_auth/swiftkerbauth/conf/container-server.conf b/test/functional_auth/swiftkerbauth/conf/container-server.conf
deleted file mode 100644
index a406b4d..0000000
--- a/test/functional_auth/swiftkerbauth/conf/container-server.conf
+++ /dev/null
@@ -1,36 +0,0 @@
-[DEFAULT]
-#
-# Default gluster mount point to be used for object store,can be changed by
-# setting the following value in {account,container,object}-server.conf files.
-# It is recommended to keep this value same for all the three services but can
-# be kept different if environment demands.
-devices = /mnt/gluster-object
-#
-# Once you are confident that your startup processes will always have your
-# gluster volumes properly mounted *before* the container-server workers
-# start, you can *consider* setting this value to "false" to reduce the
-# per-request overhead it can incur.
-mount_check = true
-bind_port = 6011
-#
-# Override swift's default behaviour for fallocate.
-disable_fallocate = true
-#
-# One or two workers should be sufficient for almost any installation of
-# Gluster.
-workers = 1
-
-[pipeline:main]
-pipeline = container-server
-
-[app:container-server]
-use = egg:gluster_swift#container
-user = root
-log_facility = LOG_LOCAL2
-log_level = WARN
-#
-# After ensuring things are running in a stable manner, you can turn off
-# normal request logging for the container server to unclutter the log
-# files. Warnings and errors will still be logged.
-log_requests = off
-
diff --git a/test/functional_auth/swiftkerbauth/conf/fs.conf b/test/functional_auth/swiftkerbauth/conf/fs.conf
deleted file mode 100644
index 6d2a791..0000000
--- a/test/functional_auth/swiftkerbauth/conf/fs.conf
+++ /dev/null
@@ -1,13 +0,0 @@
-[DEFAULT]
-#
-# IP address of a node in the GlusterFS server cluster hosting the
-# volumes to be served via Swift API.
-mount_ip = localhost
-
-# Performance optimization parameter. When turned off, the filesystem will
-# see a reduced number of stat calls, resulting in substantially faster
-# response time for GET and HEAD container requests on containers with large
-# numbers of objects, at the expense of an accurate count of combined bytes
-# used by all objects in the container. For most installations "off" works
-# fine.
-accurate_size_in_listing = off \ No newline at end of file
diff --git a/test/functional_auth/swiftkerbauth/conf/object-server.conf b/test/functional_auth/swiftkerbauth/conf/object-server.conf
deleted file mode 100644
index d10d282..0000000
--- a/test/functional_auth/swiftkerbauth/conf/object-server.conf
+++ /dev/null
@@ -1,51 +0,0 @@
-[DEFAULT]
-#
-# Default gluster mount point to be used for object store,can be changed by
-# setting the following value in {account,container,object}-server.conf files.
-# It is recommended to keep this value same for all the three services but can
-# be kept different if environment demands.
-devices = /mnt/gluster-object
-#
-# Once you are confident that your startup processes will always have your
-# gluster volumes properly mounted *before* the object-server workers start,
-# you can *consider* setting this value to "false" to reduce the per-request
-# overhead it can incur.
-mount_check = true
-bind_port = 6010
-#
-# Maximum number of clients one worker can process simultaneously (it will
-# actually accept N + 1). Setting this to one (1) will only handle one request
-# at a time, without accepting another request concurrently. By increasing the
-# number of workers to a much higher value, one can prevent slow file system
-# operations for one request from starving other requests.
-max_clients = 1024
-#
-# If not doing the above, setting this value initially to match the number of
-# CPUs is a good starting point for determining the right value.
-workers = 1
-# Override swift's default behaviour for fallocate.
-disable_fallocate = true
-
-[pipeline:main]
-pipeline = object-server
-
-[app:object-server]
-use = egg:gluster_swift#object
-user = root
-log_facility = LOG_LOCAL2
-log_level = WARN
-#
-# For performance, after ensuring things are running in a stable manner, you
-# can turn off normal request logging for the object server to reduce the
-# per-request overhead and unclutter the log files. Warnings and errors will
-# still be logged.
-log_requests = off
-#
-# Adjust this value to match the stripe width of the underlying storage array
-# (not the stripe element size). This will provide a reasonable starting point
-# for tuning this value.
-disk_chunk_size = 65536
-#
-# Adjust this value match whatever is set for the disk_chunk_size initially.
-# This will provide a reasonable starting point for tuning this value.
-network_chunk_size = 65536
diff --git a/test/functional_auth/swiftkerbauth/conf/proxy-server.conf b/test/functional_auth/swiftkerbauth/conf/proxy-server.conf
index e6f8aa1..248f87c 100644
--- a/test/functional_auth/swiftkerbauth/conf/proxy-server.conf
+++ b/test/functional_auth/swiftkerbauth/conf/proxy-server.conf
@@ -5,7 +5,7 @@ user = root
workers = 1
[pipeline:main]
-pipeline = catch_errors healthcheck proxy-logging cache proxy-logging kerbauth proxy-server
+pipeline = catch_errors healthcheck proxy-logging cache tempurl proxy-logging kerbauth proxy-server
[app:proxy-server]
use = egg:gluster_swift#proxy
@@ -48,6 +48,10 @@ object_chunk_size = 65536
# amount of memory available on the system can accommodate increased values
# for object_chunk_size.
put_queue_depth = 10
+# The following parameter is used by object-expirer and needs to be same
+# across all conf files!
+auto_create_account_prefix = gs
+expiring_objects_account_name = expiring
[filter:catch_errors]
use = egg:swift#catch_errors
@@ -59,12 +63,15 @@ access_log_level = WARN
[filter:healthcheck]
use = egg:swift#healthcheck
-[filter:kerbauth]
-use = egg:gluster_swift#kerbauth
-ext_authentication_url = http://client.rhelbox.com/cgi-bin/swift-auth
-
[filter:cache]
use = egg:swift#memcache
# Update this line to contain a comma separated list of memcache servers
# shared by all nodes running the proxy-server service.
memcache_servers = localhost:11211
+
+[filter:tempurl]
+use = egg:swift#tempurl
+
+[filter:kerbauth]
+use = egg:gluster_swift#kerbauth
+ext_authentication_url = http://client.rhelbox.com/cgi-bin/swift-auth
diff --git a/test/functional_auth/swiftkerbauth/conf/swift.conf b/test/functional_auth/swiftkerbauth/conf/swift.conf
deleted file mode 100644
index 0d25209..0000000
--- a/test/functional_auth/swiftkerbauth/conf/swift.conf
+++ /dev/null
@@ -1,84 +0,0 @@
-[DEFAULT]
-
-
-[swift-hash]
-# random unique string that can never change (DO NOT LOSE)
-swift_hash_path_suffix = gluster
-
-
-# The swift-constraints section sets the basic constraints on data
-# saved in the swift cluster.
-
-[swift-constraints]
-# max_file_size is the largest "normal" object that can be saved in
-# the cluster. This is also the limit on the size of each segment of
-# a "large" object when using the large object manifest support.
-# This value is set in bytes. Setting it to lower than 1MiB will cause
-# some tests to fail.
-# Default is 1 TiB = 2**30*1024
-
-max_file_size = 1099511627776
-
-# max_meta_name_length is the max number of bytes in the utf8 encoding
-# of the name portion of a metadata header.
-
-#max_meta_name_length = 128
-
-
-# max_meta_value_length is the max number of bytes in the utf8 encoding
-# of a metadata value
-
-#max_meta_value_length = 256
-
-
-# max_meta_count is the max number of metadata keys that can be stored
-# on a single account, container, or object
-
-#max_meta_count = 90
-
-
-# max_meta_overall_size is the max number of bytes in the utf8 encoding
-# of the metadata (keys + values)
-
-#max_meta_overall_size = 4096
-
-
-# max_object_name_length is the max number of bytes in the utf8 encoding of an
-# object name: Gluster FS can handle much longer file names, but the length
-# between the slashes of the URL is handled below. Remember that most web
-# clients can't handle anything greater than 2048, and those that do are
-# rather clumsy.
-
-max_object_name_length = 2048
-
-# max_object_name_component_length (GlusterFS) is the max number of bytes in
-# the utf8 encoding of an object name component (the part between the
-# slashes); this is a limit imposed by the underlying file system (for XFS it
-# is 255 bytes).
-
-max_object_name_component_length = 255
-
-# container_listing_limit is the default (and max) number of items
-# returned for a container listing request
-
-#container_listing_limit = 10000
-
-
-# account_listing_limit is the default (and max) number of items returned
-# for an account listing request
-
-#account_listing_limit = 10000
-
-
-# max_account_name_length is the max number of bytes in the utf8 encoding of
-# an account name: Gluster FS Filename limit (XFS limit?), must be the same
-# size as max_object_name_component_length above.
-
-max_account_name_length = 255
-
-
-# max_container_name_length is the max number of bytes in the utf8 encoding
-# of a container name: Gluster FS Filename limit (XFS limit?), must be the same
-# size as max_object_name_component_length above.
-
-max_container_name_length = 255
diff --git a/test/functional_auth/swiftkerbauth/test_swkrbath_active.py b/test/functional_auth/swiftkerbauth/test_swkrbath_active.py
deleted file mode 100644
index 86c79ef..0000000
--- a/test/functional_auth/swiftkerbauth/test_swkrbath_active.py
+++ /dev/null
@@ -1,93 +0,0 @@
-#!/usr/bin/python
-
-# Copyright (c) 2010-2014 OpenStack Foundation
-#
-# 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.
-
-import re
-import unittest
-from nose import SkipTest
-import commands
-import os
-from test import get_config
-from swift.common.bufferedhttp import http_connect_raw as http_connect
-
-config = get_config('func_test')
-
-class Utils:
- @classmethod
- def SwiftKerbAuthPrep(self,
- user=config['username'],domain=config['domain_name'],\
- passwd=config['password']):
- username = '%s@%s' % (user, domain)
- return commands.getstatusoutput('kinit %s <<< %s' % (username, passwd))
-
- @classmethod
- def SwiftKerbAuthCleanAll(self):
- return commands.getstatusoutput('kdestroy')
-
-
-class TestSwKrbAthActive(unittest.TestCase):
- def setUp(self):
- #Perform kinit in active mode.
- (status, output) = Utils.SwiftKerbAuthPrep()
- self.assertEqual(status, 0, \
- 'swkrbauth prep failed with valid credentials'+output)
- self.auth_host = config['auth_host']
- self.auth_port = int(config['auth_port'])
- self.auth_prefix = config.get('auth_prefix', '/auth/')
- self.auth_version = str(config.get('auth_version', '1'))
- self.account_name = config['account']
- self.username = config['username']
- self.password = config['password']
- self.auth_scheme = config['auth_scheme']
-
- #Prepare auth_url. e.g. http://client.rhelbox.com:8080/auth/v1.0
- if self.auth_version == "1":
- self.auth_path = '%sv1.0' % (self.auth_prefix)
- else:
- self.auth_path = self.auth_prefix
- self.auth_netloc = "%s:%d" % (self.auth_host, self.auth_port)
- auth_url = self.auth_scheme + self.auth_netloc + self.auth_path
-
- #Obtain the X-Auth-Token from kerberos server to use it in furhter
- #testing
- self.auth_token = None
- (status, output) = commands.getstatusoutput('curl -v -u : --negotiate\
- --location-trusted %s' % (auth_url))
- self.assertEqual(status, 0, 'Token negotiation failed:' +output)
- match = re.search('X-Auth-Token: AUTH.*', output)
- if match:
- self.auth_token = match.group(0).split(':')[1].strip()
- else:
- self.fail('No X-Auth-Token found, failed')
-
- def tearDown(self):
- Utils.SwiftKerbAuthCleanAll()
-
-
- def _get_auth_token(self):
- return {'X-Auth-Token' : self.auth_token}
-
- def testGetAccounts(self):
- #TODO: The test case is to perform GET on the account mentioned via
- #configuration file. This is a sample test case. The whole test
- #suite can be enhanced further to have further complicated test cases.
- path = '/v1/AUTH_%s' % (config['account'])
-
- headers = self._get_auth_token()
- conn = http_connect(config['auth_host'], config['auth_port'], 'GET',
- path, headers)
- resp = conn.getresponse()
- self.assertTrue(resp.status == 204)
diff --git a/test/functional_auth/tempauth/conf/container-server.conf b/test/functional_auth/tempauth/conf/container-server.conf
deleted file mode 100644
index 122d97e..0000000
--- a/test/functional_auth/tempauth/conf/container-server.conf
+++ /dev/null
@@ -1,35 +0,0 @@
-[DEFAULT]
-devices = /mnt/gluster-object
-#
-# Once you are confident that your startup processes will always have your
-# gluster volumes properly mounted *before* the container-server workers
-# start, you can *consider* setting this value to "false" to reduce the
-# per-request overhead it can incur.
-#
-# *** Keep false for Functional Tests ***
-mount_check = false
-bind_port = 6011
-#
-# Override swift's default behaviour for fallocate.
-disable_fallocate = true
-#
-# One or two workers should be sufficient for almost any installation of
-# Gluster.
-workers = 1
-
-[pipeline:main]
-pipeline = container-server
-
-[app:container-server]
-use = egg:gluster_swift#container
-user = root
-log_facility = LOG_LOCAL2
-log_level = WARN
-#
-# After ensuring things are running in a stable manner, you can turn off
-# normal request logging for the container server to unclutter the log
-# files. Warnings and errors will still be logged.
-log_requests = off
-
-#enable object versioning for functional test
-allow_versions = on
diff --git a/test/functional_auth/tempauth/conf/fs.conf b/test/functional_auth/tempauth/conf/fs.conf
deleted file mode 100644
index b06a854..0000000
--- a/test/functional_auth/tempauth/conf/fs.conf
+++ /dev/null
@@ -1,19 +0,0 @@
-[DEFAULT]
-#
-# IP address of a node in the GlusterFS server cluster hosting the
-# volumes to be served via Swift API.
-mount_ip = localhost
-
-# Performance optimization parameter. When turned off, the filesystem will
-# see a reduced number of stat calls, resulting in substantially faster
-# response time for GET and HEAD container requests on containers with large
-# numbers of objects, at the expense of an accurate count of combined bytes
-# used by all objects in the container. For most installations "off" works
-# fine.
-#
-# *** Keep on for Functional Tests ***
-accurate_size_in_listing = on
-
-# *** Keep on for Functional Tests ***
-container_update_object_count = on
-account_update_container_count = on
diff --git a/test/functional_auth/tempauth/conf/object-expirer.conf b/test/functional_auth/tempauth/conf/object-expirer.conf
deleted file mode 100644
index b75963c..0000000
--- a/test/functional_auth/tempauth/conf/object-expirer.conf
+++ /dev/null
@@ -1,17 +0,0 @@
-[DEFAULT]
-
-[object-expirer]
-# auto_create_account_prefix = .
-
-[pipeline:main]
-pipeline = catch_errors cache proxy-server
-
-[app:proxy-server]
-use = egg:swift#proxy
-
-[filter:cache]
-use = egg:swift#memcache
-memcache_servers = 127.0.0.1:11211
-
-[filter:catch_errors]
-use = egg:swift#catch_errors
diff --git a/test/functional_auth/tempauth/conf/object-server.conf b/test/functional_auth/tempauth/conf/object-server.conf
deleted file mode 100644
index 3cb9ead..0000000
--- a/test/functional_auth/tempauth/conf/object-server.conf
+++ /dev/null
@@ -1,48 +0,0 @@
-[DEFAULT]
-devices = /mnt/gluster-object
-#
-# Once you are confident that your startup processes will always have your
-# gluster volumes properly mounted *before* the object-server workers start,
-# you can *consider* setting this value to "false" to reduce the per-request
-# overhead it can incur.
-#
-# *** Keep false for Functional Tests ***
-mount_check = false
-bind_port = 6010
-#
-# Maximum number of clients one worker can process simultaneously (it will
-# actually accept N + 1). Setting this to one (1) will only handle one request
-# at a time, without accepting another request concurrently. By increasing the
-# number of workers to a much higher value, one can prevent slow file system
-# operations for one request from starving other requests.
-max_clients = 1024
-#
-# If not doing the above, setting this value initially to match the number of
-# CPUs is a good starting point for determining the right value.
-workers = 1
-# Override swift's default behaviour for fallocate.
-disable_fallocate = true
-
-[pipeline:main]
-pipeline = object-server
-
-[app:object-server]
-use = egg:gluster_swift#object
-user = root
-log_facility = LOG_LOCAL2
-log_level = WARN
-#
-# For performance, after ensuring things are running in a stable manner, you
-# can turn off normal request logging for the object server to reduce the
-# per-request overhead and unclutter the log files. Warnings and errors will
-# still be logged.
-log_requests = off
-#
-# Adjust this value to match the stripe width of the underlying storage array
-# (not the stripe element size). This will provide a reasonable starting point
-# for tuning this value.
-disk_chunk_size = 65536
-#
-# Adjust this value match whatever is set for the disk_chunk_size initially.
-# This will provide a reasonable starting point for tuning this value.
-network_chunk_size = 65556
diff --git a/test/functional_auth/tempauth/conf/proxy-server.conf b/test/functional_auth/tempauth/conf/proxy-server.conf
index 830fadf..554ce61 100644
--- a/test/functional_auth/tempauth/conf/proxy-server.conf
+++ b/test/functional_auth/tempauth/conf/proxy-server.conf
@@ -5,7 +5,7 @@ user = root
workers = 1
[pipeline:main]
-pipeline = catch_errors healthcheck proxy-logging cache tempauth proxy-logging proxy-server
+pipeline = catch_errors healthcheck proxy-logging cache tempurl tempauth proxy-logging proxy-server
[app:proxy-server]
use = egg:gluster_swift#proxy
@@ -48,6 +48,10 @@ object_chunk_size = 65536
# amount of memory available on the system can accommodate increased values
# for object_chunk_size.
put_queue_depth = 10
+# The following parameter is used by object-expirer and needs to be same
+# across all conf files!
+auto_create_account_prefix = gs
+expiring_objects_account_name = expiring
[filter:catch_errors]
use = egg:swift#catch_errors
@@ -58,15 +62,18 @@ use = egg:swift#proxy_logging
[filter:healthcheck]
use = egg:swift#healthcheck
+[filter:cache]
+use = egg:swift#memcache
+# Update this line to contain a comma separated list of memcache servers
+# shared by all nodes running the proxy-server service.
+memcache_servers = localhost:11211
+
+[filter:tempurl]
+use = egg:swift#tempurl
+
[filter:tempauth]
use = egg:swift#tempauth
user_admin_admin = admin .admin .reseller_admin
user_test_tester = testing .admin
user_test2_tester2 = testing2 .admin
user_test_tester3 = testing3
-
-[filter:cache]
-use = egg:swift#memcache
-# Update this line to contain a comma separated list of memcache servers
-# shared by all nodes running the proxy-server service.
-memcache_servers = localhost:11211
diff --git a/test/functional_auth/tempauth/conf/swift.conf b/test/functional_auth/tempauth/conf/swift.conf
deleted file mode 100644
index ce9a4d0..0000000
--- a/test/functional_auth/tempauth/conf/swift.conf
+++ /dev/null
@@ -1,85 +0,0 @@
-[DEFAULT]
-
-
-[swift-hash]
-# random unique string that can never change (DO NOT LOSE)
-swift_hash_path_suffix = gluster
-
-
-# The swift-constraints section sets the basic constraints on data
-# saved in the swift cluster.
-
-[swift-constraints]
-
-# max_file_size is the largest "normal" object that can be saved in
-# the cluster. This is also the limit on the size of each segment of
-# a "large" object when using the large object manifest support.
-# This value is set in bytes. Setting it to lower than 1MiB will cause
-# some tests to fail.
-# Default is 1 TiB = 2**30*1024
-max_file_size = 1099511627776
-
-
-# max_meta_name_length is the max number of bytes in the utf8 encoding
-# of the name portion of a metadata header.
-
-#max_meta_name_length = 128
-
-
-# max_meta_value_length is the max number of bytes in the utf8 encoding
-# of a metadata value
-
-#max_meta_value_length = 256
-
-
-# max_meta_count is the max number of metadata keys that can be stored
-# on a single account, container, or object
-
-#max_meta_count = 90
-
-
-# max_meta_overall_size is the max number of bytes in the utf8 encoding
-# of the metadata (keys + values)
-
-#max_meta_overall_size = 4096
-
-
-# max_object_name_length is the max number of bytes in the utf8 encoding of an
-# object name: Gluster FS can handle much longer file names, but the length
-# between the slashes of the URL is handled below. Remember that most web
-# clients can't handle anything greater than 2048, and those that do are
-# rather clumsy.
-
-max_object_name_length = 2048
-
-# max_object_name_component_length (GlusterFS) is the max number of bytes in
-# the utf8 encoding of an object name component (the part between the
-# slashes); this is a limit imposed by the underlying file system (for XFS it
-# is 255 bytes).
-
-max_object_name_component_length = 255
-
-# container_listing_limit is the default (and max) number of items
-# returned for a container listing request
-
-#container_listing_limit = 10000
-
-
-# account_listing_limit is the default (and max) number of items returned
-# for an account listing request
-
-#account_listing_limit = 10000
-
-
-# max_account_name_length is the max number of bytes in the utf8 encoding of
-# an account name: Gluster FS Filename limit (XFS limit?), must be the same
-# size as max_object_name_component_length above.
-
-max_account_name_length = 255
-
-
-# max_container_name_length is the max number of bytes in the utf8 encoding
-# of a container name: Gluster FS Filename limit (XFS limit?), must be the same
-# size as max_object_name_component_length above.
-
-max_container_name_length = 255
diff --git a/gluster/swift/common/middleware/gswauth/test_swauth/unit/__init__.py b/test/object_expirer_functional/__init__.py
index e69de29..e69de29 100644
--- a/gluster/swift/common/middleware/gswauth/test_swauth/unit/__init__.py
+++ b/test/object_expirer_functional/__init__.py
diff --git a/test/object_expirer_functional/test_object_expirer.py b/test/object_expirer_functional/test_object_expirer.py
new file mode 100644
index 0000000..aaec75e
--- /dev/null
+++ b/test/object_expirer_functional/test_object_expirer.py
@@ -0,0 +1,332 @@
+# Copyright (c) 2014 Red Hat, Inc.
+#
+# 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.
+
+import os
+import time
+
+from swift.common.manager import Manager
+from swift.common.internal_client import InternalClient
+
+from test.functional.tests import Base, config, Utils
+from test.functional.swift_test_client import Account, Connection, \
+ ResponseError
+
+
+class TestObjectExpirerEnv:
+ @classmethod
+ def setUp(cls):
+ cls.conn = Connection(config)
+ cls.conn.authenticate()
+ cls.account = Account(cls.conn,
+ config.get('account',
+ config['username']))
+ cls.account.delete_containers()
+ cls.container = cls.account.container(Utils.create_name())
+ if not cls.container.create():
+ raise ResponseError(cls.conn.response)
+ cls.file_size = 8
+ cls.root_dir = os.path.join('/mnt/gluster-object',
+ cls.account.conn.storage_url.split('/')[2].split('_')[1])
+ cls.client = InternalClient('/etc/swift/object-expirer.conf',
+ 'Test Object Expirer', 1)
+ cls.expirer = Manager(['object-expirer'])
+
+
+class TestObjectExpirer(Base):
+ env = TestObjectExpirerEnv
+ set_up = False
+
+ def test_object_expiry_X_Delete_At_PUT(self):
+ obj = self.env.container.file(Utils.create_name())
+ x_delete_at = str(int(time.time()) + 2)
+ obj.write_random(self.env.file_size,
+ hdrs={'X-Delete-At': x_delete_at})
+
+ # Object is not expired. Should still be accessible.
+ obj.read()
+ self.assert_status(200)
+
+ # Ensure X-Delete-At is saved as object metadata.
+ self.assertEqual(x_delete_at, str(obj.info()['x_delete_at']))
+
+ # Wait for object to be expired.
+ time.sleep(3)
+
+ # Object has expired. Should no longer be accessible.
+ self.assertRaises(ResponseError, obj.read)
+ self.assert_status(404)
+
+ # Object should still be present on filesystem.
+ self.assertTrue(os.path.isfile(os.path.join(self.env.root_dir,
+ self.env.container.name,
+ obj.name)))
+
+ # But, GET on container should list the expired object.
+ result = self.env.container.files()
+ self.assertTrue(obj.name in self.env.container.files())
+
+ # Check existence of corresponding tracker object in gsexpiring
+ # account.
+ enteredLoop = False
+ for c in self.env.client.iter_containers("gsexpiring"):
+ for o in self.env.client.iter_objects("gsexpiring", c['name']):
+ enteredLoop = True
+ l = o['name'].split('/')
+ self.assertTrue(l[0].endswith('AUTH_' + self.env.account.name))
+ self.assertEqual(l[1], self.env.container.name)
+ self.assertEqual(l[2], obj.name)
+ if not enteredLoop:
+ self.fail("Tracker object not found.")
+
+ # Run expirer daemon once.
+ self.env.expirer.once()
+
+ # Ensure object is physically deleted from filesystem.
+ self.assertFalse(os.path.exists(os.path.join(self.env.root_dir,
+ self.env.container.name,
+ obj.name)))
+
+ # Ensure tracker object is consumed.
+ try:
+ self.env.client.iter_containers("gsexpiring").next()
+ except StopIteration:
+ pass
+ else:
+ self.fail("Tracker object persists!")
+
+ # GET on container should no longer list the object.
+ self.assertFalse(obj.name in self.env.container.files())
+
+ def test_object_expiry_X_Delete_After_PUT(self):
+ obj = self.env.container.file(Utils.create_name())
+ obj.write_random(self.env.file_size,
+ hdrs={'X-Delete-After': 2})
+
+ # Object is not expired. Should still be accessible.
+ obj.read()
+ self.assert_status(200)
+
+ # Ensure X-Delete-At is saved as object metadata.
+ self.assertTrue(str(obj.info()['x_delete_at']))
+
+ # Wait for object to be expired.
+ time.sleep(3)
+
+ # Object has expired. Should no longer be accessible.
+ self.assertRaises(ResponseError, obj.read)
+ self.assert_status(404)
+
+ # Object should still be present on filesystem.
+ self.assertTrue(os.path.isfile(os.path.join(self.env.root_dir,
+ self.env.container.name,
+ obj.name)))
+
+ # But, GET on container should list the expired object.
+ result = self.env.container.files()
+ self.assertTrue(obj.name in self.env.container.files())
+
+ # Check existence of corresponding tracker object in gsexpiring
+ # account.
+ enteredLoop = False
+ for c in self.env.client.iter_containers("gsexpiring"):
+ for o in self.env.client.iter_objects("gsexpiring", c['name']):
+ enteredLoop = True
+ l = o['name'].split('/')
+ self.assertTrue(l[0].endswith('AUTH_' + self.env.account.name))
+ self.assertEqual(l[1], self.env.container.name)
+ self.assertEqual(l[2], obj.name)
+ if not enteredLoop:
+ self.fail("Tracker object not found.")
+
+ # Run expirer daemon once.
+ self.env.expirer.once()
+
+ # Ensure object is physically deleted from filesystem.
+ self.assertFalse(os.path.exists(os.path.join(self.env.root_dir,
+ self.env.container.name,
+ obj.name)))
+
+ # Ensure tracker object is consumed.
+ try:
+ self.env.client.iter_containers("gsexpiring").next()
+ except StopIteration:
+ pass
+ else:
+ self.fail("Tracker object persists!")
+
+ # GET on container should no longer list the object.
+ self.assertFalse(obj.name in self.env.container.files())
+
+
+ def test_object_expiry_X_Delete_At_POST(self):
+
+ # Create normal object
+ obj = self.env.container.file(Utils.create_name())
+ obj.write_random(self.env.file_size)
+ obj.read()
+ self.assert_status(200)
+
+ # Send POST on that object and set it to be expired.
+ x_delete_at = str(int(time.time()) + 2)
+ obj.sync_metadata(metadata={'X-Delete-At': x_delete_at},
+ cfg={'x_delete_at': x_delete_at})
+
+ # Ensure X-Delete-At is saved as object metadata.
+ self.assertEqual(x_delete_at, str(obj.info()['x_delete_at']))
+
+ # Object is not expired. Should still be accessible.
+ obj.read()
+ self.assert_status(200)
+
+ # Wait for object to be expired.
+ time.sleep(3)
+
+ # Object has expired. Should no longer be accessible.
+ self.assertRaises(ResponseError, obj.read)
+ self.assert_status(404)
+
+ # Object should still be present on filesystem.
+ self.assertTrue(os.path.isfile(os.path.join(self.env.root_dir,
+ self.env.container.name,
+ obj.name)))
+
+ # But, GET on container should list the expired object.
+ result = self.env.container.files()
+ self.assertTrue(obj.name in self.env.container.files())
+
+ # Check existence of corresponding tracker object in gsexpiring
+ # account.
+
+ enteredLoop = False
+ for c in self.env.client.iter_containers("gsexpiring"):
+ for o in self.env.client.iter_objects("gsexpiring", c['name']):
+ enteredLoop = True
+ l = o['name'].split('/')
+ self.assertTrue(l[0].endswith('AUTH_' + self.env.account.name))
+ self.assertEqual(l[1], self.env.container.name)
+ self.assertEqual(l[2], obj.name)
+ if not enteredLoop:
+ self.fail("Tracker object not found.")
+
+ # Run expirer daemon once.
+ self.env.expirer.once()
+ time.sleep(3)
+
+ # Ensure object is physically deleted from filesystem.
+ self.assertFalse(os.path.exists(os.path.join(self.env.root_dir,
+ self.env.container.name,
+ obj.name)))
+
+ # Ensure tracker object is consumed.
+ try:
+ self.env.client.iter_containers("gsexpiring").next()
+ except StopIteration:
+ pass
+ else:
+ self.fail("Tracker object persists!")
+
+ # GET on container should no longer list the object.
+ self.assertFalse(obj.name in self.env.container.files())
+
+
+ def test_object_expiry_X_Delete_After_POST(self):
+
+ # Create normal object
+ obj = self.env.container.file(Utils.create_name())
+ obj.write_random(self.env.file_size)
+ obj.read()
+ self.assert_status(200)
+
+ # Send POST on that object and set it to be expired.
+ obj.sync_metadata(metadata={'X-Delete-After': 2},
+ cfg={'x_delete_after': 2})
+
+ # Ensure X-Delete-At is saved as object metadata.
+ self.assertTrue(str(obj.info()['x_delete_at']))
+
+ # Object is not expired. Should still be accessible.
+ obj.read()
+ self.assert_status(200)
+
+ # Wait for object to be expired.
+ time.sleep(3)
+
+ # Object has expired. Should no longer be accessible.
+ self.assertRaises(ResponseError, obj.read)
+ self.assert_status(404)
+
+ # Object should still be present on filesystem.
+ self.assertTrue(os.path.isfile(os.path.join(self.env.root_dir,
+ self.env.container.name,
+ obj.name)))
+
+ # But, GET on container should list the expired object.
+ result = self.env.container.files()
+ self.assertTrue(obj.name in self.env.container.files())
+
+ # Check existence of corresponding tracker object in gsexpiring
+ # account.
+
+ enteredLoop = False
+ for c in self.env.client.iter_containers("gsexpiring"):
+ for o in self.env.client.iter_objects("gsexpiring", c['name']):
+ enteredLoop = True
+ l = o['name'].split('/')
+ self.assertTrue(l[0].endswith('AUTH_' + self.env.account.name))
+ self.assertEqual(l[1], self.env.container.name)
+ self.assertEqual(l[2], obj.name)
+ if not enteredLoop:
+ self.fail("Tracker object not found.")
+
+ # Run expirer daemon once.
+ self.env.expirer.once()
+ time.sleep(3)
+
+ # Ensure object is physically deleted from filesystem.
+ self.assertFalse(os.path.exists(os.path.join(self.env.root_dir,
+ self.env.container.name,
+ obj.name)))
+
+ # Ensure tracker object is consumed.
+ try:
+ self.env.client.iter_containers("gsexpiring").next()
+ except StopIteration:
+ pass
+ else:
+ self.fail("Tracker object persists!")
+
+ # GET on container should no longer list the object.
+ self.assertFalse(obj.name in self.env.container.files())
+
+
+ def test_object_expiry_err(self):
+ obj = self.env.container.file(Utils.create_name())
+
+ # X-Delete-At is invalid or is in the past
+ for i in (-2, 'abc', str(int(time.time()) - 2), 5.8):
+ self.assertRaises(ResponseError,
+ obj.write_random,
+ self.env.file_size,
+ hdrs={'X-Delete-At': i})
+ self.assert_status(400)
+
+ # X-Delete-After is invalid.
+ for i in (-2, 'abc', 3.7):
+ self.assertRaises(ResponseError,
+ obj.write_random,
+ self.env.file_size,
+ hdrs={'X-Delete-After': i})
+ self.assert_status(400)
+
diff --git a/test/unit/__init__.py b/test/unit/__init__.py
index 847479a..a1bfef8 100644
--- a/test/unit/__init__.py
+++ b/test/unit/__init__.py
@@ -33,6 +33,7 @@ from hashlib import md5
from eventlet import sleep, Timeout
import logging.handlers
from httplib import HTTPException
+from numbers import Number
class FakeRing(object):
@@ -248,6 +249,7 @@ class FakeLogger(logging.Logger):
if 'facility' in kwargs:
self.facility = kwargs['facility']
self.statsd_client = None
+ self.thread_locals = None
def _clear(self):
self.log_dict = defaultdict(list)
@@ -465,8 +467,11 @@ def fake_http_connect(*code_iter, **kwargs):
self.body = body
self.headers = headers or {}
self.timestamp = timestamp
- if kwargs.get('slow') and isinstance(kwargs['slow'], list):
- kwargs['slow'][0] -= 1
+ if 'slow' in kwargs and isinstance(kwargs['slow'], list):
+ try:
+ self._next_sleep = kwargs['slow'].pop(0)
+ except IndexError:
+ self._next_sleep = None
def getresponse(self):
if kwargs.get('raise_exc'):
@@ -482,6 +487,8 @@ def fake_http_connect(*code_iter, **kwargs):
return FakeConn(507)
if self.expect_status == -4:
return FakeConn(201)
+ if self.expect_status == 412:
+ return FakeConn(412)
return FakeConn(100)
def getheaders(self):
@@ -510,31 +517,39 @@ def fake_http_connect(*code_iter, **kwargs):
headers['x-container-timestamp'] = '1'
except StopIteration:
pass
- if self.am_slow():
+ am_slow, value = self.get_slow()
+ if am_slow:
headers['content-length'] = '4'
headers.update(self.headers)
return headers.items()
- def am_slow(self):
- if kwargs.get('slow') and isinstance(kwargs['slow'], list):
- return kwargs['slow'][0] >= 0
- return bool(kwargs.get('slow'))
+ def get_slow(self):
+ if 'slow' in kwargs and isinstance(kwargs['slow'], list):
+ if self._next_sleep is not None:
+ return True, self._next_sleep
+ else:
+ return False, 0.01
+ if kwargs.get('slow') and isinstance(kwargs['slow'], Number):
+ return True, kwargs['slow']
+ return bool(kwargs.get('slow')), 0.1
def read(self, amt=None):
- if self.am_slow():
+ am_slow, value = self.get_slow()
+ if am_slow:
if self.sent < 4:
self.sent += 1
- sleep(0.1)
+ sleep(value)
return ' '
rv = self.body[:amt]
self.body = self.body[amt:]
return rv
def send(self, amt=None):
- if self.am_slow():
+ am_slow, value = self.get_slow()
+ if am_slow:
if self.received < 4:
self.received += 1
- sleep(0.1)
+ sleep(value)
def getheader(self, name, default=None):
return dict(self.getheaders()).get(name.lower(), default)
@@ -584,4 +599,6 @@ def fake_http_connect(*code_iter, **kwargs):
return FakeConn(status, etag, body=body, timestamp=timestamp,
expect_status=expect_status, headers=headers)
+ connect.code_iter = code_iter
+
return connect
diff --git a/test/unit/common/middleware/swiftkerbauth/__init__.py b/test/unit/common/middleware/swiftkerbauth/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/test/unit/common/middleware/swiftkerbauth/__init__.py
+++ /dev/null
diff --git a/test/unit/common/middleware/swiftkerbauth/test_kerbauth.py b/test/unit/common/middleware/swiftkerbauth/test_kerbauth.py
deleted file mode 100644
index 537b8d3..0000000
--- a/test/unit/common/middleware/swiftkerbauth/test_kerbauth.py
+++ /dev/null
@@ -1,478 +0,0 @@
-# Copyright (c) 2013 Red Hat, Inc.
-#
-# 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.
-
-import os
-import errno
-import unittest
-from time import time
-from mock import patch, Mock
-from test.unit import FakeMemcache
-from swift.common.swob import Request, Response
-from gluster.swift.common.middleware.swiftkerbauth import kerbauth as auth
-
-EXT_AUTHENTICATION_URL = "127.0.0.1"
-REDIRECT_STATUS = 303 # HTTPSeeOther
-
-
-def my_filter_factory(global_conf, **local_conf):
- if 'ext_authentication_url' not in global_conf:
- global_conf['ext_authentication_url'] = EXT_AUTHENTICATION_URL
- conf = global_conf.copy()
- conf.update(local_conf)
-
- def auth_filter(app):
- return auth.KerbAuth(app, conf)
- return auth_filter
-
-# Monkey patching filter_factory to always pass ext_authentication_url
-# as a parameter. Absence of ext_authentication_url raises a RuntimeError
-
-
-def patch_filter_factory():
- auth.filter_factory = my_filter_factory
-
-
-def unpatch_filter_factory():
- reload(auth)
-
-
-class FakeApp(object):
-
- def __init__(self, status_headers_body_iter=None, acl=None, sync_key=None):
- self.calls = 0
- self.status_headers_body_iter = status_headers_body_iter
- if not self.status_headers_body_iter:
- self.status_headers_body_iter = iter([('404 Not Found', {}, '')])
- self.acl = acl
- self.sync_key = sync_key
-
- def __call__(self, env, start_response):
- self.calls += 1
- self.request = Request.blank('', environ=env)
- if self.acl:
- self.request.acl = self.acl
- if self.sync_key:
- self.request.environ['swift_sync_key'] = self.sync_key
- if 'swift.authorize' in env:
- resp = env['swift.authorize'](self.request)
- if resp:
- return resp(env, start_response)
- status, headers, body = self.status_headers_body_iter.next()
- return Response(status=status, headers=headers,
- body=body)(env, start_response)
-
-
-class TestKerbAuth(unittest.TestCase):
-
- # Patch auth.filter_factory()
- patch_filter_factory()
-
- def setUp(self):
- self.test_auth = \
- auth.filter_factory({'auth_method': 'active'})(FakeApp())
- self.test_auth_passive = \
- auth.filter_factory({'auth_method': 'passive'})(FakeApp())
-
- def _make_request(self, path, **kwargs):
- req = Request.blank(path, **kwargs)
- req.environ['swift.cache'] = FakeMemcache()
- return req
-
- def test_no_ext_authentication_url(self):
- app = FakeApp()
- try:
- # Use original auth.filter_factory and NOT monkey patched version
- unpatch_filter_factory()
- auth.filter_factory({})(app)
- except RuntimeError as e:
- # Restore monkey patched version
- patch_filter_factory()
- self.assertTrue(e.args[0].startswith("Missing filter parameter "
- "ext_authentication_url"))
-
- def test_reseller_prefix_init(self):
- app = FakeApp()
- ath = auth.filter_factory({})(app)
- self.assertEquals(ath.reseller_prefix, 'AUTH_')
- ath = auth.filter_factory({'reseller_prefix': 'TEST'})(app)
- self.assertEquals(ath.reseller_prefix, 'TEST_')
- ath = auth.filter_factory({'reseller_prefix': 'TEST_'})(app)
- self.assertEquals(ath.reseller_prefix, 'TEST_')
-
- def test_auth_prefix_init(self):
- app = FakeApp()
- ath = auth.filter_factory({})(app)
- self.assertEquals(ath.auth_prefix, '/auth/')
- ath = auth.filter_factory({'auth_prefix': ''})(app)
- self.assertEquals(ath.auth_prefix, '/auth/')
- ath = auth.filter_factory({'auth_prefix': '/'})(app)
- self.assertEquals(ath.auth_prefix, '/auth/')
- ath = auth.filter_factory({'auth_prefix': '/test/'})(app)
- self.assertEquals(ath.auth_prefix, '/test/')
- ath = auth.filter_factory({'auth_prefix': '/test'})(app)
- self.assertEquals(ath.auth_prefix, '/test/')
- ath = auth.filter_factory({'auth_prefix': 'test/'})(app)
- self.assertEquals(ath.auth_prefix, '/test/')
- ath = auth.filter_factory({'auth_prefix': 'test'})(app)
- self.assertEquals(ath.auth_prefix, '/test/')
-
- def test_top_level_redirect(self):
- req = self._make_request('/')
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, REDIRECT_STATUS)
- self.assertEquals(req.environ['swift.authorize'],
- self.test_auth.denied_response)
-
- def test_passive_top_level_deny(self):
- req = self._make_request('/')
- resp = req.get_response(self.test_auth_passive)
- self.assertEquals(resp.status_int, 401)
- self.assertEquals(req.environ['swift.authorize'],
- self.test_auth_passive.denied_response)
-
- def test_passive_deny_invalid_token(self):
- req = self._make_request('/v1/AUTH_account',
- headers={'X-Auth-Token': 'AUTH_t'})
- resp = req.get_response(self.test_auth_passive)
- self.assertEquals(resp.status_int, 401)
-
- def test_override_asked_for_and_allowed(self):
- self.test_auth = \
- auth.filter_factory({'allow_overrides': 'true'})(FakeApp())
- req = self._make_request('/v1/AUTH_account',
- environ={'swift.authorize_override': True})
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 404)
- self.assertTrue('swift.authorize' not in req.environ)
-
- def test_override_default_allowed(self):
- req = self._make_request('/v1/AUTH_account',
- environ={'swift.authorize_override': True})
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 404)
- self.assertTrue('swift.authorize' not in req.environ)
-
- def test_options_call(self):
- req = self._make_request('/v1/AUTH_cfa/c/o',
- environ={'REQUEST_METHOD': 'OPTIONS'})
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp, None)
-
- def test_auth_deny_non_reseller_prefix_no_override(self):
- fake_authorize = lambda x: Response(status='500 Fake')
- req = self._make_request('/v1/BLAH_account',
- headers={'X-Auth-Token': 'BLAH_t'},
- environ={'swift.authorize': fake_authorize}
- )
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, 500)
- self.assertEquals(req.environ['swift.authorize'], fake_authorize)
-
- def test_authorize_acl_group_access(self):
- req = self._make_request('/v1/AUTH_cfa')
- req.remote_user = 'act:usr,act'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
- req = self._make_request('/v1/AUTH_cfa')
- req.remote_user = 'act:usr,act'
- req.acl = 'act'
- self.assertEquals(self.test_auth.authorize(req), None)
- req = self._make_request('/v1/AUTH_cfa')
- req.remote_user = 'act:usr,act'
- req.acl = 'act:usr'
- self.assertEquals(self.test_auth.authorize(req), None)
- req = self._make_request('/v1/AUTH_cfa')
- req.remote_user = 'act:usr,act'
-
- def test_deny_cross_reseller(self):
- # Tests that cross-reseller is denied, even if ACLs/group names match
- req = self._make_request('/v1/OTHER_cfa')
- req.remote_user = 'act:usr,act,AUTH_cfa'
- req.acl = 'act'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
-
- def test_authorize_acl_referer_after_user_groups(self):
- req = self._make_request('/v1/AUTH_cfa/c')
- req.remote_user = 'act:usr'
- req.acl = '.r:*,act:usr'
- self.assertEquals(self.test_auth.authorize(req), None)
-
- def test_detect_reseller_request(self):
- req = self._make_request('/v1/AUTH_admin',
- headers={'X-Auth-Token': 'AUTH_t'})
- cache_key = 'AUTH_/token/AUTH_t'
- cache_entry = (time() + 3600, '.reseller_admin')
- req.environ['swift.cache'].set(cache_key, cache_entry)
- req.get_response(self.test_auth)
- self.assertTrue(req.environ.get('reseller_request', False))
-
- def test_regular_is_not_owner(self):
- orig_authorize = self.test_auth.authorize
- owner_values = []
-
- def mitm_authorize(req):
- rv = orig_authorize(req)
- owner_values.append(req.environ.get('swift_owner', False))
- return rv
-
- self.test_auth.authorize = mitm_authorize
-
- req = self._make_request(
- '/v1/AUTH_cfa/c',
- headers={'X-Auth-Token': 'AUTH_t'})
- req.remote_user = 'act:usr'
- self.test_auth.authorize(req)
- self.assertEquals(owner_values, [False])
-
- def test_no_memcache(self):
- env = {'swift.cache': None}
- try:
- self.test_auth.get_groups(env, None)
- except Exception as e:
- self.assertTrue(e.args[0].startswith("Memcache required"))
-
- def test_handle_request(self):
- req = self._make_request('/auth/v1.0')
- resp = self.test_auth.handle_request(req)
- self.assertEquals(resp.status_int, REDIRECT_STATUS)
-
- def test_handle_request_bad_request(self):
- req = self._make_request('////')
- resp = self.test_auth.handle_request(req)
- self.assertEquals(resp.status_int, 404)
-
- def test_handle_request_no_handler(self):
- req = self._make_request('/blah/blah/blah/blah')
- resp = self.test_auth.handle_request(req)
- self.assertEquals(resp.status_int, 400)
-
- def test_handle_get_token_bad_request(self):
- req = self._make_request('/blah/blah')
- resp = self.test_auth.handle_get_token(req)
- self.assertEquals(resp.status_int, 400)
- req = self._make_request('/////')
- resp = self.test_auth.handle_get_token(req)
- self.assertEquals(resp.status_int, 404)
-
- def test_passive_handle_get_token_no_user_or_key(self):
- #No user and key
- req = self._make_request('/auth/v1.0')
- resp = self.test_auth_passive.handle_get_token(req)
- self.assertEquals(resp.status_int, REDIRECT_STATUS)
- #User given but no key
- req = self._make_request('/auth/v1.0',
- headers={'X-Auth-User': 'test:user'})
- resp = self.test_auth_passive.handle_get_token(req)
- self.assertEquals(resp.status_int, 401)
-
- def test_passive_handle_get_token_account_in_req_path(self):
- req = self._make_request('/v1/test/auth',
- headers={'X-Auth-User': 'test:user',
- 'X-Auth-Key': 'password'})
- _mock_run_kinit = Mock(return_value=0)
- _mock_get_groups = Mock(return_value="user,auth_test")
- with patch('gluster.swift.common.middleware.swiftkerbauth.kerbauth.run_kinit', _mock_run_kinit):
- with patch('gluster.swift.common.middleware.swiftkerbauth.kerbauth.get_groups_from_username',
- _mock_get_groups):
- resp = self.test_auth_passive.handle_get_token(req)
- _mock_run_kinit.assert_called_once_with('user', 'password')
- self.assertEquals(_mock_get_groups.call_count, 2)
- self.assertEquals(resp.status_int, 200)
- self.assertTrue(resp.headers['X-Auth-Token'] is not None)
- self.assertTrue(resp.headers['X-Storage-Token'] is not None)
- self.assertTrue(resp.headers['X-Storage-Url'] is not None)
-
- def test_passive_handle_get_token_user_invalid_or_no__account(self):
- #X-Auth-User not in acc:user format
- req = self._make_request('/auth/v1.0',
- headers={'X-Auth-User': 'user'})
- resp = self.test_auth_passive.handle_get_token(req)
- self.assertEquals(resp.status_int, 401)
- req = self._make_request('/v1/test/auth',
- headers={'X-Auth-User': 'user'})
- resp = self.test_auth_passive.handle_get_token(req)
- self.assertEquals(resp.status_int, 401)
- # Account name mismatch
- req = self._make_request('/v1/test/auth',
- headers={'X-Auth-User': 'wrongacc:user'})
- resp = self.test_auth_passive.handle_get_token(req)
- self.assertEquals(resp.status_int, 401)
-
- def test_passive_handle_get_token_no_kinit(self):
- req = self._make_request('/auth/v1.0',
- headers={'X-Auth-User': 'test:user',
- 'X-Auth-Key': 'password'})
- _mock_run_kinit = Mock(side_effect=OSError(errno.ENOENT,
- os.strerror(errno.ENOENT)))
- with patch('gluster.swift.common.middleware.swiftkerbauth.kerbauth.run_kinit', _mock_run_kinit):
- resp = self.test_auth_passive.handle_get_token(req)
- self.assertEquals(resp.status_int, 500)
- self.assertTrue("kinit command not found" in resp.body)
- _mock_run_kinit.assert_called_once_with('user', 'password')
-
- def test_passive_handle_get_token_kinit_fail(self):
- req = self._make_request('/auth/v1.0',
- headers={'X-Auth-User': 'test:user',
- 'X-Auth-Key': 'password'})
- _mock_run_kinit = Mock(return_value=1)
- with patch('gluster.swift.common.middleware.swiftkerbauth.kerbauth.run_kinit', _mock_run_kinit):
- resp = self.test_auth_passive.handle_get_token(req)
- self.assertEquals(resp.status_int, 401)
- _mock_run_kinit.assert_called_once_with('user', 'password')
-
- def test_passive_handle_get_token_kinit_success_token_not_present(self):
- req = self._make_request('/auth/v1.0',
- headers={'X-Auth-User': 'test:user',
- 'X-Auth-Key': 'password'})
- _mock_run_kinit = Mock(return_value=0)
- _mock_get_groups = Mock(return_value="user,auth_test")
- with patch('gluster.swift.common.middleware.swiftkerbauth.kerbauth.run_kinit', _mock_run_kinit):
- with patch('gluster.swift.common.middleware.swiftkerbauth.kerbauth.get_groups_from_username',
- _mock_get_groups):
- resp = self.test_auth_passive.handle_get_token(req)
- _mock_run_kinit.assert_called_once_with('user', 'password')
- self.assertEquals(_mock_get_groups.call_count, 2)
- self.assertEquals(resp.status_int, 200)
- self.assertTrue(resp.headers['X-Auth-Token'] is not None)
- self.assertTrue(resp.headers['X-Storage-Token'] is not None)
- self.assertTrue(resp.headers['X-Storage-Url'] is not None)
-
- def test_passive_handle_get_token_kinit_realm_and_memcache(self):
- req = self._make_request('/auth/v1.0',
- headers={'X-Auth-User': 'test:user',
- 'X-Auth-Key': 'password'})
- req.environ['swift.cache'] = None
- _auth_passive = \
- auth.filter_factory({'auth_method': 'passive',
- 'realm_name': 'EXAMPLE.COM'})(FakeApp())
- _mock_run_kinit = Mock(return_value=0)
- _mock_get_groups = Mock(return_value="user,auth_test")
- with patch('gluster.swift.common.middleware.swiftkerbauth.kerbauth.run_kinit', _mock_run_kinit):
- with patch('gluster.swift.common.middleware.swiftkerbauth.kerbauth.get_groups_from_username',
- _mock_get_groups):
- try:
- _auth_passive.handle_get_token(req)
- except Exception as e:
- self.assertTrue(e.args[0].startswith("Memcache "
- "required"))
- else:
- self.fail("Expected Exception - Memcache required")
- _mock_run_kinit.assert_called_once_with('user@EXAMPLE.COM', 'password')
- _mock_get_groups.assert_called_once_with('user')
-
- def test_passive_handle_get_token_user_in_any__account(self):
- req = self._make_request('/auth/v1.0',
- headers={'X-Auth-User': 'test:user',
- 'X-Auth-Key': 'password'})
- _mock_run_kinit = Mock(return_value=0)
- _mock_get_groups = Mock(return_value="user,auth_blah")
- with patch('gluster.swift.common.middleware.swiftkerbauth.kerbauth.run_kinit', _mock_run_kinit):
- with patch('gluster.swift.common.middleware.swiftkerbauth.kerbauth.get_groups_from_username',
- _mock_get_groups):
- resp = self.test_auth_passive.handle_get_token(req)
- self.assertEquals(resp.status_int, 401)
- _mock_run_kinit.assert_called_once_with('user', 'password')
- _mock_get_groups.assert_called_once_with('user')
-
- def test_handle(self):
- req = self._make_request('/auth/v1.0')
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, REDIRECT_STATUS)
-
- def test_authorize_invalid_req(self):
- req = self._make_request('/')
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 404)
-
- def test_authorize_set_swift_owner(self):
- req = self._make_request('/v1/AUTH_test/c1/o1')
- req.remote_user = 'test,auth_reseller_admin'
- resp = self.test_auth.authorize(req)
- self.assertEquals(req.environ['swift_owner'], True)
- self.assertTrue(resp is None)
- req = self._make_request('/v1/AUTH_test/c1/o1')
- req.remote_user = 'test,auth_test'
- resp = self.test_auth.authorize(req)
- self.assertEquals(req.environ['swift_owner'], True)
- self.assertTrue(resp is None)
-
- def test_authorize_swift_sync_key(self):
- req = self._make_request(
- '/v1/AUTH_cfa/c/o',
- environ={'swift_sync_key': 'secret'},
- headers={'x-container-sync-key': 'secret',
- 'x-timestamp': '123.456'})
- resp = self.test_auth.authorize(req)
- self.assertTrue(resp is None)
-
- def test_authorize_acl_referrer_access(self):
- req = self._make_request('/v1/AUTH_cfa/c')
- req.remote_user = 'act:usr,act'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
- req = self._make_request('/v1/AUTH_cfa/c')
- req.remote_user = 'act:usr,act'
- req.acl = '.r:*,.rlistings'
- self.assertEquals(self.test_auth.authorize(req), None)
- req = self._make_request('/v1/AUTH_cfa/c')
- req.remote_user = 'act:usr,act'
- req.acl = '.r:*' # No listings allowed
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
- req = self._make_request('/v1/AUTH_cfa/c')
- req.remote_user = 'act:usr,act'
- req.acl = '.r:.example.com,.rlistings'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, 403)
- req = self._make_request('/v1/AUTH_cfa/c')
- req.remote_user = 'act:usr,act'
- req.referer = 'http://www.example.com/index.html'
- req.acl = '.r:.example.com,.rlistings'
- self.assertEquals(self.test_auth.authorize(req), None)
- req = self._make_request('/v1/AUTH_cfa/c')
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, REDIRECT_STATUS)
- req = self._make_request('/v1/AUTH_cfa/c')
- req.acl = '.r:*,.rlistings'
- self.assertEquals(self.test_auth.authorize(req), None)
- req = self._make_request('/v1/AUTH_cfa/c')
- req.acl = '.r:*' # No listings allowed
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, REDIRECT_STATUS)
- req = self._make_request('/v1/AUTH_cfa/c')
- req.acl = '.r:.example.com,.rlistings'
- resp = self.test_auth.authorize(req)
- self.assertEquals(resp.status_int, REDIRECT_STATUS)
- req = self._make_request('/v1/AUTH_cfa/c')
- req.referer = 'http://www.example.com/index.html'
- req.acl = '.r:.example.com,.rlistings'
- self.assertEquals(self.test_auth.authorize(req), None)
-
- def test_handle_x_storage_token(self):
- req = self._make_request(
- '/auth/v1.0',
- headers={'x-storage-token': 'blahblah', })
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, REDIRECT_STATUS)
-
- def test_invalid_token(self):
- req = self._make_request('/k1/test')
- req.environ['HTTP_X_AUTH_TOKEN'] = 'AUTH_blahblahblah'
- resp = req.get_response(self.test_auth)
- self.assertEquals(resp.status_int, REDIRECT_STATUS)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/test/unit/common/middleware/swiftkerbauth/test_kerbauth_utils.py b/test/unit/common/middleware/swiftkerbauth/test_kerbauth_utils.py
deleted file mode 100644
index 2a4e90b..0000000
--- a/test/unit/common/middleware/swiftkerbauth/test_kerbauth_utils.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# Copyright (c) 2013 Red Hat, Inc.
-#
-# 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.
-
-import unittest
-import re
-from time import time
-from test.unit import FakeMemcache
-from gluster.swift.common.middleware.swiftkerbauth import kerbauth_utils as ku
-
-
-class TestKerbUtils(unittest.TestCase):
-
- def test_get_remote_user(self):
- env = {'REMOTE_USER': "auth_admin@EXAMPLE.COM"}
- result = ku.get_remote_user(env)
- self.assertEqual(result, "auth_admin")
-
- def test_get_remote_user_err(self):
- env = {'REMOTE_USER': "auth_admin"}
- try:
- ku.get_remote_user(env)
- except RuntimeError as err:
- self.assertTrue(err.args[0].startswith("Malformed REMOTE_USER"))
- else:
- self.fail("Expected RuntimeError")
-
- def test_get_auth_data(self):
- mc = FakeMemcache()
- expiry = time() + 100
- ku.set_auth_data(mc, "root", "AUTH_tk", expiry, "root,admin")
- (token, expires, groups) = ku.get_auth_data(mc, "root")
- self.assertEqual(("AUTH_tk", expiry, "root,admin"),
- (token, expires, groups))
-
- def test_get_auth_data_err(self):
- mc = FakeMemcache()
- (token, expires, groups) = ku.get_auth_data(mc, "root")
- self.assertEqual((token, expires, groups), (None, None, None))
-
- expiry = time() - 1
- ku.set_auth_data(mc, "root", "AUTH_tk", expiry, "root,admin")
- (token, expires, groups) = ku.get_auth_data(mc, "root")
- self.assertEqual((token, expires, groups), (None, None, None))
-
- def test_set_auth_data(self):
- mc = FakeMemcache()
- expiry = time() + 100
- ku.set_auth_data(mc, "root", "AUTH_tk", expiry, "root,admin")
-
- def test_generate_token(self):
- token = ku.generate_token()
- matches = re.match('AUTH_tk[a-f0-9]{32}', token)
- self.assertTrue(matches is not None)
-
- def test_get_groups_from_username(self):
- groups = ku.get_groups_from_username("root")
- self.assertTrue("root" in groups)
-
- def test_get_groups_from_username_err(self):
- try:
- ku.get_groups_from_username("Zroot")
- except RuntimeError as err:
- self.assertTrue(err.args[0].startswith("Failure running id -G"))
- else:
- self.fail("Expected RuntimeError")
diff --git a/test/unit/common/test_constraints.py b/test/unit/common/test_constraints.py
index 6c78d75..e8ddd69 100644
--- a/test/unit/common/test_constraints.py
+++ b/test/unit/common/test_constraints.py
@@ -57,10 +57,10 @@ class TestConstraints(unittest.TestCase):
cnt.set_object_name_component_length()
self.assertEqual(len, cnt.get_object_name_component_length())
- with patch('swift.common.constraints.constraints_conf_int',
- mock_constraints_conf_int):
- cnt.set_object_name_component_length()
- self.assertEqual(cnt.get_object_name_component_length(), 1000)
+ with patch('swift.common.constraints.constraints_conf_int',
+ mock_constraints_conf_int):
+ cnt.set_object_name_component_length()
+ self.assertEqual(cnt.get_object_name_component_length(), 1000)
def test_validate_obj_name_component(self):
max_obj_len = cnt.get_object_name_component_length()
@@ -75,65 +75,6 @@ class TestConstraints(unittest.TestCase):
self.assertTrue(cnt.validate_obj_name_component('..'))
self.assertTrue(cnt.validate_obj_name_component(''))
- def test_validate_headers(self):
- req = Mock()
- req.headers = []
- self.assertEqual(cnt.validate_headers(req), '')
- req.headers = ['x-some-header']
- self.assertEqual(cnt.validate_headers(req), '')
- #TODO: Although we now support x-delete-at and x-delete-after,
- #retained this test case as we may add some other header to
- #unsupported list in future
- raise SkipTest
- req.headers = ['x-delete-at', 'x-some-header']
- self.assertNotEqual(cnt.validate_headers(req), '')
- req.headers = ['x-delete-after', 'x-some-header']
- self.assertNotEqual(cnt.validate_headers(req), '')
- req.headers = ['x-delete-at', 'x-delete-after', 'x-some-header']
- self.assertNotEqual(cnt.validate_headers(req), '')
-
- def test_validate_headers_ignoring_config_set(self):
- with patch('gluster.swift.common.constraints.'
- 'Glusterfs._ignore_unsupported_headers', True):
- req = Mock()
- req.headers = []
- self.assertEqual(cnt.validate_headers(req), '')
- req.headers = ['x-some-header']
- self.assertEqual(cnt.validate_headers(req), '')
- #TODO: Although we now support x-delete-at and x-delete-after,
- #retained this test case as we may add some other header to
- #unsupported list in future
- raise SkipTest
- req.headers = ['x-delete-at', 'x-some-header']
- self.assertEqual(cnt.validate_headers(req), '')
- req.headers = ['x-delete-after', 'x-some-header']
- self.assertEqual(cnt.validate_headers(req), '')
- req.headers = ['x-delete-at', 'x-delete-after', 'x-some-header']
- self.assertEqual(cnt.validate_headers(req), '')
-
- def test_gluster_check_metadata(self):
- mock_check_metadata = Mock()
- with patch('gluster.swift.common.constraints.__check_metadata',
- mock_check_metadata):
- req = Mock()
- req.headers = []
- cnt.gluster_check_metadata(req, 'object')
- self.assertTrue(1, mock_check_metadata.call_count)
- cnt.gluster_check_metadata(req, 'object', POST=False)
- self.assertTrue(1, mock_check_metadata.call_count)
- req.headers = ['x-some-header']
- self.assertEqual(cnt.gluster_check_metadata(req, 'object', POST=False), None)
- #TODO: Although we now support x-delete-at and x-delete-after,
- #retained this test case as we may add some other header to
- #unsupported list in future
- raise SkipTest
- req.headers = ['x-delete-at', 'x-some-header']
- self.assertNotEqual(cnt.gluster_check_metadata(req, 'object', POST=False), None)
- req.headers = ['x-delete-after', 'x-some-header']
- self.assertNotEqual(cnt.gluster_check_metadata(req, 'object', POST=False), None)
- req.headers = ['x-delete-at', 'x-delete-after', 'x-some-header']
- self.assertNotEqual(cnt.gluster_check_metadata(req, 'object', POST=False), None)
-
def test_gluster_check_object_creation(self):
with patch('gluster.swift.common.constraints.__check_object_creation',
mock_check_object_creation):
@@ -147,9 +88,3 @@ class TestConstraints(unittest.TestCase):
req = Mock()
req.headers = []
self.assertTrue(cnt.gluster_check_object_creation(req, 'dir/.'))
- #TODO: Although we now support x-delete-at and x-delete-after,
- #retained this test case as we may add some other header to
- #unsupported list in future
- raise SkipTest
- req.headers = ['x-delete-at']
- self.assertTrue(cnt.gluster_check_object_creation(req, 'dir/z'))
diff --git a/test/unit/common/test_diskdir.py b/test/unit/common/test_diskdir.py
index f32c3ad..ebc554a 100644
--- a/test/unit/common/test_diskdir.py
+++ b/test/unit/common/test_diskdir.py
@@ -408,7 +408,7 @@ class TestContainerBroker(unittest.TestCase):
self.assertEqual(os.path.basename(broker.db_file), 'db_file.db')
broker.initialize(self.initial_ts)
self.assertTrue(os.path.isdir(self.container))
- self.assertEquals(self.initial_ts, broker.metadata[utils.X_TIMESTAMP])
+ self.assertEquals(self.initial_ts, broker.metadata[utils.X_TIMESTAMP][0])
self.assertFalse(broker.is_deleted())
def test_creation_existing(self):
@@ -419,7 +419,7 @@ class TestContainerBroker(unittest.TestCase):
self.assertEqual(os.path.basename(broker.db_file), 'db_file.db')
broker.initialize(self.initial_ts)
self.assertTrue(os.path.isdir(self.container))
- self.assertEquals(self.initial_ts, broker.metadata[utils.X_TIMESTAMP])
+ self.assertEquals(self.initial_ts, broker.metadata[utils.X_TIMESTAMP][0])
self.assertFalse(broker.is_deleted())
def test_creation_existing_bad_metadata(self):
@@ -432,7 +432,7 @@ class TestContainerBroker(unittest.TestCase):
self.assertEqual(os.path.basename(broker.db_file), 'db_file.db')
broker.initialize(self.initial_ts)
self.assertTrue(os.path.isdir(self.container))
- self.assertEquals(self.initial_ts, broker.metadata[utils.X_TIMESTAMP])
+ self.assertEquals(self.initial_ts, broker.metadata[utils.X_TIMESTAMP][0])
self.assertFalse(broker.is_deleted())
def test_empty(self):
@@ -954,7 +954,7 @@ class TestContainerBroker(unittest.TestCase):
self.assertEqual(os.path.basename(broker.db_file), 'db_file.db')
broker.initialize(self.initial_ts)
self.assertTrue(os.path.isdir(self.container))
- self.assertEquals(self.initial_ts, broker.metadata[utils.X_TIMESTAMP])
+ self.assertEquals(self.initial_ts, broker.metadata[utils.X_TIMESTAMP][0])
self.assertFalse(broker.is_deleted())
broker.delete_db(normalize_timestamp(time()))
self.assertTrue(broker.is_deleted())
@@ -1005,7 +1005,7 @@ class TestAccountBroker(unittest.TestCase):
self.assertEqual(os.path.basename(broker.db_file), 'db_file.db')
broker.initialize(self.initial_ts)
self.assertTrue(os.path.isdir(self.drive_fullpath))
- self.assertEquals(self.initial_ts, broker.metadata[utils.X_TIMESTAMP])
+ self.assertEquals(self.initial_ts, broker.metadata[utils.X_TIMESTAMP][0])
self.assertFalse(broker.is_deleted())
def test_creation_bad_metadata(self):
@@ -1016,7 +1016,7 @@ class TestAccountBroker(unittest.TestCase):
self.assertEqual(os.path.basename(broker.db_file), 'db_file.db')
broker.initialize(self.initial_ts)
self.assertTrue(os.path.isdir(self.drive_fullpath))
- self.assertEquals(self.initial_ts, broker.metadata[utils.X_TIMESTAMP])
+ self.assertEquals(self.initial_ts, broker.metadata[utils.X_TIMESTAMP][0])
self.assertFalse(broker.is_deleted())
def test_empty(self):
@@ -1219,7 +1219,7 @@ class TestAccountBroker(unittest.TestCase):
self.assertEqual(os.path.basename(broker.db_file), 'db_file.db')
broker.initialize(self.initial_ts)
self.assertTrue(os.path.isdir(self.drive_fullpath))
- self.assertEquals(self.initial_ts, broker.metadata[utils.X_TIMESTAMP])
+ self.assertEquals(self.initial_ts, broker.metadata[utils.X_TIMESTAMP][0])
self.assertFalse(broker.is_deleted())
broker.delete_db(normalize_timestamp(time()))
# Deleting the "db" should be a NOOP
diff --git a/test/unit/common/test_utils.py b/test/unit/common/test_utils.py
index dd03bd8..55bd0cf 100644
--- a/test/unit/common/test_utils.py
+++ b/test/unit/common/test_utils.py
@@ -25,9 +25,10 @@ import hashlib
import tarfile
import shutil
from collections import defaultdict
-from mock import patch
+from mock import patch, Mock
from gluster.swift.common import utils, Glusterfs
-from gluster.swift.common.exceptions import GlusterFileSystemOSError
+from gluster.swift.common.exceptions import GlusterFileSystemOSError,\
+ GlusterFileSystemIOError
from swift.common.exceptions import DiskFileNoSpace
#
@@ -712,6 +713,18 @@ class TestUtils(unittest.TestCase):
ret = utils.validate_object(md)
assert ret
+ def test_validate_object_with_stat(self):
+ md = {utils.X_TIMESTAMP: 'na',
+ utils.X_CONTENT_TYPE: 'na',
+ utils.X_ETAG: 'bad',
+ utils.X_CONTENT_LENGTH: '12345',
+ utils.X_TYPE: utils.OBJECT,
+ utils.X_OBJECT_TYPE: 'na'}
+ fake_stat = Mock(st_size=12346)
+ self.assertFalse(utils.validate_object(md, fake_stat))
+ fake_stat = Mock(st_size=12345)
+ self.assertTrue(utils.validate_object(md, fake_stat))
+
class TestUtilsDirObjects(unittest.TestCase):
@@ -794,7 +807,8 @@ class TestUtilsDirObjects(unittest.TestCase):
def _mock_rm(path):
print "_mock_rm-metadata_enoent(%s)" % path
shutil.rmtree(path)
- raise OSError(errno.ENOENT, os.strerror(errno.ENOENT))
+ raise GlusterFileSystemIOError(errno.ENOENT,
+ os.strerror(errno.ENOENT))
# Remove the files
for f in self.files:
@@ -805,8 +819,8 @@ class TestUtilsDirObjects(unittest.TestCase):
try:
try:
self.assertTrue(utils.rmobjdir(self.rootdir))
- except OSError:
- self.fail("Unexpected OSError")
+ except IOError:
+ self.fail("Unexpected IOError")
else:
pass
finally:
diff --git a/test/unit/obj/test_diskfile.py b/test/unit/obj/test_diskfile.py
index f8c26db..1fe0904 100644
--- a/test/unit/obj/test_diskfile.py
+++ b/test/unit/obj/test_diskfile.py
@@ -223,7 +223,7 @@ class TestDiskFile(unittest.TestCase):
ini_md = {
'X-Type': 'Object',
'X-Object-Type': 'file',
- 'Content-Length': 5,
+ 'Content-Length': 4,
'ETag': 'etag',
'X-Timestamp': 'ts',
'Content-Type': 'application/loctet-stream'}
@@ -283,9 +283,8 @@ class TestDiskFile(unittest.TestCase):
with gdf.open():
assert gdf._is_dir
assert gdf._data_file == the_dir
- assert gdf._metadata == exp_md
- def _create_and_get_diskfile(self, dev, par, acc, con, obj):
+ def _create_and_get_diskfile(self, dev, par, acc, con, obj, fsize=256):
# FIXME: assumes account === volume
the_path = os.path.join(self.td, dev, con)
the_file = os.path.join(the_path, obj)
@@ -293,7 +292,7 @@ class TestDiskFile(unittest.TestCase):
base_dir = os.path.dirname(the_file)
os.makedirs(base_dir)
with open(the_file, "wb") as fd:
- fd.write("y" * 256)
+ fd.write("y" * fsize)
gdf = self._get_diskfile(dev, par, acc, con, obj)
assert gdf._obj == base_obj
assert not gdf._is_dir
@@ -353,6 +352,26 @@ class TestDiskFile(unittest.TestCase):
assert len(chunks) == 1, repr(chunks)
assert called[0] == 1, called
+ def test_reader_larger_file(self):
+ closed = [False]
+ fd = [-1]
+
+ def mock_close(*args, **kwargs):
+ closed[0] = True
+ os.close(fd[0])
+
+ with mock.patch("gluster.swift.obj.diskfile.do_close", mock_close):
+ gdf = self._create_and_get_diskfile("vol0", "p57", "ufo47", "bar", "z", fsize=1024*1024*2)
+ with gdf.open():
+ assert gdf._fd is not None
+ assert gdf._data_file == os.path.join(self.td, "vol0", "bar", "z")
+ reader = gdf.reader()
+ assert reader._fd is not None
+ fd[0] = reader._fd
+ chunks = [ck for ck in reader]
+ assert reader._fd is None
+ assert closed[0]
+
def test_reader_dir_object(self):
called = [False]
@@ -926,3 +945,31 @@ class TestDiskFile(unittest.TestCase):
dw.write("123")
os.unlink(saved_tmppath)
assert not os.path.exists(saved_tmppath)
+
+ def test_unlink_not_called_after_rename(self):
+ the_obj_path = os.path.join("b", "a")
+ the_file = os.path.join(the_obj_path, "z")
+ gdf = self._get_diskfile("vol0", "p57", "ufo47", "bar", the_file)
+
+ body = '1234\n'
+ etag = md5(body).hexdigest()
+ metadata = {
+ 'X-Timestamp': '1234',
+ 'Content-Type': 'file',
+ 'ETag': etag,
+ 'Content-Length': '5',
+ }
+
+ _mock_do_unlink = Mock() # Shouldn't be called
+ with patch("gluster.swift.obj.diskfile.do_unlink", _mock_do_unlink):
+ with gdf.create() as dw:
+ assert dw._tmppath is not None
+ tmppath = dw._tmppath
+ dw.write(body)
+ dw.put(metadata)
+ # do_unlink is not called if dw._tmppath is set to None
+ assert dw._tmppath is None
+ self.assertFalse(_mock_do_unlink.called)
+
+ assert os.path.exists(gdf._data_file) # Real file exists
+ assert not os.path.exists(tmppath) # Temp file does not exist
diff --git a/test/unit/obj/test_expirer.py b/test/unit/obj/test_expirer.py
index 4329eef..236775e 100644
--- a/test/unit/obj/test_expirer.py
+++ b/test/unit/obj/test_expirer.py
@@ -46,7 +46,7 @@ class TestObjectExpirer(TestCase):
self.old_loadapp = internal_client.loadapp
self.old_sleep = internal_client.sleep
- internal_client.loadapp = lambda x: None
+ internal_client.loadapp = lambda *a, **kw: None
internal_client.sleep = not_sleep
def teardown(self):
@@ -618,7 +618,7 @@ class TestObjectExpirer(TestCase):
start_response('204 No Content', [('Content-Length', '0')])
return []
- internal_client.loadapp = lambda x: fake_app
+ internal_client.loadapp = lambda *a, **kw: fake_app
x = expirer.ObjectExpirer({})
ts = '1234'
@@ -635,7 +635,7 @@ class TestObjectExpirer(TestCase):
start_response('204 No Content', [('Content-Length', '0')])
return []
- internal_client.loadapp = lambda x: fake_app
+ internal_client.loadapp = lambda *a, **kw: fake_app
x = expirer.ObjectExpirer({})
ts = '1234'
@@ -649,7 +649,7 @@ class TestObjectExpirer(TestCase):
start_response('404 Not Found', [('Content-Length', '0')])
return []
- internal_client.loadapp = lambda x: fake_app
+ internal_client.loadapp = lambda *a, **kw: fake_app
x = expirer.ObjectExpirer({})
x.delete_actual_object('/path/to/object', '1234')
@@ -661,7 +661,7 @@ class TestObjectExpirer(TestCase):
[('Content-Length', '0')])
return []
- internal_client.loadapp = lambda x: fake_app
+ internal_client.loadapp = lambda *a, **kw: fake_app
x = expirer.ObjectExpirer({})
x.delete_actual_object('/path/to/object', '1234')
@@ -674,7 +674,7 @@ class TestObjectExpirer(TestCase):
[('Content-Length', '0')])
return []
- internal_client.loadapp = lambda x: fake_app
+ internal_client.loadapp = lambda *a, **kw: fake_app
x = expirer.ObjectExpirer({})
exc = None
@@ -692,7 +692,7 @@ class TestObjectExpirer(TestCase):
x = expirer.ObjectExpirer({})
x.swift.make_request = mock.MagicMock()
x.delete_actual_object(name, timestamp)
- x.swift.make_request.assert_called_once()
+ self.assertTrue(x.swift.make_request.called)
self.assertEqual(x.swift.make_request.call_args[0][1],
'/v1/' + urllib.quote(name))
diff --git a/test/unit/proxy/controllers/test_obj.py b/test/unit/proxy/controllers/test_obj.py
index aada616..4942691 100755
--- a/test/unit/proxy/controllers/test_obj.py
+++ b/test/unit/proxy/controllers/test_obj.py
@@ -22,7 +22,7 @@ import mock
import swift
from swift.proxy import server as proxy_server
from swift.common.swob import HTTPException
-from test.unit import FakeRing, FakeMemcache, fake_http_connect
+from test.unit import FakeRing, FakeMemcache, fake_http_connect, debug_logger
@contextmanager
@@ -90,26 +90,96 @@ class TestObjControllerWriteAffinity(unittest.TestCase):
class TestObjController(unittest.TestCase):
+ def setUp(self):
+ logger = debug_logger('proxy-server')
+ logger.thread_locals = ('txn1', '127.0.0.2')
+ self.app = proxy_server.Application(
+ None, FakeMemcache(), account_ring=FakeRing(),
+ container_ring=FakeRing(), object_ring=FakeRing(),
+ logger=logger)
+ self.controller = proxy_server.ObjectController(self.app,
+ 'a', 'c', 'o')
+ self.controller.container_info = mock.MagicMock(return_value={
+ 'partition': 1,
+ 'nodes': [
+ {'ip': '127.0.0.1', 'port': '1', 'device': 'sda'},
+ {'ip': '127.0.0.1', 'port': '2', 'device': 'sda'},
+ {'ip': '127.0.0.1', 'port': '3', 'device': 'sda'},
+ ],
+ 'write_acl': None,
+ 'read_acl': None,
+ 'sync_key': None,
+ 'versions': None})
+
+ def test_PUT_simple(self):
+ req = swift.common.swob.Request.blank('/v1/a/c/o')
+ req.headers['content-length'] = '0'
+ with set_http_connect(201, 201, 201):
+ resp = self.controller.PUT(req)
+ self.assertEquals(resp.status_int, 201)
+
+ def test_PUT_if_none_match(self):
+ req = swift.common.swob.Request.blank('/v1/a/c/o')
+ req.headers['if-none-match'] = '*'
+ req.headers['content-length'] = '0'
+ with set_http_connect(201, 201, 201):
+ resp = self.controller.PUT(req)
+ self.assertEquals(resp.status_int, 201)
+
+ def test_PUT_if_none_match_denied(self):
+ req = swift.common.swob.Request.blank('/v1/a/c/o')
+ req.headers['if-none-match'] = '*'
+ req.headers['content-length'] = '0'
+ with set_http_connect(201, (412, 412), 201):
+ resp = self.controller.PUT(req)
+ self.assertEquals(resp.status_int, 412)
+
+ def test_PUT_if_none_match_not_star(self):
+ req = swift.common.swob.Request.blank('/v1/a/c/o')
+ req.headers['if-none-match'] = 'somethingelse'
+ req.headers['content-length'] = '0'
+ with set_http_connect(201, 201, 201):
+ resp = self.controller.PUT(req)
+ self.assertEquals(resp.status_int, 400)
+
+ def test_GET_simple(self):
+ req = swift.common.swob.Request.blank('/v1/a/c/o')
+ with set_http_connect(200):
+ resp = self.controller.GET(req)
+ self.assertEquals(resp.status_int, 200)
+
+ def test_DELETE_simple(self):
+ req = swift.common.swob.Request.blank('/v1/a/c/o')
+ with set_http_connect(204, 204, 204):
+ resp = self.controller.DELETE(req)
+ self.assertEquals(resp.status_int, 204)
+
+ def test_POST_simple(self):
+ req = swift.common.swob.Request.blank('/v1/a/c/o')
+ with set_http_connect(200, 200, 200, 201, 201, 201):
+ resp = self.controller.POST(req)
+ self.assertEquals(resp.status_int, 202)
+
+ def test_COPY_simple(self):
+ req = swift.common.swob.Request.blank('/v1/a/c/o')
+ with set_http_connect(200, 200, 200, 201, 201, 201):
+ resp = self.controller.POST(req)
+ self.assertEquals(resp.status_int, 202)
+
+ def test_HEAD_simple(self):
+ req = swift.common.swob.Request.blank('/v1/a/c/o')
+ with set_http_connect(200, 200, 200, 201, 201, 201):
+ resp = self.controller.POST(req)
+ self.assertEquals(resp.status_int, 202)
def test_PUT_log_info(self):
# mock out enough to get to the area of the code we want to test
with mock.patch('swift.proxy.controllers.obj.check_object_creation',
mock.MagicMock(return_value=None)):
- app = mock.MagicMock()
- app.container_ring.get_nodes.return_value = (1, [2])
- app.object_ring.get_nodes.return_value = (1, [2])
- controller = proxy_server.ObjectController(app, 'a', 'c', 'o')
- controller.container_info = mock.MagicMock(return_value={
- 'partition': 1,
- 'nodes': [{}],
- 'write_acl': None,
- 'sync_key': None,
- 'versions': None})
- # and now test that we add the header to log_info
req = swift.common.swob.Request.blank('/v1/a/c/o')
req.headers['x-copy-from'] = 'somewhere'
try:
- controller.PUT(req)
+ self.controller.PUT(req)
except HTTPException:
pass
self.assertEquals(
@@ -119,7 +189,7 @@ class TestObjController(unittest.TestCase):
req.method = 'POST'
req.headers['x-copy-from'] = 'elsewhere'
try:
- controller.PUT(req)
+ self.controller.PUT(req)
except HTTPException:
pass
self.assertEquals(req.environ.get('swift.log_info'), None)
diff --git a/test/unit/proxy/test_server.py b/test/unit/proxy/test_server.py
index 0cb2278..1a59016 100644
--- a/test/unit/proxy/test_server.py
+++ b/test/unit/proxy/test_server.py
@@ -30,6 +30,7 @@ from urllib import quote
from hashlib import md5
from tempfile import mkdtemp
import weakref
+import re
import mock
from eventlet import sleep, spawn, wsgi, listen
@@ -60,7 +61,8 @@ from swift.proxy.controllers.base import get_container_memcache_key, \
get_account_memcache_key, cors_validation
import swift.proxy.controllers
from swift.common.request_helpers import get_sys_meta_prefix
-from swift.common.swob import Request, Response, HTTPUnauthorized
+from swift.common.swob import Request, Response, HTTPUnauthorized, \
+ HTTPException
# mocks
logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))
@@ -245,6 +247,7 @@ def set_http_connect(*args, **kwargs):
swift.proxy.controllers.obj.http_connect = new_connect
swift.proxy.controllers.account.http_connect = new_connect
swift.proxy.controllers.container.http_connect = new_connect
+ return new_connect
# tests
@@ -692,10 +695,10 @@ class TestObjectController(unittest.TestCase):
def setUp(self):
self.app = proxy_server.Application(None, FakeMemcache(),
+ logger=debug_logger('proxy-ut'),
account_ring=FakeRing(),
container_ring=FakeRing(),
object_ring=FakeRing())
- monkey_patch_mimetools()
def tearDown(self):
self.app.account_ring.set_replicas(3)
@@ -802,6 +805,7 @@ class TestObjectController(unittest.TestCase):
self.app.update_request(req)
self.app.memcache.store = {}
res = controller.PUT(req)
+ self.assertEqual(test_errors, [])
self.assertTrue(res.status.startswith('201 '))
def test_PUT_respects_write_affinity(self):
@@ -1609,7 +1613,7 @@ class TestObjectController(unittest.TestCase):
dev['ip'] = '127.0.0.1'
dev['port'] = 1
- class SlowBody():
+ class SlowBody(object):
def __init__(self):
self.sent = 0
@@ -1658,7 +1662,7 @@ class TestObjectController(unittest.TestCase):
dev['ip'] = '127.0.0.1'
dev['port'] = 1
- class SlowBody():
+ class SlowBody(object):
def __init__(self):
self.sent = 0
@@ -1693,7 +1697,7 @@ class TestObjectController(unittest.TestCase):
dev['port'] = 1
req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'GET'})
self.app.update_request(req)
- set_http_connect(200, 200, 200, slow=True)
+ set_http_connect(200, 200, 200, slow=0.1)
req.sent_size = 0
resp = req.get_response(self.app)
got_exc = False
@@ -1703,7 +1707,7 @@ class TestObjectController(unittest.TestCase):
got_exc = True
self.assert_(not got_exc)
self.app.recoverable_node_timeout = 0.1
- set_http_connect(200, 200, 200, slow=True)
+ set_http_connect(200, 200, 200, slow=1.0)
resp = req.get_response(self.app)
got_exc = False
try:
@@ -1718,16 +1722,17 @@ class TestObjectController(unittest.TestCase):
self.app.update_request(req)
self.app.recoverable_node_timeout = 0.1
- set_http_connect(200, 200, 200, slow=[3])
+ set_http_connect(200, 200, 200, slow=[1.0, 1.0, 1.0])
resp = req.get_response(self.app)
got_exc = False
try:
- resp.body
+ self.assertEquals('', resp.body)
except ChunkReadTimeout:
got_exc = True
self.assert_(got_exc)
- set_http_connect(200, 200, 200, body='lalala', slow=[2])
+ set_http_connect(200, 200, 200, body='lalala',
+ slow=[1.0, 1.0])
resp = req.get_response(self.app)
got_exc = False
try:
@@ -1736,8 +1741,8 @@ class TestObjectController(unittest.TestCase):
got_exc = True
self.assert_(not got_exc)
- set_http_connect(200, 200, 200, body='lalala', slow=[2],
- etags=['a', 'a', 'a'])
+ set_http_connect(200, 200, 200, body='lalala',
+ slow=[1.0, 1.0], etags=['a', 'a', 'a'])
resp = req.get_response(self.app)
got_exc = False
try:
@@ -1746,8 +1751,8 @@ class TestObjectController(unittest.TestCase):
got_exc = True
self.assert_(not got_exc)
- set_http_connect(200, 200, 200, body='lalala', slow=[2],
- etags=['a', 'b', 'a'])
+ set_http_connect(200, 200, 200, body='lalala',
+ slow=[1.0, 1.0], etags=['a', 'b', 'a'])
resp = req.get_response(self.app)
got_exc = False
try:
@@ -1757,8 +1762,8 @@ class TestObjectController(unittest.TestCase):
self.assert_(not got_exc)
req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'GET'})
- set_http_connect(200, 200, 200, body='lalala', slow=[2],
- etags=['a', 'b', 'b'])
+ set_http_connect(200, 200, 200, body='lalala',
+ slow=[1.0, 1.0], etags=['a', 'b', 'b'])
resp = req.get_response(self.app)
got_exc = False
try:
@@ -1787,17 +1792,17 @@ class TestObjectController(unittest.TestCase):
'Content-Type': 'text/plain'},
body=' ')
self.app.update_request(req)
- set_http_connect(200, 200, 201, 201, 201, slow=True)
+ set_http_connect(200, 200, 201, 201, 201, slow=0.1)
resp = req.get_response(self.app)
self.assertEquals(resp.status_int, 201)
self.app.node_timeout = 0.1
- set_http_connect(201, 201, 201, slow=True)
req = Request.blank('/v1/a/c/o',
environ={'REQUEST_METHOD': 'PUT'},
headers={'Content-Length': '4',
'Content-Type': 'text/plain'},
body=' ')
self.app.update_request(req)
+ set_http_connect(201, 201, 201, slow=1.0)
resp = req.get_response(self.app)
self.assertEquals(resp.status_int, 503)
@@ -2227,307 +2232,322 @@ class TestObjectController(unittest.TestCase):
resp = controller.PUT(req)
self.assertEquals(resp.status_int, 400)
- def test_copy_from(self):
+ @contextmanager
+ def controller_context(self, req, *args, **kwargs):
+ _v, account, container, obj = utils.split_path(req.path, 4, 4, True)
+ controller = proxy_server.ObjectController(self.app, account,
+ container, obj)
+ self.app.update_request(req)
+ self.app.memcache.store = {}
with save_globals():
- controller = proxy_server.ObjectController(self.app, 'account',
- 'container', 'object')
- # initial source object PUT
- req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
- headers={'Content-Length': '0'})
- self.app.update_request(req)
- set_http_connect(200, 200, 201, 201, 201)
- # acct cont obj obj obj
+ new_connect = set_http_connect(*args, **kwargs)
+ yield controller
+ unused_status_list = []
+ while True:
+ try:
+ unused_status_list.append(new_connect.code_iter.next())
+ except StopIteration:
+ break
+ if unused_status_list:
+ raise self.fail('UN-USED STATUS CODES: %r' %
+ unused_status_list)
+
+ def test_basic_put_with_x_copy_from(self):
+ req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
+ headers={'Content-Length': '0',
+ 'X-Copy-From': 'c/o'})
+ status_list = (200, 200, 200, 200, 200, 201, 201, 201)
+ # acct cont objc objc objc obj obj obj
+ with self.controller_context(req, *status_list) as controller:
resp = controller.PUT(req)
- self.assertEquals(resp.status_int, 201)
+ self.assertEquals(resp.status_int, 201)
+ self.assertEquals(resp.headers['x-copied-from'], 'c/o')
- # basic copy
- req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
- headers={'Content-Length': '0',
- 'X-Copy-From': 'c/o'})
- self.app.update_request(req)
- set_http_connect(200, 200, 200, 200, 200, 200, 200, 201, 201, 201)
- # acct cont acct cont objc objc objc obj obj obj
- self.app.memcache.store = {}
+ def test_basic_put_with_x_copy_from_across_container(self):
+ req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
+ headers={'Content-Length': '0',
+ 'X-Copy-From': 'c2/o'})
+ status_list = (200, 200, 200, 200, 200, 200, 201, 201, 201)
+ # acct cont conc objc objc objc obj obj obj
+ with self.controller_context(req, *status_list) as controller:
resp = controller.PUT(req)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(resp.headers['x-copied-from'], 'c/o')
+ self.assertEquals(resp.status_int, 201)
+ self.assertEquals(resp.headers['x-copied-from'], 'c2/o')
- # non-zero content length
- req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
- headers={'Content-Length': '5',
- 'X-Copy-From': 'c/o'})
- self.app.update_request(req)
- set_http_connect(200, 200, 200, 200, 200, 200, 200)
- # acct cont acct cont objc objc objc
- self.app.memcache.store = {}
+ def test_copy_non_zero_content_length(self):
+ req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
+ headers={'Content-Length': '5',
+ 'X-Copy-From': 'c/o'})
+ status_list = (200, 200)
+ # acct cont
+ with self.controller_context(req, *status_list) as controller:
resp = controller.PUT(req)
- self.assertEquals(resp.status_int, 400)
+ self.assertEquals(resp.status_int, 400)
- # extra source path parsing
- req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
- headers={'Content-Length': '0',
- 'X-Copy-From': 'c/o/o2'})
- req.account = 'a'
- set_http_connect(200, 200, 200, 200, 200, 200, 200, 201, 201, 201)
- # acct cont acct cont objc objc objc obj obj obj
- self.app.memcache.store = {}
+ def test_copy_with_slashes_in_x_copy_from(self):
+ # extra source path parsing
+ req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
+ headers={'Content-Length': '0',
+ 'X-Copy-From': 'c/o/o2'})
+ status_list = (200, 200, 200, 200, 200, 201, 201, 201)
+ # acct cont objc objc objc obj obj obj
+ with self.controller_context(req, *status_list) as controller:
resp = controller.PUT(req)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(resp.headers['x-copied-from'], 'c/o/o2')
+ self.assertEquals(resp.status_int, 201)
+ self.assertEquals(resp.headers['x-copied-from'], 'c/o/o2')
- # space in soure path
- req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
- headers={'Content-Length': '0',
- 'X-Copy-From': 'c/o%20o2'})
- req.account = 'a'
- set_http_connect(200, 200, 200, 200, 200, 200, 200, 201, 201, 201)
- # acct cont acct cont objc objc objc obj obj obj
- self.app.memcache.store = {}
+ def test_copy_with_spaces_in_x_copy_from(self):
+ # space in soure path
+ req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
+ headers={'Content-Length': '0',
+ 'X-Copy-From': 'c/o%20o2'})
+ status_list = (200, 200, 200, 200, 200, 201, 201, 201)
+ # acct cont objc objc objc obj obj obj
+ with self.controller_context(req, *status_list) as controller:
resp = controller.PUT(req)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(resp.headers['x-copied-from'], 'c/o%20o2')
+ self.assertEquals(resp.status_int, 201)
+ self.assertEquals(resp.headers['x-copied-from'], 'c/o%20o2')
- # repeat tests with leading /
- req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
- headers={'Content-Length': '0',
- 'X-Copy-From': '/c/o'})
- self.app.update_request(req)
- set_http_connect(200, 200, 200, 200, 200, 200, 200, 201, 201, 201)
- # acct cont acct cont objc objc objc obj obj obj
- self.app.memcache.store = {}
+ def test_copy_with_leading_slash_in_x_copy_from(self):
+ # repeat tests with leading /
+ req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
+ headers={'Content-Length': '0',
+ 'X-Copy-From': '/c/o'})
+ status_list = (200, 200, 200, 200, 200, 201, 201, 201)
+ # acct cont objc objc objc obj obj obj
+ with self.controller_context(req, *status_list) as controller:
resp = controller.PUT(req)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(resp.headers['x-copied-from'], 'c/o')
+ self.assertEquals(resp.status_int, 201)
+ self.assertEquals(resp.headers['x-copied-from'], 'c/o')
- req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
- headers={'Content-Length': '0',
- 'X-Copy-From': '/c/o/o2'})
- req.account = 'a'
- set_http_connect(200, 200, 200, 200, 200, 200, 200, 201, 201, 201)
- # acct cont acct cont objc objc objc obj obj obj
- self.app.memcache.store = {}
+ def test_copy_with_leading_slash_and_slashes_in_x_copy_from(self):
+ req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
+ headers={'Content-Length': '0',
+ 'X-Copy-From': '/c/o/o2'})
+ status_list = (200, 200, 200, 200, 200, 201, 201, 201)
+ # acct cont objc objc objc obj obj obj
+ with self.controller_context(req, *status_list) as controller:
resp = controller.PUT(req)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(resp.headers['x-copied-from'], 'c/o/o2')
+ self.assertEquals(resp.status_int, 201)
+ self.assertEquals(resp.headers['x-copied-from'], 'c/o/o2')
- # negative tests
+ def test_copy_with_no_object_in_x_copy_from(self):
+ req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
+ headers={'Content-Length': '0',
+ 'X-Copy-From': '/c'})
+ status_list = (200, 200)
+ # acct cont
+ with self.controller_context(req, *status_list) as controller:
+ try:
+ controller.PUT(req)
+ except HTTPException as resp:
+ self.assertEquals(resp.status_int // 100, 4) # client error
+ else:
+ raise self.fail('Invalid X-Copy-From did not raise '
+ 'client error')
- # invalid x-copy-from path
- req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
- headers={'Content-Length': '0',
- 'X-Copy-From': '/c'})
- self.app.update_request(req)
- self.app.memcache.store = {}
+ def test_copy_server_error_reading_source(self):
+ req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
+ headers={'Content-Length': '0',
+ 'X-Copy-From': '/c/o'})
+ status_list = (200, 200, 503, 503, 503)
+ # acct cont objc objc objc
+ with self.controller_context(req, *status_list) as controller:
resp = controller.PUT(req)
- self.assertEquals(resp.status_int // 100, 4) # client error
+ self.assertEquals(resp.status_int, 503)
- # server error
- req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
- headers={'Content-Length': '0',
- 'X-Copy-From': '/c/o'})
- self.app.update_request(req)
- set_http_connect(200, 200, 503, 503, 503)
- # acct cont objc objc objc
- self.app.memcache.store = {}
+ def test_copy_not_found_reading_source(self):
+ req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
+ headers={'Content-Length': '0',
+ 'X-Copy-From': '/c/o'})
+ # not found
+ status_list = (200, 200, 404, 404, 404)
+ # acct cont objc objc objc
+ with self.controller_context(req, *status_list) as controller:
resp = controller.PUT(req)
- self.assertEquals(resp.status_int, 503)
+ self.assertEquals(resp.status_int, 404)
- # not found
- req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
- headers={'Content-Length': '0',
- 'X-Copy-From': '/c/o'})
- self.app.update_request(req)
- set_http_connect(200, 200, 404, 404, 404)
- # acct cont objc objc objc
- self.app.memcache.store = {}
+ def test_copy_with_some_missing_sources(self):
+ req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
+ headers={'Content-Length': '0',
+ 'X-Copy-From': '/c/o'})
+ status_list = (200, 200, 404, 404, 200, 201, 201, 201)
+ # acct cont objc objc objc obj obj obj
+ with self.controller_context(req, *status_list) as controller:
resp = controller.PUT(req)
- self.assertEquals(resp.status_int, 404)
+ self.assertEquals(resp.status_int, 201)
- # some missing containers
- req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
- headers={'Content-Length': '0',
- 'X-Copy-From': '/c/o'})
- self.app.update_request(req)
- set_http_connect(200, 200, 404, 404, 200, 201, 201, 201)
- # acct cont objc objc objc obj obj obj
- self.app.memcache.store = {}
+ def test_copy_with_object_metadata(self):
+ req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
+ headers={'Content-Length': '0',
+ 'X-Copy-From': '/c/o',
+ 'X-Object-Meta-Ours': 'okay'})
+ # test object metadata
+ status_list = (200, 200, 200, 200, 200, 201, 201, 201)
+ # acct cont objc objc objc obj obj obj
+ with self.controller_context(req, *status_list) as controller:
resp = controller.PUT(req)
- self.assertEquals(resp.status_int, 201)
+ self.assertEquals(resp.status_int, 201)
+ self.assertEquals(resp.headers.get('x-object-meta-test'), 'testing')
+ self.assertEquals(resp.headers.get('x-object-meta-ours'), 'okay')
+ self.assertEquals(resp.headers.get('x-delete-at'), '9876543210')
- # test object meta data
- req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
- headers={'Content-Length': '0',
- 'X-Copy-From': '/c/o',
- 'X-Object-Meta-Ours': 'okay'})
- self.app.update_request(req)
- set_http_connect(200, 200, 200, 200, 200, 201, 201, 201)
- # acct cont objc objc objc obj obj obj
- self.app.memcache.store = {}
- resp = controller.PUT(req)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(resp.headers.get('x-object-meta-test'),
- 'testing')
- self.assertEquals(resp.headers.get('x-object-meta-ours'), 'okay')
- self.assertEquals(resp.headers.get('x-delete-at'), '9876543210')
+ def test_copy_source_larger_than_max_file_size(self):
+ req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
+ headers={'Content-Length': '0',
+ 'X-Copy-From': '/c/o'})
- # copy-from object is too large to fit in target object
- req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
- headers={'Content-Length': '0',
- 'X-Copy-From': '/c/o'})
- self.app.update_request(req)
+ # copy-from object is too large to fit in target object
+ class LargeResponseBody(object):
- class LargeResponseBody(object):
+ def __len__(self):
+ return MAX_FILE_SIZE + 1
- def __len__(self):
- return MAX_FILE_SIZE + 1
+ def __getitem__(self, key):
+ return ''
- def __getitem__(self, key):
- return ''
+ copy_from_obj_body = LargeResponseBody()
+ status_list = (200, 200, 200, 200, 200)
+ # acct cont objc objc objc
+ kwargs = dict(body=copy_from_obj_body)
+ with self.controller_context(req, *status_list,
+ **kwargs) as controller:
+ self.app.update_request(req)
- copy_from_obj_body = LargeResponseBody()
- set_http_connect(200, 200, 200, 200, 200, 201, 201, 201,
- body=copy_from_obj_body)
self.app.memcache.store = {}
resp = controller.PUT(req)
self.assertEquals(resp.status_int, 413)
- def test_COPY(self):
- with save_globals():
- controller = proxy_server.ObjectController(self.app, 'a', 'c', 'o')
- req = Request.blank('/v1/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
- headers={'Content-Length': '0'})
- req.account = 'a'
- set_http_connect(200, 200, 201, 201, 201)
- # acct cont obj obj obj
- resp = controller.PUT(req)
- self.assertEquals(resp.status_int, 201)
-
- req = Request.blank('/v1/a/c/o',
- environ={'REQUEST_METHOD': 'COPY'},
- headers={'Destination': 'c/o'})
- req.account = 'a'
- set_http_connect(200, 200, 200, 200, 200, 201, 201, 201, 200, 200)
- # acct cont acct cont objc objc objc obj obj obj
- self.app.memcache.store = {}
+ def test_basic_COPY(self):
+ req = Request.blank('/v1/a/c/o',
+ environ={'REQUEST_METHOD': 'COPY'},
+ headers={'Destination': 'c/o2'})
+ status_list = (200, 200, 200, 200, 200, 201, 201, 201)
+ # acct cont objc objc objc obj obj obj
+ with self.controller_context(req, *status_list) as controller:
resp = controller.COPY(req)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(resp.headers['x-copied-from'], 'c/o')
+ self.assertEquals(resp.status_int, 201)
+ self.assertEquals(resp.headers['x-copied-from'], 'c/o')
- req = Request.blank('/v1/a/c/o/o2',
- environ={'REQUEST_METHOD': 'COPY'},
- headers={'Destination': 'c/o'})
- req.account = 'a'
- controller.object_name = 'o/o2'
- set_http_connect(200, 200, 200, 200, 200, 201, 201, 201, 200, 200)
- # acct cont acct cont objc objc objc obj obj obj
- self.app.memcache.store = {}
+ def test_COPY_across_containers(self):
+ req = Request.blank('/v1/a/c/o',
+ environ={'REQUEST_METHOD': 'COPY'},
+ headers={'Destination': 'c2/o'})
+ status_list = (200, 200, 200, 200, 200, 200, 201, 201, 201)
+ # acct cont c2 objc objc objc obj obj obj
+ with self.controller_context(req, *status_list) as controller:
resp = controller.COPY(req)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(resp.headers['x-copied-from'], 'c/o/o2')
-
- req = Request.blank('/v1/a/c/o',
- environ={'REQUEST_METHOD': 'COPY'},
- headers={'Destination': '/c/o'})
- req.account = 'a'
- controller.object_name = 'o'
- set_http_connect(200, 200, 200, 200, 200, 201, 201, 201, 200, 200)
- # acct cont acct cont objc objc objc obj obj obj
- self.app.memcache.store = {}
+ self.assertEquals(resp.status_int, 201)
+ self.assertEquals(resp.headers['x-copied-from'], 'c/o')
+
+ def test_COPY_source_with_slashes_in_name(self):
+ req = Request.blank('/v1/a/c/o/o2',
+ environ={'REQUEST_METHOD': 'COPY'},
+ headers={'Destination': 'c/o'})
+ status_list = (200, 200, 200, 200, 200, 201, 201, 201)
+ # acct cont objc objc objc obj obj obj
+ with self.controller_context(req, *status_list) as controller:
resp = controller.COPY(req)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(resp.headers['x-copied-from'], 'c/o')
+ self.assertEquals(resp.status_int, 201)
+ self.assertEquals(resp.headers['x-copied-from'], 'c/o/o2')
- req = Request.blank('/v1/a/c/o/o2',
- environ={'REQUEST_METHOD': 'COPY'},
- headers={'Destination': '/c/o'})
- req.account = 'a'
- controller.object_name = 'o/o2'
- set_http_connect(200, 200, 200, 200, 200, 201, 201, 201, 200, 200)
- # acct cont acct cont objc objc objc obj obj obj
- self.app.memcache.store = {}
+ def test_COPY_destination_leading_slash(self):
+ req = Request.blank('/v1/a/c/o',
+ environ={'REQUEST_METHOD': 'COPY'},
+ headers={'Destination': '/c/o'})
+ status_list = (200, 200, 200, 200, 200, 201, 201, 201)
+ # acct cont objc objc objc obj obj obj
+ with self.controller_context(req, *status_list) as controller:
resp = controller.COPY(req)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(resp.headers['x-copied-from'], 'c/o/o2')
+ self.assertEquals(resp.status_int, 201)
+ self.assertEquals(resp.headers['x-copied-from'], 'c/o')
+
+ def test_COPY_source_with_slashes_destination_leading_slash(self):
+ req = Request.blank('/v1/a/c/o/o2',
+ environ={'REQUEST_METHOD': 'COPY'},
+ headers={'Destination': '/c/o'})
+ status_list = (200, 200, 200, 200, 200, 201, 201, 201)
+ # acct cont objc objc objc obj obj obj
+ with self.controller_context(req, *status_list) as controller:
+ resp = controller.COPY(req)
+ self.assertEquals(resp.status_int, 201)
+ self.assertEquals(resp.headers['x-copied-from'], 'c/o/o2')
- req = Request.blank('/v1/a/c/o',
- environ={'REQUEST_METHOD': 'COPY'},
- headers={'Destination': 'c_o'})
- req.account = 'a'
- controller.object_name = 'o'
- set_http_connect(200, 200)
- # acct cont
- self.app.memcache.store = {}
+ def test_COPY_no_object_in_destination(self):
+ req = Request.blank('/v1/a/c/o',
+ environ={'REQUEST_METHOD': 'COPY'},
+ headers={'Destination': 'c_o'})
+ status_list = [] # no requests needed
+ with self.controller_context(req, *status_list) as controller:
resp = controller.COPY(req)
- self.assertEquals(resp.status_int, 412)
+ self.assertEquals(resp.status_int, 412)
- req = Request.blank('/v1/a/c/o',
- environ={'REQUEST_METHOD': 'COPY'},
- headers={'Destination': '/c/o'})
- req.account = 'a'
- controller.object_name = 'o'
- set_http_connect(200, 200, 503, 503, 503)
- # acct cont objc objc objc
- self.app.memcache.store = {}
+ def test_COPY_server_error_reading_source(self):
+ req = Request.blank('/v1/a/c/o',
+ environ={'REQUEST_METHOD': 'COPY'},
+ headers={'Destination': '/c/o'})
+ status_list = (200, 200, 503, 503, 503)
+ # acct cont objc objc objc
+ with self.controller_context(req, *status_list) as controller:
resp = controller.COPY(req)
- self.assertEquals(resp.status_int, 503)
+ self.assertEquals(resp.status_int, 503)
- req = Request.blank('/v1/a/c/o',
- environ={'REQUEST_METHOD': 'COPY'},
- headers={'Destination': '/c/o'})
- req.account = 'a'
- controller.object_name = 'o'
- set_http_connect(200, 200, 404, 404, 404)
- # acct cont objc objc objc
- self.app.memcache.store = {}
+ def test_COPY_not_found_reading_source(self):
+ req = Request.blank('/v1/a/c/o',
+ environ={'REQUEST_METHOD': 'COPY'},
+ headers={'Destination': '/c/o'})
+ status_list = (200, 200, 404, 404, 404)
+ # acct cont objc objc objc
+ with self.controller_context(req, *status_list) as controller:
resp = controller.COPY(req)
- self.assertEquals(resp.status_int, 404)
+ self.assertEquals(resp.status_int, 404)
- req = Request.blank('/v1/a/c/o',
- environ={'REQUEST_METHOD': 'COPY'},
- headers={'Destination': '/c/o'})
- req.account = 'a'
- controller.object_name = 'o'
- set_http_connect(200, 200, 404, 404, 200, 201, 201, 201)
- # acct cont objc objc objc obj obj obj
- self.app.memcache.store = {}
+ def test_COPY_with_some_missing_sources(self):
+ req = Request.blank('/v1/a/c/o',
+ environ={'REQUEST_METHOD': 'COPY'},
+ headers={'Destination': '/c/o'})
+ status_list = (200, 200, 404, 404, 200, 201, 201, 201)
+ # acct cont objc objc objc obj obj obj
+ with self.controller_context(req, *status_list) as controller:
resp = controller.COPY(req)
- self.assertEquals(resp.status_int, 201)
+ self.assertEquals(resp.status_int, 201)
- req = Request.blank('/v1/a/c/o',
- environ={'REQUEST_METHOD': 'COPY'},
- headers={'Destination': '/c/o',
- 'X-Object-Meta-Ours': 'okay'})
- req.account = 'a'
- controller.object_name = 'o'
- set_http_connect(200, 200, 200, 200, 200, 201, 201, 201)
- # acct cont objc objc objc obj obj obj
- self.app.memcache.store = {}
+ def test_COPY_with_metadata(self):
+ req = Request.blank('/v1/a/c/o',
+ environ={'REQUEST_METHOD': 'COPY'},
+ headers={'Destination': '/c/o',
+ 'X-Object-Meta-Ours': 'okay'})
+ status_list = (200, 200, 200, 200, 200, 201, 201, 201)
+ # acct cont objc objc objc obj obj obj
+ with self.controller_context(req, *status_list) as controller:
resp = controller.COPY(req)
- self.assertEquals(resp.status_int, 201)
- self.assertEquals(resp.headers.get('x-object-meta-test'),
- 'testing')
- self.assertEquals(resp.headers.get('x-object-meta-ours'), 'okay')
- self.assertEquals(resp.headers.get('x-delete-at'), '9876543210')
+ self.assertEquals(resp.status_int, 201)
+ self.assertEquals(resp.headers.get('x-object-meta-test'),
+ 'testing')
+ self.assertEquals(resp.headers.get('x-object-meta-ours'), 'okay')
+ self.assertEquals(resp.headers.get('x-delete-at'), '9876543210')
- req = Request.blank('/v1/a/c/o',
- environ={'REQUEST_METHOD': 'COPY'},
- headers={'Destination': '/c/o'})
- self.app.update_request(req)
+ def test_COPY_source_larger_than_max_file_size(self):
+ req = Request.blank('/v1/a/c/o',
+ environ={'REQUEST_METHOD': 'COPY'},
+ headers={'Destination': '/c/o'})
- class LargeResponseBody(object):
+ class LargeResponseBody(object):
- def __len__(self):
- return MAX_FILE_SIZE + 1
+ def __len__(self):
+ return MAX_FILE_SIZE + 1
- def __getitem__(self, key):
- return ''
+ def __getitem__(self, key):
+ return ''
- copy_from_obj_body = LargeResponseBody()
- set_http_connect(200, 200, 200, 200, 200, 201, 201, 201,
- body=copy_from_obj_body)
- self.app.memcache.store = {}
+ copy_from_obj_body = LargeResponseBody()
+ status_list = (200, 200, 200, 200, 200)
+ # acct cont objc objc objc
+ kwargs = dict(body=copy_from_obj_body)
+ with self.controller_context(req, *status_list,
+ **kwargs) as controller:
resp = controller.COPY(req)
- self.assertEquals(resp.status_int, 413)
+ self.assertEquals(resp.status_int, 413)
def test_COPY_newest(self):
with save_globals():
@@ -2574,7 +2594,7 @@ class TestObjectController(unittest.TestCase):
def test_chunked_put(self):
- class ChunkedFile():
+ class ChunkedFile(object):
def __init__(self, bytes):
self.bytes = bytes
@@ -3824,9 +3844,7 @@ class TestObjectController(unittest.TestCase):
req.content_length = 0
resp = controller.OPTIONS(req)
self.assertEquals(200, resp.status_int)
- self.assertEquals(
- 'https://bar.baz',
- resp.headers['access-control-allow-origin'])
+ self.assertEquals('*', resp.headers['access-control-allow-origin'])
for verb in 'OPTIONS COPY GET POST PUT DELETE HEAD'.split():
self.assertTrue(
verb in resp.headers['access-control-allow-methods'])
@@ -3842,10 +3860,11 @@ class TestObjectController(unittest.TestCase):
def stubContainerInfo(*args):
return {
'cors': {
- 'allow_origin': 'http://foo.bar'
+ 'allow_origin': 'http://not.foo.bar'
}
}
controller.container_info = stubContainerInfo
+ controller.app.strict_cors_mode = False
def objectGET(controller, req):
return Response(headers={
@@ -3876,6 +3895,50 @@ class TestObjectController(unittest.TestCase):
'x-trans-id', 'x-object-meta-color'])
self.assertEquals(expected_exposed, exposed)
+ controller.app.strict_cors_mode = True
+ req = Request.blank(
+ '/v1/a/c/o.jpg',
+ {'REQUEST_METHOD': 'GET'},
+ headers={'Origin': 'http://foo.bar'})
+
+ resp = cors_validation(objectGET)(controller, req)
+
+ self.assertEquals(200, resp.status_int)
+ self.assertTrue('access-control-allow-origin' not in resp.headers)
+
+ def test_CORS_valid_with_obj_headers(self):
+ with save_globals():
+ controller = proxy_server.ObjectController(self.app, 'a', 'c', 'o')
+
+ def stubContainerInfo(*args):
+ return {
+ 'cors': {
+ 'allow_origin': 'http://foo.bar'
+ }
+ }
+ controller.container_info = stubContainerInfo
+
+ def objectGET(controller, req):
+ return Response(headers={
+ 'X-Object-Meta-Color': 'red',
+ 'X-Super-Secret': 'hush',
+ 'Access-Control-Allow-Origin': 'http://obj.origin',
+ 'Access-Control-Expose-Headers': 'x-trans-id'
+ })
+
+ req = Request.blank(
+ '/v1/a/c/o.jpg',
+ {'REQUEST_METHOD': 'GET'},
+ headers={'Origin': 'http://foo.bar'})
+
+ resp = cors_validation(objectGET)(controller, req)
+
+ self.assertEquals(200, resp.status_int)
+ self.assertEquals('http://obj.origin',
+ resp.headers['access-control-allow-origin'])
+ self.assertEquals('x-trans-id',
+ resp.headers['access-control-expose-headers'])
+
def _gather_x_container_headers(self, controller_call, req, *connect_args,
**kwargs):
header_list = kwargs.pop('header_list', ['X-Container-Device',
@@ -4092,7 +4155,8 @@ class TestContainerController(unittest.TestCase):
self.app = proxy_server.Application(None, FakeMemcache(),
account_ring=FakeRing(),
container_ring=FakeRing(),
- object_ring=FakeRing())
+ object_ring=FakeRing(),
+ logger=FakeLogger())
def test_transfer_headers(self):
src_headers = {'x-remove-versions-location': 'x',
@@ -4843,9 +4907,7 @@ class TestContainerController(unittest.TestCase):
req.content_length = 0
resp = controller.OPTIONS(req)
self.assertEquals(200, resp.status_int)
- self.assertEquals(
- 'https://bar.baz',
- resp.headers['access-control-allow-origin'])
+ self.assertEquals('*', resp.headers['access-control-allow-origin'])
for verb in 'OPTIONS GET POST PUT DELETE HEAD'.split():
self.assertTrue(
verb in resp.headers['access-control-allow-methods'])
@@ -5016,11 +5078,60 @@ class TestContainerController(unittest.TestCase):
'X-Account-Device': 'sdc'}
])
+ def test_PUT_backed_x_timestamp_header(self):
+ timestamps = []
+
+ def capture_timestamps(*args, **kwargs):
+ headers = kwargs['headers']
+ timestamps.append(headers.get('X-Timestamp'))
+
+ req = Request.blank('/v1/a/c', method='PUT', headers={'': ''})
+ with save_globals():
+ new_connect = set_http_connect(200, # account existance check
+ 201, 201, 201,
+ give_connect=capture_timestamps)
+ resp = self.app.handle_request(req)
+
+ # sanity
+ self.assertRaises(StopIteration, new_connect.code_iter.next)
+ self.assertEqual(2, resp.status_int // 100)
+
+ timestamps.pop(0) # account existance check
+ self.assertEqual(3, len(timestamps))
+ for timestamp in timestamps:
+ self.assertEqual(timestamp, timestamps[0])
+ self.assert_(re.match('[0-9]{10}\.[0-9]{5}', timestamp))
+
+ def test_DELETE_backed_x_timestamp_header(self):
+ timestamps = []
+
+ def capture_timestamps(*args, **kwargs):
+ headers = kwargs['headers']
+ timestamps.append(headers.get('X-Timestamp'))
+
+ req = Request.blank('/v1/a/c', method='DELETE', headers={'': ''})
+ self.app.update_request(req)
+ with save_globals():
+ new_connect = set_http_connect(200, # account existance check
+ 201, 201, 201,
+ give_connect=capture_timestamps)
+ resp = self.app.handle_request(req)
+
+ # sanity
+ self.assertRaises(StopIteration, new_connect.code_iter.next)
+ self.assertEqual(2, resp.status_int // 100)
+
+ timestamps.pop(0) # account existance check
+ self.assertEqual(3, len(timestamps))
+ for timestamp in timestamps:
+ self.assertEqual(timestamp, timestamps[0])
+ self.assert_(re.match('[0-9]{10}\.[0-9]{5}', timestamp))
+
def test_node_read_timeout_retry_to_container(self):
with save_globals():
req = Request.blank('/v1/a/c', environ={'REQUEST_METHOD': 'GET'})
self.app.node_timeout = 0.1
- set_http_connect(200, 200, 200, body='abcdef', slow=[2])
+ set_http_connect(200, 200, 200, body='abcdef', slow=[1.0, 1.0])
resp = req.get_response(self.app)
got_exc = False
try:
@@ -5547,6 +5658,143 @@ class TestAccountControllerFakeGetResponse(unittest.TestCase):
resp = req.get_response(self.app)
self.assertEqual(400, resp.status_int)
+ def test_account_acl_header_access(self):
+ acl = {
+ 'admin': ['AUTH_alice'],
+ 'read-write': ['AUTH_bob'],
+ 'read-only': ['AUTH_carol'],
+ }
+ prefix = get_sys_meta_prefix('account')
+ privileged_headers = {(prefix + 'core-access-control'): format_acl(
+ version=2, acl_dict=acl)}
+
+ app = proxy_server.Application(
+ None, FakeMemcache(), account_ring=FakeRing(),
+ container_ring=FakeRing(), object_ring=FakeRing())
+
+ with save_globals():
+ # Mock account server will provide privileged information (ACLs)
+ set_http_connect(200, 200, 200, headers=privileged_headers)
+ req = Request.blank('/v1/a', environ={'REQUEST_METHOD': 'GET'})
+ resp = app.handle_request(req)
+
+ # Not a swift_owner -- ACLs should NOT be in response
+ header = 'X-Account-Access-Control'
+ self.assert_(header not in resp.headers, '%r was in %r' % (
+ header, resp.headers))
+
+ # Same setup -- mock acct server will provide ACLs
+ set_http_connect(200, 200, 200, headers=privileged_headers)
+ req = Request.blank('/v1/a', environ={'REQUEST_METHOD': 'GET',
+ 'swift_owner': True})
+ resp = app.handle_request(req)
+
+ # For a swift_owner, the ACLs *should* be in response
+ self.assert_(header in resp.headers, '%r not in %r' % (
+ header, resp.headers))
+
+ def test_account_acls_through_delegation(self):
+
+ # Define a way to grab the requests sent out from the AccountController
+ # to the Account Server, and a way to inject responses we'd like the
+ # Account Server to return.
+ resps_to_send = []
+
+ @contextmanager
+ def patch_account_controller_method(verb):
+ old_method = getattr(proxy_server.AccountController, verb)
+ new_method = lambda self, req, *_, **__: resps_to_send.pop(0)
+ try:
+ setattr(proxy_server.AccountController, verb, new_method)
+ yield
+ finally:
+ setattr(proxy_server.AccountController, verb, old_method)
+
+ def make_test_request(http_method, swift_owner=True):
+ env = {
+ 'REQUEST_METHOD': http_method,
+ 'swift_owner': swift_owner,
+ }
+ acl = {
+ 'admin': ['foo'],
+ 'read-write': ['bar'],
+ 'read-only': ['bas'],
+ }
+ headers = {} if http_method in ('GET', 'HEAD') else {
+ 'x-account-access-control': format_acl(version=2, acl_dict=acl)
+ }
+
+ return Request.blank('/v1/a', environ=env, headers=headers)
+
+ # Our AccountController will invoke methods to communicate with the
+ # Account Server, and they will return responses like these:
+ def make_canned_response(http_method):
+ acl = {
+ 'admin': ['foo'],
+ 'read-write': ['bar'],
+ 'read-only': ['bas'],
+ }
+ headers = {'x-account-sysmeta-core-access-control': format_acl(
+ version=2, acl_dict=acl)}
+ canned_resp = Response(headers=headers)
+ canned_resp.environ = {
+ 'PATH_INFO': '/acct',
+ 'REQUEST_METHOD': http_method,
+ }
+ resps_to_send.append(canned_resp)
+
+ app = proxy_server.Application(
+ None, FakeMemcache(), account_ring=FakeRing(),
+ container_ring=FakeRing(), object_ring=FakeRing())
+ app.allow_account_management = True
+
+ ext_header = 'x-account-access-control'
+ with patch_account_controller_method('GETorHEAD_base'):
+ # GET/HEAD requests should remap sysmeta headers from acct server
+ for verb in ('GET', 'HEAD'):
+ make_canned_response(verb)
+ req = make_test_request(verb)
+ resp = app.handle_request(req)
+ h = parse_acl(version=2, data=resp.headers.get(ext_header))
+ self.assertEqual(h['admin'], ['foo'])
+ self.assertEqual(h['read-write'], ['bar'])
+ self.assertEqual(h['read-only'], ['bas'])
+
+ # swift_owner = False: GET/HEAD shouldn't return sensitive info
+ make_canned_response(verb)
+ req = make_test_request(verb, swift_owner=False)
+ resp = app.handle_request(req)
+ h = resp.headers
+ self.assertEqual(None, h.get(ext_header))
+
+ # swift_owner unset: GET/HEAD shouldn't return sensitive info
+ make_canned_response(verb)
+ req = make_test_request(verb, swift_owner=False)
+ del req.environ['swift_owner']
+ resp = app.handle_request(req)
+ h = resp.headers
+ self.assertEqual(None, h.get(ext_header))
+
+ # Verify that PUT/POST requests remap sysmeta headers from acct server
+ with patch_account_controller_method('make_requests'):
+ make_canned_response('PUT')
+ req = make_test_request('PUT')
+ resp = app.handle_request(req)
+
+ h = parse_acl(version=2, data=resp.headers.get(ext_header))
+ self.assertEqual(h['admin'], ['foo'])
+ self.assertEqual(h['read-write'], ['bar'])
+ self.assertEqual(h['read-only'], ['bas'])
+
+ make_canned_response('POST')
+ req = make_test_request('POST')
+ resp = app.handle_request(req)
+
+ h = parse_acl(version=2, data=resp.headers.get(ext_header))
+ self.assertEqual(h['admin'], ['foo'])
+ self.assertEqual(h['read-write'], ['bar'])
+ self.assertEqual(h['read-only'], ['bas'])
+
class FakeObjectController(object):
diff --git a/tools/gswauth_functional_tests.sh b/tools/gswauth_functional_tests.sh
index dbe2248..287a20a 100755
--- a/tools/gswauth_functional_tests.sh
+++ b/tools/gswauth_functional_tests.sh
@@ -50,7 +50,7 @@ quit()
fail()
{
cleanup
- quit "$1"
+ quit "$1"
}
run_generic_tests()
@@ -66,9 +66,9 @@ run_generic_tests()
nosetests -v --exe \
--with-xunit \
- --xunit-file functional_tests/gluster-swift-gswauth-generic-functional-TC-report.xml \
+ --xunit-file functional_tests_result/gluster-swift-gswauth-generic-functional-TC-report.xml \
--with-html-output \
- --html-out-file functional_tests/gluster-swift-gswauth-generic-functional-result.html \
+ --html-out-file functional_tests_result/gluster-swift-gswauth-generic-functional-result.html \
test/functional || fail "Functional tests failed"
}
@@ -91,6 +91,7 @@ export SWIFT_TEST_CONFIG_FILE=/etc/swift/test.conf
# Install the configuration files
sudo mkdir /etc/swift > /dev/null 2>&1
+sudo cp -r test/functional_auth/common_conf/* /etc/swift || fail "Unable to copy configuration files to /etc/swift"
sudo cp -r test/functional_auth/gswauth/conf/* /etc/swift || fail "Unable to copy configuration files to /etc/swift"
sudo_env gluster-swift-gen-builders test test2 gsmetadata || fail "Unable to create ring files"
@@ -101,12 +102,12 @@ sudo_env swift-init main start || fail "Unable to start swift"
#swauth-prep
sudo_env gswauth-prep -K gswauthkey || fail "Unable to prep gswauth"
-mkdir functional_tests > /dev/null 2>&1
+mkdir functional_tests_result > /dev/null 2>&1
nosetests -v --exe \
--with-xunit \
- --xunit-file functional_tests/gluster-swift-gswauth-functional-TC-report.xml \
+ --xunit-file functional_tests_result/gluster-swift-gswauth-functional-TC-report.xml \
--with-html-output \
- --html-out-file functional_tests/gluster-swift-gswauth-functional-result.html \
+ --html-out-file functional_tests_result/gluster-swift-gswauth-functional-result.html \
test/functional_auth/gswauth || fail "Functional gswauth test failed"
run_generic_tests
diff --git a/tools/keystone_functional_tests.sh b/tools/keystone_functional_tests.sh
index 620bcc7..b5aa25a 100755
--- a/tools/keystone_functional_tests.sh
+++ b/tools/keystone_functional_tests.sh
@@ -87,15 +87,15 @@ sudo_env gluster-swift-gen-builders $accounts || fail "Unable to create ring fil
sudo service memcached start || fail "Unable to start memcached"
sudo_env swift-init main start || fail "Unable to start swift"
-mkdir functional_tests > /dev/null 2>&1
+mkdir functional_tests_result > /dev/null 2>&1
echo "== Keystone: Generic Functional Tests =="
nosetests -v --exe \
--with-xunit \
- --xunit-file functional_tests/gluster-swift-keystone-generic-functional-TC-report.xml \
+ --xunit-file functional_tests_result/gluster-swift-keystone-generic-functional-TC-report.xml \
--with-html-output \
- --html-out-file functional_tests/gluster-swift-keystone-generic-functional-result.html \
+ --html-out-file functional_tests_result/gluster-swift-keystone-generic-functional-result.html \
test/functional || fail "Functional tests failed"
cleanup
diff --git a/tools/swkrbath_functional_tests.sh b/tools/object_expirer_functional.sh
index 9995f1d..2578619 100755
--- a/tools/swkrbath_functional_tests.sh
+++ b/tools/object_expirer_functional.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-# Copyright (c) 2014 Red Hat, Inc.
+# Copyright (c) 2013 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -28,69 +28,60 @@ cleanup()
sudo service memcached stop
sudo_env swift-init main stop
sudo rm -rf /etc/swift > /dev/null 2>&1
- for acct in /mnt/gluster-object/* ; do
- sudo rm -rf /mnt/gluster-object/${acct}/* > /dev/null 2>&1
- sudo setfattr -x user.swift.metadata /mnt/gluster-object/${acct} > /dev/null 2>&1
- done
+ sudo rm -rf /mnt/gluster-object/test{,2}/* > /dev/null 2>&1
+ sudo rm -rf /mnt/gluster-object/gsexpiring/* > /dev/null 2>&1
+ sudo setfattr -x user.swift.metadata /mnt/gluster-object/test{,2} > /dev/null 2>&1
}
quit()
{
- echo "$1"
- exit 1
+ echo "$1"
+ exit 1
}
fail()
{
- cleanup
- quit "$1"
+ cleanup
+ quit "$1"
}
### MAIN ###
# Only run if there is no configuration in the system
if [ -x /etc/swift ] ; then
- quit "/etc/swift exists, cannot run functional tests."
+ quit "/etc/swift exists, cannot run functional tests."
fi
# Check the directories exist
-DIRS="/mnt/gluster-object /mnt/gluster-object/test /mnt/gluster-object/test2 /mnt/gluster-object/gsmetadata"
+DIRS="/mnt/gluster-object /mnt/gluster-object/test /mnt/gluster-object/test2 /mnt/gluster-object/gsexpiring"
for d in $DIRS ; do
- if [ ! -x $d ] ; then
- quit "$d must exist on an XFS or GlusterFS volume"
- fi
+ if [ ! -x $d ] ; then
+ quit "$d must exist on an XFS or GlusterFS volume"
+ fi
done
export SWIFT_TEST_CONFIG_FILE=/etc/swift/test.conf
# Install the configuration files
sudo mkdir /etc/swift > /dev/null 2>&1
-sudo cp -r test/functional_auth/swiftkerbauth/conf/* /etc/swift || fail "Unable to copy configuration files to /etc/swift"
+sudo cp -r test/functional_auth/common_conf/* /etc/swift || fail "Unable to copy configuration files to /etc/swift"
+sudo cp -r test/functional_auth/tempauth/conf/* /etc/swift || fail "Unable to copy configuration files to /etc/swift"
+sudo_env gluster-swift-gen-builders gsexpiring test || fail "Unable to create ring files"
-# Create the ring files
-accounts=""
-for acct in /mnt/gluster-object/* ; do
- acct=`basename $acct`
- accounts="$acct $accounts"
-done
-sudo_env gluster-swift-gen-builders $accounts || fail "Unable to create ring files"
+# Remove old files
+sudo rm -rf /mnt/gluster-object/test* > /dev/null 2>&1
+sudo rm -rf /mnt/gluster-object/gsexpiring/* > /dev/null 2>&1
# Start the services
sudo service memcached start || fail "Unable to start memcached"
sudo_env swift-init main start || fail "Unable to start swift"
-mkdir functional_tests > /dev/null 2>&1
-
-echo "== SwiftKerbAuth: Functional Tests =="
-
+echo "Running functional tests with tempauth"
+mkdir functional_tests > /dev/null 2>&1
nosetests -v --exe \
- --with-xunit \
- --xunit-file functional_tests/gluster-swift-swiftkerbauth-generic-functional-TC-report.xml \
- --with-html-output \
- --html-out-file functional_tests/gluster-swift-swiftkerbauth-generic-functional-result.html \
- test/functional_auth/swiftkerbauth || fail "Functional tests failed"
+ test/object_expirer_functional || fail "Object expirer functional tests failed"
cleanup
exit 0
diff --git a/tools/functional_tests.sh b/tools/tempauth_functional_tests.sh
index 94d5d9b..f237f3e 100755
--- a/tools/functional_tests.sh
+++ b/tools/tempauth_functional_tests.sh
@@ -64,6 +64,7 @@ export SWIFT_TEST_CONFIG_FILE=/etc/swift/test.conf
# Install the configuration files
sudo mkdir /etc/swift > /dev/null 2>&1
+sudo cp -r test/functional_auth/common_conf/* /etc/swift || fail "Unable to copy configuration files to /etc/swift"
sudo cp -r test/functional_auth/tempauth/conf/* /etc/swift || fail "Unable to copy configuration files to /etc/swift"
sudo_env gluster-swift-gen-builders test test2 || fail "Unable to create ring files"
@@ -71,13 +72,15 @@ sudo_env gluster-swift-gen-builders test test2 || fail "Unable to create ring fi
sudo service memcached start || fail "Unable to start memcached"
sudo_env swift-init main start || fail "Unable to start swift"
+echo "Running functional tests with tempauth"
+
mkdir functional_tests > /dev/null 2>&1
nosetests -v --exe \
--with-xunit \
--xunit-file functional_tests/gluster-swift-generic-functional-TC-report.xml \
- --with-html-output \
- --html-out-file functional_tests/gluster-swift-generic-functional-result.html \
- test/functional || fail "Functional tests failed"
+ --with-html-output \
+ --html-out-file functional_tests/gluster-swift-generic-functional-result.html \
+ test/functional || fail "Functional tests failed"
cleanup
exit 0
diff --git a/tox.ini b/tox.ini
index a00a36a..1942c26 100644
--- a/tox.ini
+++ b/tox.ini
@@ -14,45 +14,47 @@ setenv = VIRTUAL_ENV={envdir}
NOSE_OPENSTACK_YELLOW=0.025
NOSE_OPENSTACK_SHOW_ELAPSED=1
NOSE_OPENSTACK_STDOUT=1
+ NOSE_WITH_COVERAGE=1
+ NOSE_COVER_BRANCHES=1
+ NOSE_COVER_PACKAGE=gluster
deps =
- https://launchpad.net/gluster-swift/icehouse/1.13.0/+download/swift-1.13.0.tar.gz
- --download-cache={homedir}/.pipcache
- -r{toxinidir}/tools/test-requires
- -r{toxinidir}/tools/requirements.txt
+ git+https://github.com/openstack/swift.git@icehouse-eol
+ -r{toxinidir}/requirements.txt
+ -r{toxinidir}/test-requirements.txt
+# Just having testtools package installed fixes some dependency issue
+# https://github.com/swiftstack/vagrant-swift-all-in-one/issues/19
+# pip needs a good dependency resolver :/
+ testtools
+ flake8
changedir = {toxinidir}/test/unit
-commands = nosetests -v --exe --with-xunit --with-coverage --cover-package gluster --cover-erase --cover-xml --cover-html --cover-branches --with-html-output {posargs}
+commands = nosetests -v {posargs}
+
+[testenv:cover]
+setenv = VIRTUAL_ENV={envdir}
+ NOSE_WITH_COVERAGE=1
+ NOSE_COVER_BRANCHES=1
+ NOSE_COVER_HTML=1
+ NOSE_COVER_HTML_DIR={toxinidir}/cover
[tox:jenkins]
downloadcache = ~/cache/pip
[testenv:functest]
changedir = {toxinidir}
-commands = bash tools/functional_tests.sh
- bash tools/gswauth_functional_tests.sh
+commands = bash ./.functests
[testenv:ksfunctest]
changedir = {toxinidir}
commands = bash tools/keystone_functional_tests.sh
-[testenv:swfunctest]
-changedir = {toxinidir}
-commands = bash tools/swkrbath_functional_tests.sh
-
[testenv:pep8]
-deps =
- --download-cache={homedir}/.pipcache
- -r{toxinidir}/tools/test-requires
changedir = {toxinidir}
commands =
- flake8
- flake8 gluster test
-
-[testenv:cover]
-setenv = NOSE_WITH_COVERAGE=1
+ flake8 gluster test setup.py
[testenv:venv]
-commands =
changedir = {toxinidir}
+commands = {posargs}
[testenv:run]
changedir = {toxinidir}