3802 |
21 Mar 16 |
nicklas |
1 |
package net.sf.basedb.reggie.projectarchive; |
3802 |
21 Mar 16 |
nicklas |
2 |
|
3802 |
21 Mar 16 |
nicklas |
3 |
|
3802 |
21 Mar 16 |
nicklas |
4 |
import java.util.HashSet; |
3802 |
21 Mar 16 |
nicklas |
5 |
import java.util.Iterator; |
3802 |
21 Mar 16 |
nicklas |
6 |
import java.util.Set; |
3802 |
21 Mar 16 |
nicklas |
7 |
import java.util.TimerTask; |
3802 |
21 Mar 16 |
nicklas |
8 |
|
3802 |
21 Mar 16 |
nicklas |
9 |
import net.sf.basedb.clients.web.extensions.service.ServiceControllerAction; |
3802 |
21 Mar 16 |
nicklas |
10 |
import net.sf.basedb.clients.web.extensions.service.Services; |
3802 |
21 Mar 16 |
nicklas |
11 |
import net.sf.basedb.core.Application; |
3802 |
21 Mar 16 |
nicklas |
12 |
import net.sf.basedb.core.DbControl; |
3802 |
21 Mar 16 |
nicklas |
13 |
import net.sf.basedb.core.FileServer; |
3802 |
21 Mar 16 |
nicklas |
14 |
import net.sf.basedb.core.Include; |
3802 |
21 Mar 16 |
nicklas |
15 |
import net.sf.basedb.core.ItemQuery; |
3802 |
21 Mar 16 |
nicklas |
16 |
import net.sf.basedb.core.Sample; |
3802 |
21 Mar 16 |
nicklas |
17 |
import net.sf.basedb.core.SessionControl; |
3802 |
21 Mar 16 |
nicklas |
18 |
import net.sf.basedb.core.query.Annotations; |
3802 |
21 Mar 16 |
nicklas |
19 |
import net.sf.basedb.core.query.Expressions; |
3802 |
21 Mar 16 |
nicklas |
20 |
import net.sf.basedb.core.query.Hql; |
3802 |
21 Mar 16 |
nicklas |
21 |
import net.sf.basedb.core.query.Restrictions; |
4306 |
17 Jan 17 |
nicklas |
22 |
import net.sf.basedb.opengrid.CmdResult; |
4306 |
17 Jan 17 |
nicklas |
23 |
import net.sf.basedb.opengrid.OpenGrid; |
4306 |
17 Jan 17 |
nicklas |
24 |
import net.sf.basedb.opengrid.RemoteHost; |
4306 |
17 Jan 17 |
nicklas |
25 |
import net.sf.basedb.opengrid.RemoteSession; |
4306 |
17 Jan 17 |
nicklas |
26 |
import net.sf.basedb.opengrid.ScriptBuilder; |
4306 |
17 Jan 17 |
nicklas |
27 |
import net.sf.basedb.opengrid.config.ConnectionInfo; |
3802 |
21 Mar 16 |
nicklas |
28 |
import net.sf.basedb.reggie.Reggie; |
3802 |
21 Mar 16 |
nicklas |
29 |
import net.sf.basedb.reggie.dao.Annotationtype; |
3802 |
21 Mar 16 |
nicklas |
30 |
import net.sf.basedb.reggie.dao.Fileserver; |
3802 |
21 Mar 16 |
nicklas |
31 |
import net.sf.basedb.reggie.dao.Subtype; |
4306 |
17 Jan 17 |
nicklas |
32 |
import net.sf.basedb.reggie.grid.ScriptUtil; |
3802 |
21 Mar 16 |
nicklas |
33 |
import net.sf.basedb.util.Values; |
3802 |
21 Mar 16 |
nicklas |
34 |
import net.sf.basedb.util.extensions.Extension; |
7076 |
27 Mar 23 |
nicklas |
35 |
import net.sf.basedb.util.extensions.logging.ExtensionsLog; |
7076 |
27 Mar 23 |
nicklas |
36 |
import net.sf.basedb.util.extensions.logging.ExtensionsLogger; |
3802 |
21 Mar 16 |
nicklas |
37 |
|
3802 |
21 Mar 16 |
nicklas |
38 |
import org.slf4j.LoggerFactory; |
3802 |
21 Mar 16 |
nicklas |
39 |
|
3802 |
21 Mar 16 |
nicklas |
40 |
/** |
3802 |
21 Mar 16 |
nicklas |
Service for managing file permission in the project archive. |
3802 |
21 Mar 16 |
nicklas |
It will check the database at regular intervals for cases and |
3802 |
21 Mar 16 |
nicklas |
load their {@link Annotationtype#CONSENT} annotation. Files |
3802 |
21 Mar 16 |
nicklas |
that correspond to a case with consent = "Yes" will get read |
3802 |
21 Mar 16 |
nicklas |
permissin for the group, all other files will be private. |
3802 |
21 Mar 16 |
nicklas |
46 |
|
3802 |
21 Mar 16 |
nicklas |
Note that checks are usually made in 2-hour intervals, but a job handler may |
3802 |
21 Mar 16 |
nicklas |
call {@link #setForceCheck()} when a job has ended to force a check at the next |
3802 |
21 Mar 16 |
nicklas |
timer event (typically within the next 5 seconds). |
3802 |
21 Mar 16 |
nicklas |
50 |
|
3802 |
21 Mar 16 |
nicklas |
@author nicklas |
3802 |
21 Mar 16 |
nicklas |
@since 4.3 |
3802 |
21 Mar 16 |
nicklas |
53 |
*/ |
3802 |
21 Mar 16 |
nicklas |
54 |
public class ProjectArchiveService |
3802 |
21 Mar 16 |
nicklas |
55 |
{ |
3802 |
21 Mar 16 |
nicklas |
56 |
|
7076 |
27 Mar 23 |
nicklas |
57 |
private static final ExtensionsLogger logger = |
7076 |
27 Mar 23 |
nicklas |
58 |
ExtensionsLog.getLogger("net.sf.basedb.reggie.project-archive-service", true).wrap(LoggerFactory.getLogger(ProjectArchiveService.class)); |
3802 |
21 Mar 16 |
nicklas |
59 |
|
3802 |
21 Mar 16 |
nicklas |
// The singleton |
3802 |
21 Mar 16 |
nicklas |
61 |
private static ProjectArchiveService instance = null; |
3802 |
21 Mar 16 |
nicklas |
62 |
|
3802 |
21 Mar 16 |
nicklas |
// At most 120 minutes between permission checks (1 when debugging) |
7076 |
27 Mar 23 |
nicklas |
64 |
private static final long MAX_WAIT_INTERVAL_NORMAL = 120 * 60000; |
7076 |
27 Mar 23 |
nicklas |
65 |
private static final long MAX_WAIT_INTERVAL_DEBUG = 1 * 60000; |
3802 |
21 Mar 16 |
nicklas |
66 |
|
3802 |
21 Mar 16 |
nicklas |
67 |
/** |
3802 |
21 Mar 16 |
nicklas |
Get the singleton instance of the service. If the service has |
3802 |
21 Mar 16 |
nicklas |
not been created yet it is created at this time. |
3802 |
21 Mar 16 |
nicklas |
70 |
*/ |
3802 |
21 Mar 16 |
nicklas |
71 |
public static final ProjectArchiveService getInstance() |
3802 |
21 Mar 16 |
nicklas |
72 |
{ |
3802 |
21 Mar 16 |
nicklas |
73 |
if (instance == null) |
3802 |
21 Mar 16 |
nicklas |
74 |
{ |
3802 |
21 Mar 16 |
nicklas |
75 |
synchronized (ProjectArchiveService.class) |
3802 |
21 Mar 16 |
nicklas |
76 |
{ |
3802 |
21 Mar 16 |
nicklas |
77 |
if (instance == null) |
3802 |
21 Mar 16 |
nicklas |
78 |
{ |
3802 |
21 Mar 16 |
nicklas |
79 |
ProjectArchiveService tmp = new ProjectArchiveService(); |
3802 |
21 Mar 16 |
nicklas |
80 |
instance = tmp; |
3802 |
21 Mar 16 |
nicklas |
81 |
} |
3802 |
21 Mar 16 |
nicklas |
82 |
} |
3802 |
21 Mar 16 |
nicklas |
83 |
} |
3802 |
21 Mar 16 |
nicklas |
84 |
return instance; |
3802 |
21 Mar 16 |
nicklas |
85 |
} |
3802 |
21 Mar 16 |
nicklas |
86 |
|
3802 |
21 Mar 16 |
nicklas |
87 |
private volatile boolean isRunning; |
3802 |
21 Mar 16 |
nicklas |
88 |
private SessionControl systemSc; |
3802 |
21 Mar 16 |
nicklas |
89 |
private Extension<ServiceControllerAction> ext; |
3802 |
21 Mar 16 |
nicklas |
90 |
private TimerTask timer; |
3802 |
21 Mar 16 |
nicklas |
91 |
|
3802 |
21 Mar 16 |
nicklas |
// Time when last check for permissions was made |
3802 |
21 Mar 16 |
nicklas |
93 |
private volatile long lastPermissionCheck; |
3802 |
21 Mar 16 |
nicklas |
// If flag is set, we force check for permissions |
3802 |
21 Mar 16 |
nicklas |
95 |
private volatile boolean forcePermissionCheck; |
3802 |
21 Mar 16 |
nicklas |
96 |
|
3802 |
21 Mar 16 |
nicklas |
97 |
private ProjectArchiveService() |
3802 |
21 Mar 16 |
nicklas |
98 |
{} |
3802 |
21 Mar 16 |
nicklas |
99 |
|
3802 |
21 Mar 16 |
nicklas |
100 |
/** |
3802 |
21 Mar 16 |
nicklas |
Is the service running or not? |
3802 |
21 Mar 16 |
nicklas |
102 |
*/ |
3802 |
21 Mar 16 |
nicklas |
103 |
public boolean isRunning() |
3802 |
21 Mar 16 |
nicklas |
104 |
{ |
3802 |
21 Mar 16 |
nicklas |
105 |
return isRunning; |
3802 |
21 Mar 16 |
nicklas |
106 |
} |
3802 |
21 Mar 16 |
nicklas |
107 |
|
3802 |
21 Mar 16 |
nicklas |
108 |
/** |
3802 |
21 Mar 16 |
nicklas |
Start the service if it is not running. |
3802 |
21 Mar 16 |
nicklas |
110 |
*/ |
3802 |
21 Mar 16 |
nicklas |
111 |
public synchronized void start(SessionControl systemSc, Extension<ServiceControllerAction> ext) |
3802 |
21 Mar 16 |
nicklas |
112 |
{ |
3802 |
21 Mar 16 |
nicklas |
113 |
if (!isRunning) |
3802 |
21 Mar 16 |
nicklas |
114 |
{ |
3802 |
21 Mar 16 |
nicklas |
115 |
logger.debug("Starting project-archive file permissions service"); |
3802 |
21 Mar 16 |
nicklas |
116 |
this.systemSc = systemSc; |
3802 |
21 Mar 16 |
nicklas |
117 |
this.ext = ext; |
3802 |
21 Mar 16 |
nicklas |
118 |
|
3802 |
21 Mar 16 |
nicklas |
119 |
Reggie.getRootSessionControl(systemSc); |
3802 |
21 Mar 16 |
nicklas |
120 |
|
3802 |
21 Mar 16 |
nicklas |
121 |
timer = Application.getScheduler().scheduleAtFixedRate( |
3802 |
21 Mar 16 |
nicklas |
122 |
new PermissionCheckTask(), 30000, 30000, false); |
3802 |
21 Mar 16 |
nicklas |
123 |
|
3802 |
21 Mar 16 |
nicklas |
124 |
isRunning = true; |
3802 |
21 Mar 16 |
nicklas |
125 |
logger.debug("Project-archive file permissions service is now running"); |
3802 |
21 Mar 16 |
nicklas |
126 |
} |
3802 |
21 Mar 16 |
nicklas |
127 |
} |
3802 |
21 Mar 16 |
nicklas |
128 |
|
3802 |
21 Mar 16 |
nicklas |
129 |
/** |
3802 |
21 Mar 16 |
nicklas |
Stop the service if it is running. |
3802 |
21 Mar 16 |
nicklas |
131 |
*/ |
3802 |
21 Mar 16 |
nicklas |
132 |
public synchronized void stop() |
3802 |
21 Mar 16 |
nicklas |
133 |
{ |
3802 |
21 Mar 16 |
nicklas |
134 |
if (isRunning) |
3802 |
21 Mar 16 |
nicklas |
135 |
{ |
3802 |
21 Mar 16 |
nicklas |
136 |
logger.debug("Stopping project-archive file permissions service"); |
3802 |
21 Mar 16 |
nicklas |
137 |
isRunning = false; |
3802 |
21 Mar 16 |
nicklas |
138 |
|
3802 |
21 Mar 16 |
nicklas |
139 |
if (timer != null) |
3802 |
21 Mar 16 |
nicklas |
140 |
{ |
3802 |
21 Mar 16 |
nicklas |
141 |
timer.cancel(); |
3802 |
21 Mar 16 |
nicklas |
142 |
timer = null; |
3802 |
21 Mar 16 |
nicklas |
143 |
} |
3802 |
21 Mar 16 |
nicklas |
144 |
systemSc = null; |
3802 |
21 Mar 16 |
nicklas |
145 |
ext = null; |
3802 |
21 Mar 16 |
nicklas |
146 |
lastPermissionCheck = 0; |
3802 |
21 Mar 16 |
nicklas |
147 |
forcePermissionCheck = false; |
3802 |
21 Mar 16 |
nicklas |
148 |
Reggie.closeRootSessionControl(); |
3802 |
21 Mar 16 |
nicklas |
149 |
|
3802 |
21 Mar 16 |
nicklas |
150 |
logger.debug("Project-archive file permissions service has stopped"); |
3802 |
21 Mar 16 |
nicklas |
151 |
} |
3802 |
21 Mar 16 |
nicklas |
152 |
} |
3802 |
21 Mar 16 |
nicklas |
153 |
|
3802 |
21 Mar 16 |
nicklas |
154 |
/** |
3802 |
21 Mar 16 |
nicklas |
Restart the service. |
3802 |
21 Mar 16 |
nicklas |
156 |
*/ |
3802 |
21 Mar 16 |
nicklas |
157 |
public synchronized void restart() |
3802 |
21 Mar 16 |
nicklas |
158 |
{ |
3802 |
21 Mar 16 |
nicklas |
159 |
if (ext != null) Services.restart(ext); |
3802 |
21 Mar 16 |
nicklas |
160 |
} |
3802 |
21 Mar 16 |
nicklas |
161 |
|
3802 |
21 Mar 16 |
nicklas |
162 |
/** |
3802 |
21 Mar 16 |
nicklas |
Set a flag forcing the service to perform a check at the next |
3802 |
21 Mar 16 |
nicklas |
timer event. This method is intended to be called when a |
3802 |
21 Mar 16 |
nicklas |
there is a known change in permissions. |
3802 |
21 Mar 16 |
nicklas |
166 |
*/ |
3802 |
21 Mar 16 |
nicklas |
167 |
public void setForceCheck() |
3802 |
21 Mar 16 |
nicklas |
168 |
{ |
3802 |
21 Mar 16 |
nicklas |
169 |
this.forcePermissionCheck = true; |
3802 |
21 Mar 16 |
nicklas |
170 |
} |
3802 |
21 Mar 16 |
nicklas |
171 |
|
3802 |
21 Mar 16 |
nicklas |
172 |
/** |
3802 |
21 Mar 16 |
nicklas |
Perform a file permission check. Note that this method may return without doing |
3802 |
21 Mar 16 |
nicklas |
anything depending on how long time it was since the last check and if |
3802 |
21 Mar 16 |
nicklas |
the {@link #setForceCheck()} has been called or not. |
3802 |
21 Mar 16 |
nicklas |
176 |
*/ |
3802 |
21 Mar 16 |
nicklas |
177 |
void permissionCheck() |
3802 |
21 Mar 16 |
nicklas |
178 |
{ |
3802 |
21 Mar 16 |
nicklas |
179 |
long now = System.currentTimeMillis(); |
3802 |
21 Mar 16 |
nicklas |
180 |
long timeSinceLastCheck = now - lastPermissionCheck; |
3802 |
21 Mar 16 |
nicklas |
181 |
|
3802 |
21 Mar 16 |
nicklas |
// If we have not waited long enough and there there is no recent request we don't have to check |
7076 |
27 Mar 23 |
nicklas |
183 |
long maxWaitInterval = logger.isDebugEnabled() ? MAX_WAIT_INTERVAL_DEBUG : MAX_WAIT_INTERVAL_NORMAL; |
7076 |
27 Mar 23 |
nicklas |
184 |
if (timeSinceLastCheck < maxWaitInterval && !forcePermissionCheck) |
3802 |
21 Mar 16 |
nicklas |
185 |
{ |
7076 |
27 Mar 23 |
nicklas |
186 |
logger.debug("No project-archive permission check since no recent request and not long enough wait time [" + (timeSinceLastCheck / 1000) + " seconds]"); |
3802 |
21 Mar 16 |
nicklas |
187 |
return; |
3802 |
21 Mar 16 |
nicklas |
188 |
} |
3802 |
21 Mar 16 |
nicklas |
189 |
|
3802 |
21 Mar 16 |
nicklas |
190 |
forcePermissionCheck = false; |
3802 |
21 Mar 16 |
nicklas |
191 |
lastPermissionCheck = now; |
7076 |
27 Mar 23 |
nicklas |
192 |
logger.debug("Time for project-archive permission check [" + (timeSinceLastCheck / 1000) + " seconds]"); |
3802 |
21 Mar 16 |
nicklas |
193 |
|
3802 |
21 Mar 16 |
nicklas |
194 |
SessionControl sc = Reggie.getRootSessionControl(systemSc); |
3802 |
21 Mar 16 |
nicklas |
195 |
DbControl dc = null; |
3802 |
21 Mar 16 |
nicklas |
196 |
try |
3802 |
21 Mar 16 |
nicklas |
197 |
{ |
6599 |
22 Feb 22 |
nicklas |
198 |
dc = sc.newDbControl("Reggie: Project archive service"); |
3802 |
21 Mar 16 |
nicklas |
199 |
|
3802 |
21 Mar 16 |
nicklas |
// Load all specimen with parent case that has with Consent=Yes |
3802 |
21 Mar 16 |
nicklas |
201 |
ItemQuery<Sample> query = Sample.getQuery(); |
3802 |
21 Mar 16 |
nicklas |
202 |
Subtype.SPECIMEN.addFilter(dc, query); |
3802 |
21 Mar 16 |
nicklas |
203 |
query.setIncludes(sc.getActiveProjectId() != 0 ? Reggie.INCLUDE_IN_CURRENT_PROJECT : Include.ALL); |
3802 |
21 Mar 16 |
nicklas |
// Join Case |
3802 |
21 Mar 16 |
nicklas |
205 |
query.join(Hql.innerJoin("parent", "cse")); |
3802 |
21 Mar 16 |
nicklas |
// Consent==Yes |
3802 |
21 Mar 16 |
nicklas |
207 |
query.join(Annotations.innerJoin("cse", Annotationtype.CONSENT.load(dc), "cs")); |
3802 |
21 Mar 16 |
nicklas |
208 |
query.restrict(Restrictions.eq(Hql.alias("cs"), Expressions.string("Yes"))); |
3802 |
21 Mar 16 |
nicklas |
209 |
|
7076 |
27 Mar 23 |
nicklas |
210 |
if (logger.isDebugEnabled()) logger.debug(query.toQl(dc)); |
3802 |
21 Mar 16 |
nicklas |
211 |
|
6565 |
01 Feb 22 |
nicklas |
212 |
Set<String> itemNamesWithConsent = new HashSet<String>(); |
3802 |
21 Mar 16 |
nicklas |
213 |
Iterator<Sample> it = query.iterate(dc); |
3802 |
21 Mar 16 |
nicklas |
214 |
while (it.hasNext()) |
3802 |
21 Mar 16 |
nicklas |
215 |
{ |
6565 |
01 Feb 22 |
nicklas |
216 |
itemNamesWithConsent.add(it.next().getName()); |
3802 |
21 Mar 16 |
nicklas |
217 |
} |
6565 |
01 Feb 22 |
nicklas |
218 |
|
6565 |
01 Feb 22 |
nicklas |
// Load blood items with Consent=Yes |
6565 |
01 Feb 22 |
nicklas |
220 |
query = Sample.getQuery(); |
6565 |
01 Feb 22 |
nicklas |
221 |
Subtype.BLOOD.addFilter(dc, query); |
6565 |
01 Feb 22 |
nicklas |
222 |
query.setIncludes(sc.getActiveProjectId() != 0 ? Reggie.INCLUDE_IN_CURRENT_PROJECT : Include.ALL); |
6565 |
01 Feb 22 |
nicklas |
// Consent==Yes |
6565 |
01 Feb 22 |
nicklas |
224 |
query.join(Annotations.innerJoin(Annotationtype.CONSENT.load(dc), "cs")); |
6565 |
01 Feb 22 |
nicklas |
225 |
query.restrict(Restrictions.eq(Hql.alias("cs"), Expressions.string("Yes"))); |
3802 |
21 Mar 16 |
nicklas |
226 |
|
7076 |
27 Mar 23 |
nicklas |
227 |
if (logger.isDebugEnabled()) logger.debug(query.toQl(dc)); |
6565 |
01 Feb 22 |
nicklas |
228 |
|
6565 |
01 Feb 22 |
nicklas |
229 |
it = query.iterate(dc); |
6565 |
01 Feb 22 |
nicklas |
230 |
while (it.hasNext()) |
6565 |
01 Feb 22 |
nicklas |
231 |
{ |
6565 |
01 Feb 22 |
nicklas |
232 |
itemNamesWithConsent.add(it.next().getName()); |
6565 |
01 Feb 22 |
nicklas |
233 |
} |
6565 |
01 Feb 22 |
nicklas |
234 |
|
3802 |
21 Mar 16 |
nicklas |
235 |
if (logger.isDebugEnabled()) |
3802 |
21 Mar 16 |
nicklas |
236 |
{ |
6565 |
01 Feb 22 |
nicklas |
237 |
logger.debug("Found " + itemNamesWithConsent.size() + " specimen/blood items with Consent=Yes"); |
3802 |
21 Mar 16 |
nicklas |
238 |
} |
3802 |
21 Mar 16 |
nicklas |
239 |
|
3802 |
21 Mar 16 |
nicklas |
240 |
FileServer projectArchive = Fileserver.PROJECT_ARCHIVE.load(dc); |
7052 |
21 Feb 23 |
nicklas |
241 |
FileServer projectArchiveDNA = Fileserver.PROJECT_ARCHIVE_DNA.load(dc); |
3802 |
21 Mar 16 |
nicklas |
242 |
dc.close(); |
7052 |
21 Feb 23 |
nicklas |
243 |
|
7052 |
21 Feb 23 |
nicklas |
244 |
permissionCheck(projectArchive, itemNamesWithConsent); |
7052 |
21 Feb 23 |
nicklas |
245 |
permissionCheck(projectArchiveDNA, itemNamesWithConsent); |
7052 |
21 Feb 23 |
nicklas |
246 |
|
7052 |
21 Feb 23 |
nicklas |
247 |
} |
7052 |
21 Feb 23 |
nicklas |
248 |
finally |
7052 |
21 Feb 23 |
nicklas |
249 |
{ |
7052 |
21 Feb 23 |
nicklas |
250 |
lastPermissionCheck = System.currentTimeMillis(); |
7052 |
21 Feb 23 |
nicklas |
251 |
if (dc != null) dc.close(); |
7052 |
21 Feb 23 |
nicklas |
252 |
} |
7052 |
21 Feb 23 |
nicklas |
253 |
} |
7052 |
21 Feb 23 |
nicklas |
254 |
|
7052 |
21 Feb 23 |
nicklas |
255 |
|
7052 |
21 Feb 23 |
nicklas |
256 |
private void permissionCheck(FileServer fileServer, Set<String> itemNamesWithConsent) |
7052 |
21 Feb 23 |
nicklas |
257 |
{ |
7052 |
21 Feb 23 |
nicklas |
258 |
if (logger.isDebugEnabled()) |
7052 |
21 Feb 23 |
nicklas |
259 |
{ |
7076 |
27 Mar 23 |
nicklas |
260 |
logger.debug(fileServer.getName() + ": " + fileServer.getHost()+":"+fileServer.getRootPath()); |
7052 |
21 Feb 23 |
nicklas |
261 |
} |
7052 |
21 Feb 23 |
nicklas |
262 |
|
7052 |
21 Feb 23 |
nicklas |
263 |
RemoteSession session = null; |
7052 |
21 Feb 23 |
nicklas |
264 |
int numItems = 0; |
7052 |
21 Feb 23 |
nicklas |
265 |
try |
7052 |
21 Feb 23 |
nicklas |
266 |
{ |
7052 |
21 Feb 23 |
nicklas |
// Get the project archive and connect to it via SSH |
7052 |
21 Feb 23 |
nicklas |
268 |
RemoteHost host = new RemoteHost(new ConnectionInfo(fileServer)); |
7052 |
21 Feb 23 |
nicklas |
269 |
String rootPath = ScriptUtil.checkValidPath(fileServer.getRootPath(), true, true); |
7052 |
21 Feb 23 |
nicklas |
270 |
session = host.connect(5); |
3802 |
21 Mar 16 |
nicklas |
271 |
|
3802 |
21 Mar 16 |
nicklas |
// List all files in root folder. Returns one entry per line white-space-separated columns: |
3802 |
21 Mar 16 |
nicklas |
// <permissions> <'d' or 'f'> <path> |
3802 |
21 Mar 16 |
nicklas |
// Example: 750 d 1234567.1/l.r.m.c.lib.g |
4573 |
15 Sep 17 |
nicklas |
// -L option is important since the top-level (aka. "site") directory may be a symbolic link |
7076 |
27 Mar 23 |
nicklas |
276 |
String cmd = "find -L " + rootPath + " -printf '%m %y %P\\n'"; |
7076 |
27 Mar 23 |
nicklas |
277 |
if (logger.isDebugEnabled()) logger.debug(cmd); |
7052 |
21 Feb 23 |
nicklas |
278 |
CmdResult<String> find = session.executeCmd(cmd, 120); |
3802 |
21 Mar 16 |
nicklas |
279 |
if (logger.isTraceEnabled()) logger.trace(find.toString()); |
3802 |
21 Mar 16 |
nicklas |
280 |
|
3802 |
21 Mar 16 |
nicklas |
281 |
if (find.getExitStatus() != 0) |
3802 |
21 Mar 16 |
nicklas |
282 |
{ |
3802 |
21 Mar 16 |
nicklas |
283 |
logger.error("find failed: " + find); |
3802 |
21 Mar 16 |
nicklas |
284 |
return; |
3802 |
21 Mar 16 |
nicklas |
285 |
} |
3802 |
21 Mar 16 |
nicklas |
286 |
|
3802 |
21 Mar 16 |
nicklas |
287 |
String[] lines = find.getStdout().split("\\n"); |
3918 |
03 May 16 |
nicklas |
288 |
if (logger.isDebugEnabled()) |
3918 |
03 May 16 |
nicklas |
289 |
{ |
7076 |
27 Mar 23 |
nicklas |
290 |
logger.debug("Found " + lines.length + " files/directories in " + rootPath); |
3918 |
03 May 16 |
nicklas |
291 |
} |
3918 |
03 May 16 |
nicklas |
292 |
|
3802 |
21 Mar 16 |
nicklas |
293 |
int numNoChange = 0; |
3802 |
21 Mar 16 |
nicklas |
294 |
int numToChange = 0; |
3918 |
03 May 16 |
nicklas |
295 |
int numChanged = 0; |
3918 |
03 May 16 |
nicklas |
296 |
int numFailed = 0; |
3918 |
03 May 16 |
nicklas |
297 |
int lastLine = lines.length - 1; |
3802 |
21 Mar 16 |
nicklas |
298 |
ScriptBuilder script = new ScriptBuilder(); |
3918 |
03 May 16 |
nicklas |
299 |
for (int lineNo = 0; lineNo < lines.length; lineNo++) |
3802 |
21 Mar 16 |
nicklas |
300 |
{ |
3918 |
03 May 16 |
nicklas |
301 |
String line = lines[lineNo]; |
3802 |
21 Mar 16 |
nicklas |
302 |
if (logger.isTraceEnabled()) logger.trace(line); |
3802 |
21 Mar 16 |
nicklas |
303 |
String[] entry = line.split("\\s+", 3); |
3802 |
21 Mar 16 |
nicklas |
304 |
if (entry.length < 3) continue; |
3802 |
21 Mar 16 |
nicklas |
305 |
|
3802 |
21 Mar 16 |
nicklas |
306 |
String path = entry[2]; |
3802 |
21 Mar 16 |
nicklas |
// Get rid of path that we do not care about |
3802 |
21 Mar 16 |
nicklas |
308 |
if (path.startsWith("debug")) path = path.substring(5); |
3802 |
21 Mar 16 |
nicklas |
309 |
if (path.startsWith("/")) path = path.substring(1); |
4573 |
15 Sep 17 |
nicklas |
// If the path starts with a two-digit directory (eg. 11/) we remove the first 3 characters |
4573 |
15 Sep 17 |
nicklas |
311 |
if (path.matches("\\d\\d/.*")) path = path.substring(3); |
4573 |
15 Sep 17 |
nicklas |
312 |
if (path.length() > 2) // '2' since we do not want to touch the site-directories |
3802 |
21 Mar 16 |
nicklas |
313 |
{ |
3918 |
03 May 16 |
nicklas |
314 |
numItems++; |
3918 |
03 May 16 |
nicklas |
// Get the part before the first '/' |
3918 |
03 May 16 |
nicklas |
// This should be the specimen name |
3918 |
03 May 16 |
nicklas |
317 |
int dirIndex = path.indexOf("/"); |
3918 |
03 May 16 |
nicklas |
318 |
String spName = dirIndex < 0 ? path : path.substring(0, dirIndex); |
3918 |
03 May 16 |
nicklas |
319 |
|
3918 |
03 May 16 |
nicklas |
320 |
int permissions = Values.getInt(entry[0]); |
3918 |
03 May 16 |
nicklas |
321 |
boolean isDirectory = "d".equals(entry[1]); |
6565 |
01 Feb 22 |
nicklas |
322 |
int expectedPermissions = (itemNamesWithConsent.contains(spName) ? |
3918 |
03 May 16 |
nicklas |
323 |
FilePermission.CONSENT : FilePermission.NO_CONSENT).getPermission(isDirectory); |
3918 |
03 May 16 |
nicklas |
324 |
if (expectedPermissions != permissions) |
3802 |
21 Mar 16 |
nicklas |
325 |
{ |
3918 |
03 May 16 |
nicklas |
326 |
numToChange++; |
3918 |
03 May 16 |
nicklas |
// A permission change is needed |
3918 |
03 May 16 |
nicklas |
328 |
if (logger.isDebugEnabled()) |
3918 |
03 May 16 |
nicklas |
329 |
{ |
3918 |
03 May 16 |
nicklas |
330 |
logger.debug(entry[2] + " need permissions change: " + permissions + " -> "+expectedPermissions); |
3918 |
03 May 16 |
nicklas |
331 |
} |
3918 |
03 May 16 |
nicklas |
332 |
script.cmd("chmod " + expectedPermissions + " " + rootPath + "/" + entry[2]); |
3802 |
21 Mar 16 |
nicklas |
333 |
} |
3918 |
03 May 16 |
nicklas |
334 |
else |
3918 |
03 May 16 |
nicklas |
335 |
{ |
3918 |
03 May 16 |
nicklas |
336 |
numNoChange++; |
3918 |
03 May 16 |
nicklas |
337 |
if (logger.isTraceEnabled()) |
3918 |
03 May 16 |
nicklas |
338 |
{ |
3918 |
03 May 16 |
nicklas |
339 |
logger.trace(entry[2] + " has expected permissions: " + permissions); |
3918 |
03 May 16 |
nicklas |
340 |
} |
3918 |
03 May 16 |
nicklas |
341 |
} |
3802 |
21 Mar 16 |
nicklas |
342 |
} |
3918 |
03 May 16 |
nicklas |
343 |
|
3918 |
03 May 16 |
nicklas |
344 |
if (numToChange == 100 || (lineNo == lastLine && numToChange > 0)) |
3802 |
21 Mar 16 |
nicklas |
345 |
{ |
3918 |
03 May 16 |
nicklas |
// Update the file permissions |
3918 |
03 May 16 |
nicklas |
347 |
if (logger.isTraceEnabled()) logger.trace(script.toString()); |
7052 |
21 Feb 23 |
nicklas |
348 |
CmdResult<String> chmod = session.executeCmd(script.toString(), 30); |
3918 |
03 May 16 |
nicklas |
349 |
if (logger.isTraceEnabled()) logger.trace(chmod.toString()); |
3918 |
03 May 16 |
nicklas |
350 |
|
3918 |
03 May 16 |
nicklas |
351 |
if (chmod.getExitStatus() == 0) |
3802 |
21 Mar 16 |
nicklas |
352 |
{ |
3918 |
03 May 16 |
nicklas |
353 |
if (logger.isDebugEnabled()) |
3918 |
03 May 16 |
nicklas |
354 |
{ |
3918 |
03 May 16 |
nicklas |
355 |
logger.debug("File permissions modified on " + numToChange + " files"); |
3918 |
03 May 16 |
nicklas |
356 |
} |
3918 |
03 May 16 |
nicklas |
357 |
numChanged += numToChange; |
3802 |
21 Mar 16 |
nicklas |
358 |
} |
3918 |
03 May 16 |
nicklas |
359 |
else |
3918 |
03 May 16 |
nicklas |
360 |
{ |
3918 |
03 May 16 |
nicklas |
361 |
numFailed += numToChange; |
3918 |
03 May 16 |
nicklas |
362 |
logger.error("chmod failed: " + chmod); |
3918 |
03 May 16 |
nicklas |
363 |
} |
3918 |
03 May 16 |
nicklas |
364 |
|
3918 |
03 May 16 |
nicklas |
365 |
script = new ScriptBuilder(); |
3918 |
03 May 16 |
nicklas |
366 |
numToChange = 0; |
3802 |
21 Mar 16 |
nicklas |
367 |
} |
3802 |
21 Mar 16 |
nicklas |
368 |
} |
7076 |
27 Mar 23 |
nicklas |
369 |
logger.info(fileServer.getName()+": Checked " + numItems + " file permissions; " + |
3918 |
03 May 16 |
nicklas |
370 |
numChanged + " was modified, " + |
3918 |
03 May 16 |
nicklas |
371 |
numFailed + " failed and " + |
3918 |
03 May 16 |
nicklas |
372 |
numNoChange + " had expected permissions."); |
3802 |
21 Mar 16 |
nicklas |
373 |
|
3802 |
21 Mar 16 |
nicklas |
374 |
} |
7076 |
27 Mar 23 |
nicklas |
375 |
catch (RuntimeException ex) |
7076 |
27 Mar 23 |
nicklas |
376 |
{ |
7076 |
27 Mar 23 |
nicklas |
377 |
logger.error(fileServer.getName()+": "+ex.getMessage(), ex); |
7076 |
27 Mar 23 |
nicklas |
378 |
} |
3802 |
21 Mar 16 |
nicklas |
379 |
finally |
3802 |
21 Mar 16 |
nicklas |
380 |
{ |
7052 |
21 Feb 23 |
nicklas |
381 |
OpenGrid.close(session); |
7076 |
27 Mar 23 |
nicklas |
382 |
logger.debug(fileServer.getName()+": Processed project-archive permission checks for " + numItems + " items"); |
3802 |
21 Mar 16 |
nicklas |
383 |
} |
3802 |
21 Mar 16 |
nicklas |
384 |
} |
3802 |
21 Mar 16 |
nicklas |
385 |
|
3802 |
21 Mar 16 |
nicklas |
386 |
|
3802 |
21 Mar 16 |
nicklas |
387 |
/** |
3802 |
21 Mar 16 |
nicklas |
Timer for permission checks |
3802 |
21 Mar 16 |
nicklas |
389 |
*/ |
3802 |
21 Mar 16 |
nicklas |
390 |
static class PermissionCheckTask |
3802 |
21 Mar 16 |
nicklas |
391 |
extends TimerTask |
3802 |
21 Mar 16 |
nicklas |
392 |
{ |
3802 |
21 Mar 16 |
nicklas |
393 |
public PermissionCheckTask() |
3802 |
21 Mar 16 |
nicklas |
394 |
{} |
3802 |
21 Mar 16 |
nicklas |
395 |
|
3802 |
21 Mar 16 |
nicklas |
396 |
@Override |
3802 |
21 Mar 16 |
nicklas |
397 |
public void run() |
3802 |
21 Mar 16 |
nicklas |
398 |
{ |
3802 |
21 Mar 16 |
nicklas |
399 |
try |
3802 |
21 Mar 16 |
nicklas |
400 |
{ |
3802 |
21 Mar 16 |
nicklas |
401 |
getInstance().permissionCheck(); |
3802 |
21 Mar 16 |
nicklas |
402 |
} |
3802 |
21 Mar 16 |
nicklas |
403 |
catch (Exception ex) |
3802 |
21 Mar 16 |
nicklas |
404 |
{ |
3802 |
21 Mar 16 |
nicklas |
405 |
logger.error("Exception when performing project-archive permission check", ex); |
3802 |
21 Mar 16 |
nicklas |
406 |
} |
3802 |
21 Mar 16 |
nicklas |
407 |
} |
3802 |
21 Mar 16 |
nicklas |
408 |
|
3802 |
21 Mar 16 |
nicklas |
409 |
} |
3802 |
21 Mar 16 |
nicklas |
410 |
|
3802 |
21 Mar 16 |
nicklas |
411 |
} |