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.support.SubjectThreadState;
20 
21 import hunt.shiro.subject.support.DelegatingSubject;
22 import hunt.shiro.subject.support.SubjectThreadState;
23 
24 import hunt.shiro.mgt.SecurityManager;
25 import hunt.shiro.subject.Subject;
26 import hunt.shiro.util.CollectionUtils;
27 import hunt.shiro.util.ThreadContext;
28 import hunt.shiro.util.ThreadState;
29 
30 import hunt.collection.Map;
31 import hunt.Exceptions;
32 
33 /**
34  * Manages thread-state for {@link Subject Subject} access (supporting
35  * {@code SecurityUtils.}{@link hunt.shiro.SecurityUtils#getSubject() getSubject()} calls)
36  * during a thread's execution.
37  * <p/>
38  * The {@link #bind bind} method will bind a {@link Subject} and a
39  * {@link hunt.shiro.mgt.SecurityManager SecurityManager} to the {@link ThreadContext} so they can be retrieved
40  * from the {@code ThreadContext} later by any
41  * {@code SecurityUtils.}{@link hunt.shiro.SecurityUtils#getSubject() getSubject()} calls that might occur during
42  * the thread's execution.
43  *
44  */
45 class SubjectThreadState : ThreadState {
46 
47     private Map!(string, Object) originalResources;
48 
49     private Subject subject;
50     private SecurityManager securityManager;
51 
52     /**
53      * Creates a new {@code SubjectThreadState} that will bind and unbind the specified {@code Subject} to the
54      * thread
55      *
56      * @param subject the {@code Subject} instance to bind and unbind from the {@link ThreadContext}.
57      */
58     this(Subject subject) {
59         if (subject  is null) {
60             throw new IllegalArgumentException("Subject argument cannot be null.");
61         }
62         this.subject = subject;
63 
64         SecurityManager securityManager = null;
65         DelegatingSubject ds = cast(DelegatingSubject)subject;
66         if (ds !is null) {
67             securityManager = ds.getSecurityManager();
68         }
69         if ( securityManager  is null) {
70             securityManager = ThreadContext.getSecurityManager();
71         }
72         this.securityManager = securityManager;
73     }
74 
75     /**
76      * Returns the {@code Subject} instance managed by this {@code ThreadState} implementation.
77      *
78      * @return the {@code Subject} instance managed by this {@code ThreadState} implementation.
79      */
80     protected Subject getSubject() {
81         return this.subject;
82     }
83 
84     /**
85      * Binds a {@link Subject} and {@link hunt.shiro.mgt.SecurityManager SecurityManager} to the
86      * {@link ThreadContext} so they can be retrieved later by any
87      * {@code SecurityUtils.}{@link hunt.shiro.SecurityUtils#getSubject() getSubject()} calls that might occur
88      * during the thread's execution.
89      * <p/>
90      * Prior to binding, the {@code ThreadContext}'s existing {@link ThreadContext#getResources() resources} are
91      * retained so they can be restored later via the {@link #restore restore} call.
92      */
93      void bind() {
94         SecurityManager securityManager = this.securityManager;
95         if ( securityManager  is null ) {
96             //try just in case the constructor didn't find one at the time:
97             securityManager = ThreadContext.getSecurityManager();
98         }
99         this.originalResources = ThreadContext.getResources();
100         ThreadContext.remove();
101 
102         ThreadContext.bind(this.subject);
103         if (securityManager !is null) {
104             ThreadContext.bind(securityManager);
105         }
106     }
107 
108     /**
109      * {@link ThreadContext#remove Remove}s all thread-state that was bound by this instance.  If any previous
110      * thread-bound resources existed prior to the {@link #bind bind} call, they are restored back to the
111      * {@code ThreadContext} to ensure the thread state is exactly as it was before binding.
112      */
113      void restore() {
114         ThreadContext.remove();
115         if (!CollectionUtils.isEmpty(this.originalResources)) {
116             ThreadContext.setResources(this.originalResources);
117         }
118     }
119 
120     /**
121      * Completely {@link ThreadContext#remove removes} the {@code ThreadContext} state.  Typically this method should
122      * only be called in special cases - it is more 'correct' to {@link #restore restore} a thread to its previous
123      * state than to clear it entirely.
124      */
125      void clear() {
126         ThreadContext.remove();
127     }
128 }