1 | package diskCacheV111.util; |
2 | |
3 | import java.io.BufferedReader; |
4 | import java.io.File; |
5 | import java.io.FileReader; |
6 | import java.io.IOException; |
7 | import java.util.ArrayList; |
8 | import java.util.List; |
9 | import java.util.Collection; |
10 | import java.util.NoSuchElementException; |
11 | import java.util.concurrent.locks.Lock; |
12 | import java.util.concurrent.locks.ReadWriteLock; |
13 | import java.util.concurrent.locks.ReentrantReadWriteLock; |
14 | import java.util.regex.Matcher; |
15 | import java.util.regex.Pattern; |
16 | import java.util.regex.PatternSyntaxException; |
17 | |
18 | import javax.security.auth.Subject; |
19 | import org.dcache.auth.Subjects; |
20 | |
21 | import diskCacheV111.vehicles.StorageInfo; |
22 | |
23 | public class CheckStagePermission { |
24 | private File _stageConfigFile; |
25 | private long _lastTimeReadingStageConfigFile = 0L; |
26 | private List<Pattern[]> _regexList; |
27 | private final boolean _isEnabled; |
28 | |
29 | private final ReadWriteLock _fileReadWriteLock = new ReentrantReadWriteLock(); |
30 | private final Lock _fileReadLock = _fileReadWriteLock.readLock(); |
31 | private final Lock _fileWriteLock = _fileReadWriteLock.writeLock(); |
32 | |
33 | public CheckStagePermission(String stageConfigurationFilePath) { |
34 | if ( stageConfigurationFilePath == null || stageConfigurationFilePath.length() == 0 ) { |
35 | _isEnabled = false; |
36 | return; |
37 | } |
38 | |
39 | _stageConfigFile = new File(stageConfigurationFilePath); |
40 | _isEnabled = true; |
41 | } |
42 | |
43 | /** |
44 | * Check whether staging is allowed for a particular subject on a particular object. |
45 | * |
46 | * @param subject The subject |
47 | * @param storageInfo The storage info of the object |
48 | * @return true if and only if the subject is allowed to perform |
49 | * staging |
50 | */ |
51 | public boolean canPerformStaging(Subject subject, StorageInfo storageInfo) |
52 | throws PatternSyntaxException, IOException |
53 | { |
54 | if (!_isEnabled || Subjects.isRoot(subject)) |
55 | return true; |
56 | |
57 | try { |
58 | String dn = Subjects.getDn(subject); |
59 | Collection<String> fqans = Subjects.getFqans(subject); |
60 | |
61 | String storageClass = storageInfo.getStorageClass(); |
62 | String hsm = storageInfo.getHsm(); |
63 | |
64 | String storeUnit = ""; |
65 | if (storageClass != null && hsm != null) { |
66 | storeUnit = storageClass+"@"+hsm; |
67 | } |
68 | |
69 | if (dn == null) { |
70 | dn = ""; |
71 | } |
72 | |
73 | if (fqans.isEmpty()) { |
74 | return canPerformStaging(dn, "", storeUnit); |
75 | } else { |
76 | for (String fqan: fqans) { |
77 | if (canPerformStaging(dn, fqan, storeUnit)) |
78 | return true; |
79 | } |
80 | return false; |
81 | } |
82 | } catch (NoSuchElementException e) { |
83 | throw new IllegalArgumentException("Subject has multiple DNs"); |
84 | } |
85 | } |
86 | |
87 | /** |
88 | * Check whether staging is allowed for the user with given DN and FQAN |
89 | * for the object in the given storage group. |
90 | * |
91 | * @param dn user's Distinguished Name |
92 | * @param fqan user's Fully Qualified Attribute Name |
93 | * @param storeUnit object's store unit |
94 | * @return true if the user is allowed to perform staging of the object |
95 | * @throws PatternSyntaxException |
96 | * @throws IOException |
97 | */ |
98 | public boolean canPerformStaging(String dn, String fqan, String storeUnit) throws PatternSyntaxException, IOException { |
99 | |
100 | if ( !_isEnabled ) |
101 | return true; |
102 | |
103 | if ( !_stageConfigFile.exists() ) { |
104 | //if file does not exist, staging is denied for all users |
105 | return false; |
106 | } |
107 | |
108 | if ( fileNeedsRereading() ) |
109 | rereadConfig(); |
110 | |
111 | if (fqan==null) fqan=""; |
112 | |
113 | return userMatchesPredicates(dn, fqan, storeUnit); |
114 | } |
115 | |
116 | /** |
117 | * Reread the contents of the configuration file. |
118 | * @throws IOException |
119 | * @throws PatternSyntaxException |
120 | */ |
121 | void rereadConfig() throws PatternSyntaxException, IOException { |
122 | try { |
123 | _fileWriteLock.lock(); |
124 | if ( fileNeedsRereading() ) { |
125 | BufferedReader reader = new BufferedReader(new FileReader(_stageConfigFile)); |
126 | try { |
127 | _regexList = readStageConfigFile(reader); |
128 | _lastTimeReadingStageConfigFile = System.currentTimeMillis(); |
129 | } finally { |
130 | reader.close(); |
131 | } |
132 | } |
133 | } finally { |
134 | _fileWriteLock.unlock(); |
135 | } |
136 | } |
137 | |
138 | /** |
139 | * Check whether the stageConfigFile needs rereading. |
140 | * |
141 | * @return true if the file should be reread. |
142 | */ |
143 | boolean fileNeedsRereading() { |
144 | long modificationTimeStageConfigFile; |
145 | modificationTimeStageConfigFile = _stageConfigFile.lastModified(); |
146 | |
147 | return modificationTimeStageConfigFile > _lastTimeReadingStageConfigFile; |
148 | } |
149 | |
150 | /** |
151 | * Check whether the user matches predicates, that is, whether the user is in the |
152 | * list of authorized users that are allowed to perform staging of the object in the |
153 | * given storage group. |
154 | * |
155 | * @param dn user's Distinguished Name |
156 | * @param fqan user's FQAN as a String |
157 | * @param storeUnit object's storage unit |
158 | * @return true if the user and object match predicates |
159 | */ |
160 | boolean userMatchesPredicates(String dn, String fqanStr, String storeUnit) { |
161 | try { |
162 | _fileReadLock.lock(); |
163 | for (Pattern[] regexLine : _regexList) { |
164 | if ( regexLine[0].matcher(dn).matches() ) { |
165 | |
166 | if ( regexLine[1] == null ) { |
167 | return true; // line contains only DN; DN match -> STAGE allowed |
168 | } else if ( regexLine[1].matcher(fqanStr).matches() && (regexLine[2] == null || regexLine[2].matcher(storeUnit).matches()) ) { |
169 | return true; |
170 | //two cases covered here: |
171 | //line contains DN and FQAN; DN and FQAN match -> STAGE allowed |
172 | //line contains DN, FQAN, storeUnit; DN, FQAN, storeUnit match -> STAGE allowed |
173 | } |
174 | } |
175 | } |
176 | } finally { |
177 | _fileReadLock.unlock(); |
178 | } |
179 | return false; |
180 | } |
181 | |
182 | /** |
183 | * Read configuration file and create list of compiled patterns, containing DNs and FQANs(optionally) |
184 | * of the users that are allowed to perform staging, |
185 | * as well as storage group of the object to be staged (optionally). |
186 | * |
187 | * @param reader |
188 | * @return list of compiled patterns |
189 | * @throws IOException |
190 | * @throws PatternSyntaxException |
191 | */ |
192 | List<Pattern[]> readStageConfigFile(BufferedReader reader) throws IOException, PatternSyntaxException { |
193 | |
194 | String line = null; |
195 | Pattern linePattern = Pattern.compile("\"([^\"]*)\"([ \t]+\"([^\"]*)\"([ \t]+\"([^\"]*)\")?)?"); |
196 | Matcher matcherLine; |
197 | |
198 | List<Pattern[]> regexList = new ArrayList<Pattern[]>(); |
199 | |
200 | while ((line = reader.readLine()) != null) { |
201 | |
202 | line = line.trim(); |
203 | matcherLine = linePattern.matcher(line); |
204 | if ( line.startsWith("#") || line.isEmpty() ) { //commented or empty line |
205 | continue; |
206 | } |
207 | |
208 | if ( !matcherLine.matches() ) |
209 | continue; |
210 | |
211 | Pattern[] arrayPattern = new Pattern[3]; |
212 | |
213 | String matchDN = matcherLine.group(1); |
214 | String matchFQAN = matcherLine.group(3); |
215 | String matchStoreUnit = matcherLine.group(5); |
216 | |
217 | if ( matchFQAN != null ) { |
218 | if (matchStoreUnit != null) { //line: DN, FQAN, StoreUnit |
219 | arrayPattern[0] = Pattern.compile(matchDN); |
220 | arrayPattern[1] = Pattern.compile(matchFQAN); |
221 | arrayPattern[2] = Pattern.compile(matchStoreUnit); |
222 | regexList.add(arrayPattern); |
223 | } else { //line: DN, FQAN |
224 | arrayPattern[0] = Pattern.compile(matchDN); |
225 | arrayPattern[1] = Pattern.compile(matchFQAN); |
226 | arrayPattern[2] = null; |
227 | regexList.add(arrayPattern); |
228 | } |
229 | } else { //line: DN |
230 | arrayPattern[0] = Pattern.compile(matchDN); |
231 | arrayPattern[1] = null; |
232 | arrayPattern[2] = null; |
233 | regexList.add(arrayPattern); |
234 | } |
235 | } |
236 | return regexList; |
237 | } |
238 | |
239 | } |