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.util.SimpleByteSource;
20 
21 import hunt.shiro.util.ByteSource;
22 import hunt.shiro.codec.Base64;
23 import hunt.shiro.codec.Hex;
24 // import hunt.shiro.util.Hex;
25 
26 import hunt.Exceptions;
27 import std.array;
28 
29 // import hunt.shiro.codec.Base64;
30 // import hunt.shiro.codec.CodecSupport;
31 // import hunt.shiro.codec.Hex;
32 
33 // import java.io.File;
34 // import java.io.InputStream;
35 // import java.util.Arrays;
36 
37 /**
38  * Very simple {@link ByteSource ByteSource} implementation that maintains an internal {@code byte[]} array and uses the
39  * {@link Hex Hex} and {@link Base64 Base64} codec classes to support the
40  * {@link #toHex() toHex()} and {@link #toBase64() toBase64()} implementations.
41  * <p/>
42  * The constructors on this class accept the following implicit byte-backed data types and will convert them to
43  * a byte-array automatically:
44  * <ul>
45  * <li>byte[]</li>
46  * <li>char[]</li>
47  * <li>string</li>
48  * <li>{@link ByteSource ByteSource}</li>
49  * <li>{@link File File}</li>
50  * <li>{@link InputStream InputStream}</li>
51  * </ul>
52  *
53  * @since 1.0
54  */
55 class SimpleByteSource : ByteSource {
56 
57     private byte[] bytes;
58     private string cachedHex;
59     private string cachedBase64;
60 
61     this(byte[] bytes) {
62         this.bytes = bytes;
63     }
64 
65     /**
66      * Creates an instance by converting the characters to a byte array (assumes UTF-8 encoding).
67      *
68      * @param chars the source characters to use to create the underlying byte array.
69      * @since 1.1
70      */
71     this(char[] chars) {
72         this.bytes = cast(byte[])(chars);
73     }
74 
75     /**
76      * Creates an instance by converting the string to a byte array (assumes UTF-8 encoding).
77      *
78      * @param string the source string to convert to a byte array (assumes UTF-8 encoding).
79      * @since 1.1
80      */
81     this(string string) {
82         this.bytes = cast(byte[])(string);
83     }
84 
85     /**
86      * Creates an instance using the sources bytes directly - it does not create a copy of the
87      * argument's byte array.
88      *
89      * @param source the source to use to populate the underlying byte array.
90      * @since 1.1
91      */
92     this(ByteSource source) {
93         this.bytes = source.getBytes();
94     }
95 
96     /**
97      * Creates an instance by converting the file to a byte array.
98      *
99      * @param file the file from which to acquire bytes.
100      * @since 1.1
101      */
102     // this(File file) {
103     //     this.bytes = new BytesHelper().getBytes(file);
104     // }
105 
106     /**
107      * Creates an instance by converting the stream to a byte array.
108      *
109      * @param stream the stream from which to acquire bytes.
110      * @since 1.1
111      */
112     // this(InputStream stream) {
113     //     this.bytes = new BytesHelper().getBytes(stream);
114     // }
115 
116     /**
117      * Returns {@code true} if the specified object is a recognized data type that can be easily converted to
118      * bytes by instances of this class, {@code false} otherwise.
119      * <p/>
120      * This implementation returns {@code true} IFF the specified object is an instance of one of the following
121      * types:
122      * <ul>
123      * <li>{@code byte[]}</li>
124      * <li>{@code char[]}</li>
125      * <li>{@link ByteSource}</li>
126      * <li>{@link string}</li>
127      * <li>{@link File}</li>
128      * </li>{@link InputStream}</li>
129      * </ul>
130      *
131      * @param o the object to test to see if it can be easily converted to bytes by instances of this class.
132      * @return {@code true} if the specified object can be easily converted to bytes by instances of this class,
133      *         {@code false} otherwise.
134      * @since 1.2
135      */
136     static bool isCompatible(Object o) {
137         // return o instanceof byte[] || o instanceof char[] || o instanceof string ||
138         //         o instanceof ByteSource || o instanceof File || o instanceof InputStream;
139         implementationMissing(false);
140         return false;
141     }
142 
143     
144 
145     byte[] getBytes() {
146         return this.bytes;
147     }
148 
149     bool isEmpty() {
150         return this.bytes is null || this.bytes.length == 0;
151     }
152 
153     string toHex() {
154         if ( this.cachedHex is null ) {
155             this.cachedHex = Hex.encodeToString(getBytes());
156         }
157         return this.cachedHex;
158     }
159 
160     string toBase64() {
161         if ( this.cachedBase64 is null ) {
162             this.cachedBase64 = Base64.encodeToString(getBytes());
163         }
164         return this.cachedBase64;
165     }
166 
167     override string toString() {
168         return toBase64();
169     }
170 
171     override size_t toHash() @trusted nothrow {
172         if (this.bytes.empty) {
173             return 0;
174         }
175         return hashOf(this.bytes);
176     }
177 
178     override bool opEquals(Object o) {
179         if (o is this) {
180             return true;
181         }
182         ByteSource bs = cast(ByteSource) o;
183         if (o !is null) {
184             return getBytes() == bs.getBytes();
185         }
186         return false;
187     }
188 
189     //will probably be removed in Shiro 2.0.  See SHIRO-203:
190     //https://issues.apache.org/jira/browse/SHIRO-203
191     // private static final class BytesHelper extends CodecSupport {
192     //     byte[] getBytes(File file) {
193     //         return toBytes(file);
194     //     }
195 
196     //     byte[] getBytes(InputStream stream) {
197     //         return toBytes(stream);
198     //     }
199     // }
200 }
201 
202 
203 /**
204  * Utility class that can construct ByteSource instances.  This is slightly nicer than needing to know the
205  * {@code ByteSource} implementation class to use.
206  *
207  * @since 1.2
208  */
209 final class ByteSourceUtil {
210 
211     /**
212      * Returns a new {@code ByteSource} instance representing the specified byte array.
213      *
214      * @param bytes the bytes to represent as a {@code ByteSource} instance.
215      * @return a new {@code ByteSource} instance representing the specified byte array.
216      */
217     static ByteSource bytes(byte[] bytes) {
218         return new SimpleByteSource(bytes);
219     }
220 
221     /**
222      * Returns a new {@code ByteSource} instance representing the specified character array's bytes.  The byte
223      * array is obtained assuming {@code UTF-8} encoding.
224      *
225      * @param chars the character array to represent as a {@code ByteSource} instance.
226      * @return a new {@code ByteSource} instance representing the specified character array's bytes.
227      */
228     static ByteSource bytes(char[] chars) {
229         return new SimpleByteSource(chars);
230     }
231 
232     /**
233      * Returns a new {@code ByteSource} instance representing the specified string's bytes.  The byte
234      * array is obtained assuming {@code UTF-8} encoding.
235      *
236      * @param string the string to represent as a {@code ByteSource} instance.
237      * @return a new {@code ByteSource} instance representing the specified string's bytes.
238      */
239     static ByteSource bytes(string string) {
240         return new SimpleByteSource(string);
241     }
242 
243     /**
244      * Returns a new {@code ByteSource} instance representing the specified ByteSource.
245      *
246      * @param source the ByteSource to represent as a new {@code ByteSource} instance.
247      * @return a new {@code ByteSource} instance representing the specified ByteSource.
248      */
249     static ByteSource bytes(ByteSource source) {
250         return new SimpleByteSource(source);
251     }
252 
253     /**
254      * Returns a new {@code ByteSource} instance representing the specified File's bytes.
255      *
256      * @param file the file to represent as a {@code ByteSource} instance.
257      * @return a new {@code ByteSource} instance representing the specified File's bytes.
258      */
259     // static ByteSource bytes(File file) {
260     //     return new SimpleByteSource(file);
261     // }
262 
263     /**
264      * Returns a new {@code ByteSource} instance representing the specified InputStream's bytes.
265      *
266      * @param stream the InputStream to represent as a {@code ByteSource} instance.
267      * @return a new {@code ByteSource} instance representing the specified InputStream's bytes.
268      */
269     // static ByteSource bytes(InputStream stream) {
270     //     return new SimpleByteSource(stream);
271     // }
272 
273     /**
274      * Returns {@code true} if the specified object can be easily represented as a {@code ByteSource} using
275      * the {@link ByteSourceUtil}'s default heuristics, {@code false} otherwise.
276      * <p/>
277      * This implementation merely returns {@link SimpleByteSource}.{@link SimpleByteSource#isCompatible(Object) isCompatible(source)}.
278      *
279      * @param source the object to test to see if it can be easily converted to ByteSource instances using default
280      *               heuristics.
281      * @return {@code true} if the specified object can be easily represented as a {@code ByteSource} using
282      *         the {@link ByteSourceUtil}'s default heuristics, {@code false} otherwise.
283      */
284     static bool isCompatible(Object source) {
285         return SimpleByteSource.isCompatible(source);
286     }
287 
288     /**
289      * Returns a {@code ByteSource} instance representing the specified byte source argument.  If the argument
290      * <em>cannot</em> be easily converted to bytes (as is indicated by the {@link #isCompatible(Object)} JavaDoc),
291      * this method will throw an {@link IllegalArgumentException}.
292      *
293      * @param source the byte-backed instance that should be represented as a {@code ByteSource} instance.
294      * @return a {@code ByteSource} instance representing the specified byte source argument.
295      * @throws IllegalArgumentException if the argument <em>cannot</em> be easily converted to bytes
296      *                                  (as indicated by the {@link #isCompatible(Object)} JavaDoc)
297      */
298     static ByteSource bytes(Object source) {
299         if (source is null) {
300             return null;
301         }
302         implementationMissing(false);
303         return null;
304         // if (!isCompatible(source)) {
305         //     string msg = "Unable to heuristically acquire bytes for object of type [" ~
306         //             source.getClass().getName() ~ "].  If this type is indeed a byte-backed data type, you might " ~
307         //             "want to write your own ByteSource implementation to extract its bytes explicitly.";
308         //     throw new IllegalArgumentException(msg);
309         // }
310         // if (source instanceof byte[]) {
311         //     return bytes((byte[]) source);
312         // } else if (source instanceof ByteSource) {
313         //     return (ByteSource) source;
314         // } else if (source instanceof char[]) {
315         //     return bytes((char[]) source);
316         // } else if (source instanceof string) {
317         //     return bytes((string) source);
318         // } else if (source instanceof File) {
319         //     return bytes((File) source);
320         // } else if (source instanceof InputStream) {
321         //     return bytes((InputStream) source);
322         // } else {
323         //     throw new IllegalStateException("Encountered unexpected byte source.  This is a bug - please notify " ~
324         //             "the Shiro developer list asap (the isCompatible implementation does not reflect this " ~
325         //             "method's implementation).");
326         // }
327     }
328 }