Updated version to 1.1.6.
[zs3.git] / acl.lisp
blob9be365a9e41a16647f753920920973cfbce1e7e6
1 ;;;;
2 ;;;; Copyright (c) 2008 Zachary Beane, All Rights Reserved
3 ;;;;
4 ;;;; Redistribution and use in source and binary forms, with or without
5 ;;;; modification, are permitted provided that the following conditions
6 ;;;; are met:
7 ;;;;
8 ;;;; * Redistributions of source code must retain the above copyright
9 ;;;; notice, this list of conditions and the following disclaimer.
10 ;;;;
11 ;;;; * Redistributions in binary form must reproduce the above
12 ;;;; copyright notice, this list of conditions and the following
13 ;;;; disclaimer in the documentation and/or other materials
14 ;;;; provided with the distribution.
15 ;;;;
16 ;;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
17 ;;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 ;;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ;;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20 ;;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 ;;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
22 ;;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 ;;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 ;;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 ;;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 ;;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 ;;;;
28 ;;;; acl.lisp
30 (in-package #:zs3)
32 (defclass access-control-list ()
33 ((owner
34 :initarg :owner
35 :accessor owner)
36 (grants
37 :initarg :grants
38 :accessor grants)))
40 (defmethod print-object ((object access-control-list) stream)
41 (print-unreadable-object (object stream :type t)
42 (format stream "owner ~S, ~D grant~:P"
43 (display-name (owner object))
44 (length (grants object)))))
46 (defclass grant ()
47 ((permission
48 :initarg :permission
49 :accessor permission)
50 (grantee
51 :initarg :grantee
52 :accessor grantee)))
54 (defclass acl-person (person) ())
56 (defmethod slot-unbound (class (object acl-person) (slot (eql 'display-name)))
57 (setf (display-name object) (id object)))
59 (defclass acl-email ()
60 ((email
61 :initarg :email
62 :accessor email)))
64 (defmethod print-object ((email acl-email) stream)
65 (print-unreadable-object (email stream :type t)
66 (prin1 (email email) stream)))
68 (defclass acl-group ()
69 ((label
70 :initarg :label
71 :accessor label)
72 (uri
73 :initarg :uri
74 :accessor uri)))
76 (defmethod slot-unbound (class (group acl-group) (slot (eql 'label)))
77 (setf (label group) (uri group)))
79 (defmethod print-object ((group acl-group) stream)
80 (print-unreadable-object (group stream :type t)
81 (prin1 (label group) stream)))
83 (defgeneric grantee-for-print (grantee)
84 (:method ((grantee person))
85 (display-name grantee))
86 (:method ((grantee acl-group))
87 (label grantee))
88 (:method ((grantee acl-email))
89 (email grantee)))
91 (defmethod print-object ((grant grant) stream)
92 (print-unreadable-object (grant stream :type t)
93 (format stream "~S to ~S"
94 (permission grant)
95 (grantee-for-print (grantee grant)))))
97 (defparameter *permissions*
98 '((:read . "READ")
99 (:write . "WRITE")
100 (:read-acl . "READ_ACP")
101 (:write-acl . "WRITE_ACP")
102 (:full-control . "FULL_CONTROL")))
104 (defun permission-name (permission)
105 (or (cdr (assoc permission *permissions*))
106 (error "Unknown permission - ~S" permission)))
108 (defun permission-keyword (permission)
109 (or (car (rassoc permission *permissions* :test 'string=))
110 (error "Unknown permission - ~S" permission)))
112 (defparameter *all-users*
113 (make-instance 'acl-group
114 :label "AllUsers"
115 :uri "http://acs.amazonaws.com/groups/global/AllUsers"))
117 (defparameter *aws-users*
118 (make-instance 'acl-group
119 :label "AWSUsers"
120 :uri "http://acs.amazonaws.com/groups/global/AuthenticatedUsers"))
122 (defparameter *log-delivery*
123 (make-instance 'acl-group
124 :label "LogDelivery"
125 :uri "http://acs.amazonaws.com/groups/s3/LogDelivery"))
127 (defgeneric acl-serialize (object))
129 (defmethod acl-serialize ((person person))
130 (cxml:with-element "ID" (cxml:text (id person)))
131 (cxml:with-element "DisplayName" (cxml:text (display-name person))))
133 (defvar *xsi* "http://www.w3.org/2001/XMLSchema-instance")
135 (defgeneric xsi-type (grantee)
136 (:method ((grantee acl-group))
137 "Group")
138 (:method ((grantee person))
139 "CanonicalUser")
140 (:method ((grantee acl-email))
141 "AmazonCustomerByEmail"))
143 (defun simple-element (name value)
144 (cxml:with-element name (cxml:text value)))
146 (defmethod acl-serialize ((grantee acl-group))
147 (simple-element "URI" (uri grantee)))
149 (defmethod acl-serialize ((grantee acl-email))
150 (simple-element "EmailAddress" (email grantee)))
152 (defmethod acl-serialize ((grant grant))
153 (cxml:with-element "Grant"
154 (cxml:with-element "Grantee"
155 (cxml:attribute* "xmlns" "xsi" *xsi*)
156 (cxml:attribute* "xsi" "type" (xsi-type (grantee grant)))
157 (acl-serialize (grantee grant)))
158 (simple-element "Permission" (permission-name (permission grant)))))
160 (defmethod acl-serialize ((acl access-control-list))
161 (cxml:with-xml-output (cxml:make-octet-vector-sink)
162 (cxml:with-element "AccessControlPolicy"
163 (cxml:attribute "xmlns" "http://s3.amazonaws.com/doc/2006-03-01/")
164 (cxml:with-element "Owner"
165 (acl-serialize (owner acl)))
166 (cxml:with-element "AccessControlList"
167 (dolist (grant (remove-duplicates (grants acl) :test #'acl-eqv))
168 (acl-serialize grant))))))
171 ;;; Parsing XML ACL responses
173 (defparameter *acl-binder*
174 (make-binder
175 '("AccessControlPolicy"
176 ("Owner"
177 ("ID" (bind :owner-id))
178 ("DisplayName" (bind :owner-display-name)))
179 ("AccessControlList"
180 (sequence :grants
181 ("Grant"
182 ("Grantee"
183 (elements-alist :grantee))
184 ("Permission" (bind :permission))))))))
186 (defclass acl-response (response)
187 ((acl
188 :initarg :acl
189 :accessor acl)))
191 (set-element-class "AccessControlPolicy" 'acl-response)
193 (defgeneric acl-eqv (a b)
194 (:method (a b)
195 (eql a b))
196 (:method ((a acl-group) (b acl-group))
197 (string= (uri a) (uri b)))
198 (:method ((a person) (b person))
199 (string= (id a) (id b)))
200 (:method ((a grant) (b grant))
201 (and (eql (permission a) (permission b))
202 (acl-eqv (grantee a) (grantee b)))))
204 (defun ensure-acl-group (uri)
205 (cond ((string= uri (uri *all-users*))
206 *all-users*)
207 ((string= uri (uri *aws-users*))
208 *aws-users*)
209 ((string= uri (uri *log-delivery*))
210 *log-delivery*)
212 (make-instance 'acl-group :uri uri))))
214 (defun alist-grant (bindings)
215 (let* ((permission (bvalue :permission bindings))
216 (alist (bvalue :grantee bindings))
217 (group-uri (assoc "URI" alist :test 'string=))
218 (user-id (assoc "ID" alist :test 'string=))
219 (email (assoc "EmailAddress" alist :test 'string=))
220 (display-name (assoc "DisplayName" alist :test 'string=)))
221 (make-instance 'grant
222 :permission (permission-keyword permission)
223 :grantee (cond (group-uri
224 (ensure-acl-group (cdr group-uri)))
225 (user-id
226 (make-instance 'acl-person
227 :id (cdr user-id)
228 :display-name
229 (cdr display-name)))
230 (email
231 (make-instance 'acl-email
232 :email (cdr email)))))))
234 (defmethod specialized-initialize ((response acl-response) source)
235 (let* ((bindings (xml-bind *acl-binder* source))
236 (owner (make-instance 'acl-person
237 :id (bvalue :owner-id bindings)
238 :display-name (bvalue :owner-display-name bindings)))
239 (grants (mapcar 'alist-grant (bvalue :grants bindings))))
240 (setf (acl response)
241 (make-instance 'access-control-list
242 :owner owner
243 :grants grants))
244 response))
247 (defun grant (permission &key to)
248 (make-instance 'grant :permission permission :grantee to))
250 (defun acl-email (address)
251 (make-instance 'acl-email :email address))
253 (defun acl-person (id &optional display-name)
254 (make-instance 'acl-person
255 :id id
256 :display-name (or display-name id)))