1 /**
2  * Authors: Szabo Bogdan <szabobogdan@yahoo.com>
3  * Date: 2 25, 2015
4  * License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
5  * Copyright: Public Domain
6  */
7 module vibedav.plugins.acldav;
8 
9 public import vibedav.base;
10 
11 import vibe.core.file;
12 import vibe.inet.mimetypes;
13 import vibe.http.server;
14 
15 interface ACLDavProperties {
16 	@property {
17 		/// rfc5397 - 3
18 		@ResourceProperty("current-user-principal", "DAV:")
19 		@ResourcePropertyTagText("href", "DAV:")
20 		string currentUserPrincipal(DavResource resource);
21 
22 		/// rfc3744 - 4.2
23 		@ResourceProperty("principal-URL", "DAV:")
24 		@ResourcePropertyTagText("href", "DAV:")
25 		string principalURL(DavResource resource);
26 
27 		/// rfc3744 - 5.8
28 		@ResourceProperty("principal-collection-set", "DAV:")
29 		@ResourcePropertyTagText("href", "DAV:")
30 		string[] principalCollectionSet(DavResource resource);
31 
32 		@ResourceProperty("owner", "DAV:")
33 		@ResourcePropertyTagText("href", "DAV:")
34 		string owner(DavResource resource);
35 	}
36 }
37 
38 private bool matchPluginUrl(Path path) {
39 	string strPath = path.toString;
40 	enum len = "principals/".length;
41 
42 	return strPath.length >= len && strPath[0..len] == "principals/";
43 }
44 
45 class ACLDavResourcePlugin : ACLDavProperties, IDavResourcePlugin, IDavReportSetProperties {
46 
47 	string currentUserPrincipal(DavResource resource) {
48 		if(matchPluginUrl(resource.path))
49 			return "/" ~ resource.rootURL ~ "principals/" ~ resource.username ~ "/";
50 
51 		throw new DavException(HTTPStatus.notFound, "not found");
52 	}
53 
54 	string principalURL(DavResource resource) {
55 		if(matchPluginUrl(resource.path))
56 			return  "/" ~ resource.rootURL ~ "principals/" ~ resource.username ~ "/";
57 
58 		throw new DavException(HTTPStatus.notFound, "not found");
59 	}
60 
61 	string owner(DavResource resource) {
62 		return principalURL(resource);
63 	}
64 
65 	string[] principalCollectionSet(DavResource resource) {
66 		if(matchPluginUrl(resource.path))
67 			return [  "/" ~ resource.rootURL ~ "principals/" ];
68 
69 		throw new DavException(HTTPStatus.notFound, "not found");
70 	}
71 
72 	string[] supportedReportSet(DavResource resource) {
73 		if(matchPluginUrl(resource.path))
74 			return ["expand-property:DAV:", "principal-property-search:DAV:", "principal-search-property-set:DAV:"];
75 
76 		return [];
77 	}
78 
79 	bool canSetContent(DavResource resource) {
80 		return false;
81 	}
82 
83 	bool canGetStream(DavResource resource) {
84 		return false;
85 	}
86 
87 	bool canSetProperty(DavResource resource, string name) {
88 		return false;
89 	}
90 
91 	bool canRemoveProperty(DavResource resource, string name) {
92 		return false;
93 	}
94 
95 	bool canGetProperty(DavResource resource, string name) {
96 		if(!matchPluginUrl(resource.path))
97 			return false;
98 
99 		if(hasDavInterfaceProperty!ACLDavProperties(name))
100 			return true;
101 
102 		if(hasDavInterfaceProperty!IDavReportSetProperties(name))
103 			return true;
104 
105 		return false;
106 	}
107 
108 	bool[string] getChildren(DavResource resource) {
109 		bool[string] list;
110 		return list;
111 	}
112 
113 	void setContent(DavResource resource, const ubyte[] content) {
114 		throw new DavException(HTTPStatus.internalServerError, "Can't set content.");
115 	}
116 
117 	void setContent(DavResource resource, InputStream content, ulong size) {
118 		throw new DavException(HTTPStatus.internalServerError, "Can't set content.");
119 	}
120 
121 	InputStream stream(DavResource resource) {
122 		throw new DavException(HTTPStatus.internalServerError, "Can't get stream.");
123 	}
124 
125 	void copyPropertiesTo(URL source, URL destination) { }
126 
127 	DavProp property(DavResource resource, string name) {
128 		if(!matchPluginUrl(resource.path))
129 			throw new DavException(HTTPStatus.internalServerError, "Can't get property.");
130 
131 		if(hasDavInterfaceProperty!ACLDavProperties(name))
132 			return getDavInterfaceProperty!ACLDavProperties(name, this, resource);
133 
134 		if(hasDavInterfaceProperty!IDavReportSetProperties(name))
135 			return getDavInterfaceProperty!IDavReportSetProperties(name, this, resource);
136 
137 		throw new DavException(HTTPStatus.internalServerError, "Can't get property.");
138 	}
139 
140 	HTTPStatus setProperty(DavResource resource, string name, DavProp prop) {
141 		throw new DavException(HTTPStatus.internalServerError, "Can't set property.");
142 	}
143 
144 	HTTPStatus removeProperty(DavResource resource, string name) {
145 		throw new DavException(HTTPStatus.internalServerError, "Can't remove property.");
146 	}
147 
148 	@property {
149 		string name() {
150 			return "ACLDavResourcePlugin";
151 		}
152 	}
153 }
154 
155 class ACLDavPlugin : BaseDavPlugin {
156 
157 	private IDav _dav;
158 
159 	this(IDav dav) {
160 		super(dav);
161 	}
162 
163 	bool isPrincipalsCollection(Path path) {
164 		return matchPluginUrl(path) && path.length == 1;
165 	}
166 
167 	bool isUserCollection(Path path, string username) {
168 		return matchPluginUrl(path) && path.head == username && path.length == 2;
169 	}
170 
171 	bool isRootCollection(Path path) {
172 		return path.length == 0;
173 	}
174 
175 	override {
176 		bool exists(URL url, string username) {
177 			return isPrincipalsCollection(dav.path(url)) || isUserCollection(dav.path(url), username);
178 		}
179 
180 		Path[] childList(URL url, string username) {
181   		Path[] list;
182 
183 			if(dav.path(url).length == 0) {
184 				list = [ Path("principals/") ];
185 			}
186 
187 			if(isPrincipalsCollection(dav.path(url))) {
188 				list = [ Path("principals/" ~ username ~ "/") ];
189 			}
190 
191   		return list;
192   	}
193 
194 		void bindResourcePlugins(DavResource resource) {
195 			resource.registerPlugin(new ACLDavResourcePlugin);
196 		}
197 
198 		DavResource getResource(URL url, string username) {
199 			if(isPrincipalsCollection(dav.path(url)) || isUserCollection(dav.path(url), username)) {
200 				DavResource resource = super.getResource(url, username);
201 				resource.resourceType ~= "collection:DAV:";
202 
203 				return resource;
204 			}
205 
206 			throw new DavException(HTTPStatus.internalServerError, "Can't get resource.");
207 		}
208 	}
209 
210 	@property {
211 		string name() {
212 			return "ACLDavPlugin";
213 		}
214 
215 		override string[] support(URL url, string username) {
216 			return matchPluginUrl(dav.path(url)) ? [ "access-control" ] : [];
217 		}
218 	}
219 }