Creating folders is now working.
[photos] / src / main / java / org / wamblee / photos / model / authorization / AuthorizedAlbum.java
1 /*
2  * Copyright 2005 the original author or authors.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.wamblee.photos.model.authorization;
17
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.logging.Logger;
23
24 import javax.enterprise.context.SessionScoped;
25 import javax.inject.Inject;
26 import javax.servlet.http.HttpSession;
27
28 import org.wamblee.cache.Cache;
29 import org.wamblee.cache.CachedObject;
30 import org.wamblee.photos.model.Album;
31 import org.wamblee.photos.model.Path;
32 import org.wamblee.photos.model.Photo;
33 import org.wamblee.photos.model.PhotoEntry;
34 import org.wamblee.photos.model.plumbing.AllPhotos;
35 import org.wamblee.photos.model.plumbing.AuthorizedPhotos;
36 import org.wamblee.photos.model.plumbing.PhotoCache;
37 import org.wamblee.security.authorization.AllOperation;
38 import org.wamblee.security.authorization.AuthorizationService;
39 import org.wamblee.security.authorization.DeleteOperation;
40 import org.wamblee.security.authorization.ReadOperation;
41 import org.wamblee.security.authorization.WriteOperation;
42
43 /**
44  * Decorator for an album providing defined behavior when used in a concurrent
45  * setting.
46  */
47 @SessionScoped
48 @AuthorizedPhotos
49 public class AuthorizedAlbum extends AuthorizedPhotoEntry implements Album {
50
51     private static final Logger LOGGER = Logger.getLogger(AuthorizedAlbum.class.getName());
52
53     private AuthorizationService _authorizer;
54
55     private CachedObject<String, ArrayList<PhotoEntry>> _authorizedEntries;
56
57     private HttpSession _session;
58
59     /**
60      * Constructs concurrent album as a decorator for an album implementation.
61      *
62      * @param aAlbum Album to decorate.
63      */
64     @Inject
65     public AuthorizedAlbum(@AllPhotos Album aAlbum, AuthorizationService aService,
66             @PhotoCache Cache<String, ArrayList<PhotoEntry>> aCache, HttpSession aSession) {
67         super(aAlbum);
68         _authorizer = aService;
69         _authorizedEntries = new CachedObject<>(aCache, "session:" + aSession.getId() + "/" + aAlbum.getPath(),
70                 new CachedObject.Computation<String, ArrayList<PhotoEntry>>() {
71                     public ArrayList<PhotoEntry> getObject(String aObjectKey) {
72                         return AuthorizedAlbum.this.compute();
73                     }
74                 });
75         _session = aSession;
76     }
77
78     /**
79      * Computes the cache of photo entries to which read access is allowed.
80      *
81      * @return Photo entries to which read access is allowed.
82      */
83     private synchronized ArrayList<PhotoEntry> compute() {
84         LOGGER.info("Refreshing cache " + getPath());
85         ArrayList<PhotoEntry> result = new ArrayList<>();
86         for (int i = 0; i < decorated().size(); i++) {
87             PhotoEntry entry = decorated().getEntry(i);
88             if (_authorizer.isAllowed(entry, new ReadOperation())) {
89                 result.add(decorate(entry)); // subscription will take place
90                 // automatically.
91             }
92         }
93         if (result == null) {
94             throw new RuntimeException("Result is null");
95         }
96         return result;
97     }
98
99     private Album decorated() {
100         return (Album) getEntry();
101     }
102
103     /**
104      * Creates a decorate for the photo entry to make it safe for concurrent
105      * access.
106      *
107      * @param aEntry Entry to decorate
108      * @return Decorated photo.
109      */
110     private <T extends PhotoEntry> T decorate(T aEntry) {
111         if (aEntry == null) {
112             return null;
113         } else if (aEntry instanceof Photo) {
114             return (T) new AuthorizedPhoto((Photo) aEntry);
115         } else if (aEntry instanceof Album) {
116             return (T) new AuthorizedAlbum((Album) aEntry, _authorizer, _authorizedEntries.getCache(), _session);
117         } else {
118             throw new IllegalArgumentException("Entry is neither a photo nor an album: " + aEntry);
119         }
120     }
121
122     /*
123      * (non-Javadoc)
124      * 
125      * @see org.wamblee.photos.model.Album#getEntry(java.lang.String)
126      */
127     public PhotoEntry getEntry(String aPath) {
128         return getEntry(new Path(aPath));
129     }
130
131     /*
132      * (non-Javadoc)
133      * 
134      * @see org.wamblee.photos.model.Album#getEntry(org.wamblee.photos.model.Path)
135      */
136     public PhotoEntry getEntry(Path aPath) {
137         if (aPath.isRoot()) {
138             return this;
139         }
140         List<PhotoEntry> cache = _authorizedEntries.get();
141         String id = aPath.getPart(0);
142         Path remainder = aPath.remainder();
143         for (PhotoEntry entry : cache) {
144             if (entry.getId().equals(id)) {
145                 if (remainder.isRoot()) {
146                     return entry;
147                 } else {
148                     if (!(entry instanceof Album)) {
149                         throw new IllegalArgumentException(getPath() + " " +
150                                 aPath);
151                     }
152                     return ((Album) entry).getEntry(remainder);
153                 }
154             }
155         }
156         return null;
157     }
158
159     /*
160      * (non-Javadoc)
161      * 
162      * @see org.wamblee.photos.model.Album#getEntry(int)
163      */
164     public PhotoEntry getEntry(int aIndex) {
165         return _authorizedEntries.get().get(aIndex);
166     }
167
168     /*
169      * (non-Javadoc)
170      * 
171      * @see org.wamblee.photos.model.Album#size()
172      */
173     public int size() {
174         return _authorizedEntries.get().size();
175     }
176
177     /*
178      * (non-Javadoc)
179      * 
180      * @see org.wamblee.photos.model.Album#addImage(java.lang.String,
181      *      java.io.InputStream)
182      */
183     public void addImage(String aId, InputStream aImage) throws IOException {
184         try {
185             _authorizer.check(this, new WriteOperation());
186             decorated().addImage(aId, aImage);
187         } finally {
188             _authorizedEntries.invalidate();
189         }
190     }
191
192     /*
193      * (non-Javadoc)
194      * 
195      * @see org.wamblee.photos.model.Album#addAlbum(java.lang.String)
196      */
197     public void addAlbum(String aId) throws IOException {
198         try {
199             _authorizer.check(this, new WriteOperation());
200             decorated().addAlbum(aId);
201         } finally {
202             _authorizedEntries.invalidate();
203         }
204     }
205
206     /*
207      * (non-Javadoc)
208      * 
209      * @see org.wamblee.photos.model.Album#removeEntry(java.lang.String)
210      */
211     public void removeEntry(String aId) throws IOException {
212         try {
213             // Check whether deletion is allowed.
214             PhotoEntry entry = _authorizer.check(decorated().getEntry("/" + aId), new DeleteOperation());
215             _authorizedEntries.invalidate();
216             decorated().removeEntry(aId);
217         } finally {
218             _authorizedEntries.invalidate();
219         }
220     }
221
222     public Photo findPhotoBefore(String aId) {
223         Photo entry = decorated().findPhotoBefore(aId);
224         while (entry != null && !_authorizer.isAllowed(entry, new AllOperation())) {
225             entry = decorated().findPhotoBefore(entry.getId());
226         }
227         return decorate(entry);
228     }
229
230     public Photo findPhotoAfter(String aId) {
231         Photo entry = decorated().findPhotoAfter(aId);
232         while (entry != null && !_authorizer.isAllowed(entry, new AllOperation())) {
233             entry = decorated().findPhotoAfter(entry.getId());
234         }
235         return decorate(entry);
236     }
237
238     /*
239      * (non-Javadoc)
240      * 
241      * @see java.lang.Object#toString()
242      */
243     @Override
244     public String toString() {
245         return "AuthorizedAlbum(path = '" + decorated().getPath() + "')";
246     }
247 }