1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 module hunt.shiro.subject.SimplePrincipalMap;
20 
21 import hunt.shiro.subject.PrincipalMap;
22 import hunt.shiro.util.CollectionUtils;
23 
24 import hunt.collection;
25 import hunt.Exceptions;
26 import hunt.Object;
27 import hunt.util.ObjectUtils;
28 
29 import std.array;
30 import std.range;
31 
32 /**
33  * Default implementation of the {@link PrincipalMap} interface.
34  *
35  * *EXPERIMENTAL for Shiro 1.2 - DO NOT USE YET*
36  *
37  * @author Les Hazlewood
38  */
39 class SimplePrincipalMap : PrincipalMap {
40 
41     //Key: realm name, Value: map of principals specific to that realm
42     //                        internal map - key: principal name, value: principal
43     private Map!(string, Map!(string, Object)) realmPrincipals;
44 
45     //maintains the principals from all realms plus any that are modified via the Map modification methods
46     //this ensures a fast lookup of any named principal instead of needing to iterate over
47     //the realmPrincipals for each lookup.
48     private Map!(string, Object) combinedPrincipals;
49 
50     this() {
51         this(null);
52     }
53 
54     this(Map!(string, Map!(string, Object)) backingMap) {
55         if (!CollectionUtils.isEmpty(backingMap)) {
56             this.realmPrincipals = backingMap;
57             foreach (Map!(string, Object) principals; this.realmPrincipals.byValue()) {
58                 if (!CollectionUtils.isEmpty(principals) ) {
59                     ensureCombinedPrincipals().putAll(principals);
60                 }
61             }
62         }
63     }
64 
65     int size() {
66         return CollectionUtils.size!(string, Object)(this.combinedPrincipals);
67     }
68 
69     protected Map!(string, Object) ensureCombinedPrincipals() {
70         if (this.combinedPrincipals  is null) {
71             this.combinedPrincipals = new HashMap!(string, Object)();
72         }
73         return this.combinedPrincipals;
74     }
75 
76     bool containsKey(string o) {
77         return this.combinedPrincipals !is null && this.combinedPrincipals.containsKey(o);
78     }
79 
80     bool containsValue(Object o) {
81         return this.combinedPrincipals !is null && this.combinedPrincipals.containsValue(o);
82     }
83 
84     Object get(string o) {
85         implementationMissing(false);
86         return null;
87         // return this.combinedPrincipals !is null && this.combinedPrincipals.containsKey(o);
88     }
89 
90     Object put(string s, Object o) {
91         return ensureCombinedPrincipals().put(s, o);
92     }
93 
94     Object remove(string o) {
95         return this.combinedPrincipals !is null ? this.combinedPrincipals.remove(o) : null;
96     }
97 
98     void putAll(Map!(string, Object) map) {
99         if (!CollectionUtils.isEmpty(map)) {
100             ensureCombinedPrincipals().putAll(map);
101         }
102     }
103 
104     // Set!(string) keySet() {
105     //     return CollectionUtils.isEmpty(this.combinedPrincipals) ?
106     //             Collections.emptySet!string() :
107     //             Collections.unmodifiableSet(this.combinedPrincipals.keySet());
108     // }
109 
110     // Collection!(Object) values() {
111     //     return CollectionUtils.isEmpty(this.combinedPrincipals) ?
112     //             Collections.emptySet() :
113     //             Collections.unmodifiableCollection(this.combinedPrincipals.values());
114     // }
115 
116     //  Set!(Entry!(string, Object)) entrySet() {
117     //     return CollectionUtils.isEmpty(this.combinedPrincipals) ?
118     //             Collections.<Entry!(string,Object)>emptySet() :
119     //             Collections.unmodifiableSet(this.combinedPrincipals.entrySet());
120     // }
121 
122     void clear() {
123         this.realmPrincipals = null;
124         this.combinedPrincipals = null;
125     }
126 
127     Object getPrimaryPrincipal() {
128         //heuristic - just use the first one we come across:
129         if(CollectionUtils.isEmpty(this.combinedPrincipals))
130             return null;
131         else {
132             return this.combinedPrincipals.values()[0];
133         }
134     }
135 
136     T oneByType(T)() if(is(T == class) || is(T == interface)) {
137         if (CollectionUtils.isEmpty(this.combinedPrincipals)) {
138             return null;
139         }
140         foreach(Object value; this.combinedPrincipals.values()) {
141             T v = cast(T)value;
142             if (value !is null && v !is null) {
143                 return v;
144             }
145         }
146         return null;
147     }
148 
149     Collection!(T) byType(T)() {
150         if (CollectionUtils.isEmpty(this.combinedPrincipals)) {
151             return Collections.emptySet!T();
152         }
153         Collection!(T) instances = new ArrayList!(T)();
154         foreach(Object value; this.combinedPrincipals.values()) {
155             T v = cast(T)value;
156             if (value !is null && v !is null) {
157                 instances.add(v);
158             }
159         }
160         return instances;
161     }
162 
163     List!(Object) asList() {
164         if (CollectionUtils.isEmpty(this.combinedPrincipals)) {
165             return Collections.emptyList!Object();
166         }
167         List!(Object) list = new ArrayList!(Object)(this.combinedPrincipals.size());
168         list.addAll(this.combinedPrincipals.values());
169         return list;
170     }
171 
172     Set!(Object) asSet() {
173         if (CollectionUtils.isEmpty(this.combinedPrincipals)) {
174             return Collections.emptySet!Object();
175         }
176         Set!(Object) set = new HashSet!(Object)(this.combinedPrincipals.size());
177         set.addAll(this.combinedPrincipals.values());
178         return set;
179     }
180 
181     Object[] fromRealm(string realmName) {
182         if (CollectionUtils.isEmpty(this.realmPrincipals)) {
183             return null;
184         }
185         Map!(string,Object) principals = this.realmPrincipals.get(realmName);
186         if (CollectionUtils.isEmpty(principals)) {
187             return null;
188         }
189         return principals.values();
190     }
191 
192     string[] getRealmNames() {
193         if (CollectionUtils.isEmpty(this.realmPrincipals)) {
194             return null;
195         }
196         return this.realmPrincipals.byKey.array();
197     }
198 
199      bool isEmpty() {
200         return CollectionUtils.isEmpty(this.combinedPrincipals);
201     }
202 
203     //  Iterator iterator() {
204     //     return asList().iterator();
205     // }
206 
207      Map!(string, Object) getRealmPrincipals(string name) {
208         if (this.realmPrincipals  is null) {
209             return null;
210         }
211         Map!(string,Object) principals = this.realmPrincipals.get(name);
212         if (principals  is null) {
213             return null;
214         }
215         return principals;
216     }
217 
218      Map!(string,Object) setRealmPrincipals(string realmName, Map!(string, Object) principals) {
219         if (realmName  is null) {
220             throw new NullPointerException("realmName argument cannot be null.");
221         }
222         if (this.realmPrincipals  is null) {
223             if (!CollectionUtils.isEmpty(principals)) {
224                 this.realmPrincipals = new HashMap!(string,Map!(string,Object))();
225                 return this.realmPrincipals.put(realmName, new HashMap!(string,Object)(principals));
226             } else {
227                 return null;
228             }
229         } else {
230             Map!(string,Object) existingPrincipals = this.realmPrincipals.remove(realmName);
231             if (!CollectionUtils.isEmpty(principals)) {
232                 this.realmPrincipals.put(realmName, new HashMap!(string,Object)(principals));
233             }
234             return existingPrincipals;
235         }
236     }
237 
238      Object setRealmPrincipal(string realmName, string principalName, Object principal) {
239         if (realmName  is null) {
240             throw new NullPointerException("realmName argument cannot be null.");
241         }
242         if (principalName  is null) {
243             throw new NullPointerException(("principalName argument cannot be null."));
244         }
245         if (principal  is null) {
246             return removeRealmPrincipal(realmName, principalName);
247         }
248         if (this.realmPrincipals  is null) {
249             this.realmPrincipals = new HashMap!(string,Map!(string,Object))();
250         }
251         Map!(string,Object) principals = this.realmPrincipals.get(realmName);
252         if (principals  is null) {
253             principals = new HashMap!(string,Object)();
254             this.realmPrincipals.put(realmName, principals);
255         }
256         return principals.put(principalName, principal);
257     }
258 
259      Object getRealmPrincipal(string realmName, string principalName) {
260         if (realmName  is null) {
261             throw new NullPointerException("realmName argument cannot be null.");
262         }
263         if (principalName  is null) {
264             throw new NullPointerException(("principalName argument cannot be null."));
265         }
266         if (this.realmPrincipals  is null) {
267             return null;
268         }
269         Map!(string,Object) principals = this.realmPrincipals.get(realmName);
270         if (principals !is null) {
271             return principals.get(principalName);
272         }
273         return null;
274     }
275 
276      Object removeRealmPrincipal(string realmName, string principalName) {
277         if (realmName  is null) {
278             throw new NullPointerException("realmName argument cannot be null.");
279         }
280         if (principalName  is null) {
281             throw new NullPointerException(("principalName argument cannot be null."));
282         }
283         if (this.realmPrincipals  is null) {
284             return null;
285         }
286         Map!(string,Object) principals = this.realmPrincipals.get(realmName);
287         if (principals !is null) {
288             return principals.remove(principalName);
289         }
290         return null;
291     }
292 
293     bool remove(string key, Object value) {
294         Object curValue = get(key);
295         if (curValue != value || !containsKey(key))
296             return false;
297         remove(key);
298         return true;
299     }
300 
301     // void putAll(Map!(string, Object) map) {
302     //     realmPrincipals.putAll(map);
303     // }
304 
305     // void clear() {
306     //     realmPrincipals.clear();
307     // }
308 
309     bool replace(string key, Object oldValue, Object newValue) {
310         Object curValue = get(key);
311         if (curValue != oldValue || !containsKey(key)) {
312             return false;
313         }
314         put(key, newValue);
315         return true;
316     }
317 
318     Object replace(string key, Object value) {
319         Object curValue = Object.init;
320         if (containsKey(key)) {
321             curValue = put(key, value);
322         }
323         return curValue;
324     }
325 
326     override string toString() {
327         if (isEmpty())
328             return "{}";
329 
330         Appender!string sb;
331         sb.put("{");
332         bool isFirst = true;
333         foreach (string key, Object value; this) {
334             if (!isFirst) {
335                 sb.put(", ");
336             }
337             sb.put(key ~ "=" ~ value.toString());
338             isFirst = false;
339         }
340         sb.put("}");
341 
342         return sb.data;
343     }
344 
345     Object putIfAbsent(string key, Object value) {
346         Object v = Object.init;
347 
348         if (!containsKey(key))
349             v = put(key, value);
350 
351         return v;
352     }
353 
354     Object[] values() {
355         return byValue().array();
356     }
357 
358     Object opIndex(string key) {
359         return get(key);
360     }
361 
362     
363     int opApply(scope int delegate(ref Object) dg) {
364         throw new NotImplementedException();
365     }
366 
367     int opApply(scope int delegate(ref string, ref Object) dg) {
368         throw new NotImplementedException();
369     }
370 
371     int opApply(scope int delegate(MapEntry!(string, Object) entry) dg) {
372         throw new NotImplementedException();
373     }
374 
375     InputRange!string byKey() {
376         throw new NotImplementedException();
377     }
378 
379     InputRange!Object byValue() {
380         throw new NotImplementedException();
381     }
382 
383     override bool opEquals(Object o) {
384         throw new UnsupportedOperationException();
385     }
386 
387     bool opEquals(IObject o) {
388         return opEquals(cast(Object) o);
389     }
390 
391     override size_t toHash() @trusted nothrow {
392         size_t h = 0;
393         try {
394             foreach (MapEntry!(string, Object) i; this) {
395                 h += i.toHash();
396             }
397         } catch (Exception ex) {
398         }
399         return h;
400     } 
401 
402     mixin CloneMemberTemplate!(typeof(this));
403 }