2 ;;;; Copyright (c) 2008 Zachary Beane, All Rights Reserved
4 ;;;; Redistribution and use in source and binary forms, with or without
5 ;;;; modification, are permitted provided that the following conditions
8 ;;;; * Redistributions of source code must retain the above copyright
9 ;;;; notice, this list of conditions and the following disclaimer.
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.
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.
32 (defclass access-control-list
()
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
)))))
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
()
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
()
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
))
88 (:method
((grantee acl-email
))
91 (defmethod print-object ((grant grant
) stream
)
92 (print-unreadable-object (grant stream
:type t
)
93 (format stream
"~S to ~S"
95 (grantee-for-print (grantee grant
)))))
97 (defparameter *permissions
*
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
115 :uri
"http://acs.amazonaws.com/groups/global/AllUsers"))
117 (defparameter *aws-users
*
118 (make-instance 'acl-group
120 :uri
"http://acs.amazonaws.com/groups/global/AuthenticatedUsers"))
122 (defparameter *log-delivery
*
123 (make-instance 'acl-group
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
))
138 (:method
((grantee person
))
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
*
175 '("AccessControlPolicy"
177 ("ID" (bind :owner-id
))
178 ("DisplayName" (bind :owner-display-name
)))
183 (elements-alist :grantee
))
184 ("Permission" (bind :permission
))))))))
186 (defclass acl-response
(response)
191 (set-element-class "AccessControlPolicy" 'acl-response
)
193 (defgeneric acl-eqv
(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
*))
207 ((string= uri
(uri *aws-users
*))
209 ((string= uri
(uri *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
)))
226 (make-instance 'acl-person
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
))))
241 (make-instance 'access-control-list
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
256 :display-name
(or display-name id
)))