6 changed files with 553 additions and 40 deletions
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
<%@ include file="/edit/kern/sichtrechte_helper.inc" %> |
||||
<% |
||||
/* Statusdaten fuer die Maskenzeilen ermitteln (Gruppe 0 ist gueltig). */ |
||||
int srSubjectId = srInt(tid); |
||||
SRData srListData = null; |
||||
if (srSubjectId >= 0) { |
||||
try (Connection srCon = srConnection(mandantenid)) { |
||||
try { srCon.setReadOnly(true); } catch (SQLException srReadOnlyEx) { |
||||
application.log("Read-only fuer Sichtenpruefung nicht verfuegbar", srReadOnlyEx); |
||||
} |
||||
srListData = srLoad(srCon, "group", srSubjectId, |
||||
srAssignedMasks(srCon, "group", srSubjectId)); |
||||
} catch (Exception srEx) { |
||||
application.log("SuperX Sichtrechte-Status: Analyse fehlgeschlagen", srEx); |
||||
} |
||||
} |
||||
%> |
||||
<tr> |
||||
<td class="db_label">Masken</td><td> |
||||
<table class="subform"> |
||||
<db:dbform orderBy="" childField="groupinfo_id" parentField="tid" tableName="group_masken_bez" maxRows="*" captionResource="true" followUp="/edit/kern/groupinfo_edit.jsp" autoUpdate="false" multipart="false" dbConnectionName="<%= mandantenid %>"> |
||||
<db:header/> |
||||
<db:body allowNew="true"> |
||||
<tr class="subform_tr"><db:textField hidden="true" fieldName="groupinfo_id" overrideValue="<%= tid%>"/> |
||||
<td align="left"> |
||||
<div id="fld_maskeninfo_id"> |
||||
<input onblur="updateSelect(this)" style="display:none" size="10" name="key_group_masken_bezmaskeninfo_id" class="schluesselfeld" type="text"> |
||||
<db:select id="inp_group_masken_bezmaskeninfo_id" styleClass="clsInputStyle" readOnly="false" onChange="updateKeyfield(this)" fieldName="maskeninfo_id" customEntry=",,true"><% sql = "select tid, sp_trunc_string((tid || ' - ' || trim(name)),60) from maskeninfo order by sp_trunc_string((tid || ' - ' || trim(name)),60);"; %><db:queryData name="group_masken_bez_maskeninfo38" query="<%= sql %>" dbConnectionName="<%= mandantenid %>" /> </db:select> |
||||
</div> |
||||
</td><td class="naviBtn"> |
||||
<db:updateButton styleClass="clsButtonStyle" style="height:18px" alt="button.update" src="../../images/save.svg" flavor="image" id="db_updateButton"/></td><td class="naviBtn"> |
||||
<db:deleteButton confirmMessage="button.delete.confirm" styleClass="clsButtonStyle" style="height:18px" alt="button.delete" src="../../images/delete.svg" id="db_deleteButton" flavor="image"/></td> |
||||
<td style="white-space:nowrap;vertical-align:middle"><% if (currentRow_group_masken_bez != null) { |
||||
int srMaskId=srInt(currentRow_group_masken_bez.get("maskeninfo_id")); |
||||
out.print(srInlineStatus(srListData,srMaskId,"group",srSubjectId)); |
||||
} %></td> |
||||
</tr> |
||||
</db:body> |
||||
<db:footer> |
||||
<tr class="subform_tr"> |
||||
<td><td class="naviBtn"><span connectId="new4" dojoType="tooltip">Neuen Datensatz erzeugen. Mit Mausklick auf diesen Button geht das Formular bzw. Unterformular in den Einfügemodus, alle vorhandenen Datensätze werden ausgeblendet. Zum Einfügen des neuen Datensatzes klicken Sie dann auf das Häkchen-Symbol rechts neben bzw. unter mir.</span> |
||||
<db:navNewButton styleClass="clsButtonStyle" style="height:18px" showAlwaysInFooter="false" alt="button.nav.new" src="../../images/context_add.svg" id="new4" flavor="image"/></td><td class="naviBtn"><span connectId="ins4" dojoType="tooltip">Datensatz einfügen. Wenn Sie einen neuen Datensatz erzeugt haben, können Sie diesen mit diesem Button speichern.</span> |
||||
<db:insertButton showAlways="false" style="height:18px" styleClass="clsButtonStyle" alt="Datensatz einfügen" src="../../images/circle_tick.svg" id="ins4" flavor="image"/></td></td> |
||||
</tr> |
||||
</db:footer> |
||||
</db:dbform> |
||||
</table> |
||||
</td> |
||||
</tr> |
||||
@ -0,0 +1,215 @@
@@ -0,0 +1,215 @@
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> |
||||
<%@ include file="/edit/kern/sichtrechte_helper.inc" %> |
||||
<% |
||||
if(request.getCharacterEncoding()==null) request.setCharacterEncoding("UTF-8"); |
||||
Object userobject=request.getSession().getAttribute("UserID"); |
||||
if(userobject==null) { %><jsp:forward page="/edit/not_authenticated.htm"/><% return; } |
||||
String userid=userobject.toString(); |
||||
Object mandantobject=request.getSession().getAttribute("MandantenID"); |
||||
String mandantenid=mandantobject==null?"":mandantobject.toString(); |
||||
if("default".equals(mandantenid)) mandantenid=""; |
||||
String typ=request.getParameter("typ"); |
||||
String db_form_name="group".equals(typ)?"groupinfo":"userinfo"; |
||||
String erlaubt="0"; |
||||
%> |
||||
<%@ include file="/edit/check_authentication.inc" %> |
||||
<% |
||||
if("0".equals(erlaubt)) { %><jsp:forward page="/edit/not_authorized.htm"/><% return; } |
||||
int subjectId=srInt(request.getParameter("id")); |
||||
int maskId=srInt(request.getParameter("maskeninfo_id")); |
||||
if((!"group".equals(typ) && !"user".equals(typ)) || subjectId<0 || maskId<=0) { |
||||
response.sendError(400,"Ungueltige Parameter"); return; |
||||
} |
||||
SRData data; |
||||
String maskName=""; |
||||
String error=null; |
||||
int saved=srInt(request.getParameter("saved")); |
||||
boolean writable=false; |
||||
try(Connection con=srConnection(mandantenid)) { |
||||
String subjectTable="group".equals(typ)?"groupinfo":"userinfo"; |
||||
if(!srExists(con,subjectTable,"tid",subjectId) || !srMaskAssigned(con,typ,subjectId,maskId)) { |
||||
response.sendError(404,"Keine gespeicherte Maskenzuordnung vorhanden"); return; |
||||
} |
||||
maskName=srMaskName(con,maskId); |
||||
writable=srMayWrite(con,srInt(userid),db_form_name); |
||||
if("POST".equalsIgnoreCase(request.getMethod()) && !writable) { |
||||
response.sendError(403,"Keine Schreibberechtigung für dieses DBForm"); return; |
||||
} |
||||
try { con.setReadOnly(true); } catch(SQLException e) { application.log("Read-only für Sichtenprüfung nicht verfügbar",e); } |
||||
data=srLoad(con,typ,subjectId,Collections.singleton(maskId)); |
||||
try { con.setReadOnly(false); } catch(SQLException e) { application.log("DB-Verbindung kann nicht auf schreibbar umgestellt werden",e); throw e; } |
||||
if("POST".equalsIgnoreCase(request.getMethod())) { |
||||
Set<String> allowedArts=new HashSet<String>(); |
||||
Map<String,SRView> allowedViews=new HashMap<String,SRView>(); |
||||
for(SRField f:data.fields.get(maskId)) for(SRView v:f.views) { |
||||
allowedArts.add(v.art); allowedViews.put(v.intern,v); |
||||
} |
||||
Set<String> newArts=new LinkedHashSet<String>(); |
||||
Set<String> newViews=new LinkedHashSet<String>(); |
||||
String[] postedArts=request.getParameterValues("art"); |
||||
String[] postedViews=request.getParameterValues("sicht"); |
||||
boolean valid=true; |
||||
if(postedArts!=null) for(String v:postedArts) { |
||||
if(!allowedArts.contains(v)) { valid=false; break; } |
||||
if(!data.rights.arts.contains(v)) newArts.add(v); |
||||
} |
||||
if(postedViews!=null) for(String v:postedViews) { |
||||
SRView view=allowedViews.get(v); |
||||
if(view==null) { valid=false; break; } |
||||
if(!data.rights.allows(view) && !newArts.contains(view.art)) newViews.add(v); |
||||
} |
||||
if(!valid) { |
||||
response.setStatus(400); |
||||
error="Die übermittelte Auswahl enthält nicht erlaubte Sichtrechte."; |
||||
} else if(newArts.isEmpty() && newViews.isEmpty()) { |
||||
error="Es wurden keine neuen Sichtrechte ausgewählt."; |
||||
} else { |
||||
try { |
||||
int count=srSave(con,typ,subjectId,newArts,newViews); |
||||
response.sendRedirect(response.encodeRedirectURL("masken_sichtrechte_edit.jsp?typ="+typ+ |
||||
"&id="+subjectId+"&maskeninfo_id="+maskId+"&saved="+count)); |
||||
return; |
||||
} catch(SQLException saveError) { |
||||
application.log("SuperX Sichtrechte: Speicherung fehlgeschlagen",saveError); |
||||
error="Die Berechtigungen konnten nicht gespeichert werden. Bitte Serverprotokoll prüfen."; |
||||
} |
||||
} |
||||
} |
||||
} catch(Exception ex) { |
||||
application.log("SuperX Sichtrechte: Analyse fehlgeschlagen",ex); |
||||
response.sendError(500,"Sichtrechte konnten nicht geladen werden; Details im Serverprotokoll."); return; |
||||
} |
||||
String backUrl="group".equals(typ)?"groupinfo_edit.jsp?tid="+subjectId:"userinfo_edit.jsp?tid="+subjectId; |
||||
List<SRField> fields=data.fields.get(maskId); |
||||
if(fields==null) fields=Collections.emptyList(); |
||||
%> |
||||
<!DOCTYPE html> |
||||
<html lang="de"> |
||||
<head> |
||||
<meta charset="UTF-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
||||
<title>Sichtrechte für Maske <%= maskId %></title> |
||||
<link href="/superx/style/superx.css" type="text/css" rel="stylesheet"> |
||||
<link href="/superx/style/dbforms_40.css" type="text/css" rel="stylesheet"> |
||||
<style> |
||||
.sr-wrap { max-width:1000px; padding:18px 26px 36px; font-family:Arial,Helvetica,sans-serif; color:#252a31; } |
||||
.sr-heading { margin:0 0 7px; font-size:24px; font-weight:500; } |
||||
.sr-sub { color:#54616d; margin:0 0 18px; font-size:14px; } |
||||
.sr-back { display:inline-block; margin-bottom:16px; } |
||||
.sr-field { margin:18px 0; border:1px solid #cbd6df; background:#fff; border-radius:5px; padding:16px 18px; } |
||||
.sr-field h2 { margin:0; font-size:18px; font-weight:600; } |
||||
.sr-field-head { display:flex; flex-wrap:wrap; justify-content:space-between; align-items:center; gap:10px; } |
||||
.sr-art { margin:13px 0 0; border:1px solid #dbe2e8; border-radius:4px; background:#f8fbfd; } |
||||
.sr-art-top { display:flex; align-items:center; flex-wrap:wrap; justify-content:space-between; gap:12px; padding:10px 14px; } |
||||
.sr-art-top button { border:1px solid #9daebb; background:#fff; border-radius:4px; padding:4px 9px; cursor:pointer; } |
||||
.sr-art-body { padding:5px 14px 12px; background:#fff; } |
||||
.sr-art-label { display:flex; align-items:center; gap:9px; } |
||||
.sr-view { margin:8px 0 8px 24px; display:block; } |
||||
.sr-pill { font-size:12px; padding:3px 8px; border-radius:4px; white-space:nowrap; } |
||||
.sr-ok { color:#126b38; background:#eaf6ed; } |
||||
.sr-missing { color:#a71d1d; background:#ffefef; } |
||||
.sr-unknown { color:#855800; background:#fff2d9; } |
||||
.sr-note { font-size:12px; color:#637180; margin-top:7px; } |
||||
.sr-alert { padding:12px 15px; margin:10px 0; background:#fff2d9; border-left:4px solid #b47b13; } |
||||
.sr-success { padding:12px 15px; margin:10px 0; background:#eaf6ed; border-left:4px solid #25854b; } |
||||
.sr-buttons { display:flex; flex-wrap:wrap; gap:10px; margin:22px 0; } |
||||
.sr-buttons button,.sr-buttons a { padding:8px 15px; border-radius:4px; border:1px solid #647e91; background:#f6f8fa; color:#1c3242; text-decoration:none; cursor:pointer; font-size:14px; } |
||||
.sr-buttons button.sr-save { background:#2079ac; border-color:#2079ac; color:#fff; font-weight:bold; } |
||||
.sr-buttons button:disabled { opacity:.6; cursor:not-allowed; } |
||||
</style> |
||||
<script> |
||||
function srToggleArt(checkbox) { |
||||
var id=checkbox.getAttribute('data-artid'); |
||||
var views=document.querySelectorAll('input[data-parent-art="'+id+'"]'); |
||||
for(var i=0;i<views.length;i++) { |
||||
views[i].disabled=checkbox.checked || views[i].getAttribute('data-already')==='1'; |
||||
} |
||||
// Mehrfach vorkommende Sichtarten im selben Formular synchron halten. |
||||
var matches=document.querySelectorAll('input[data-artid]'); |
||||
for(var j=0;j<matches.length;j++) { |
||||
if(matches[j]!==checkbox && matches[j].getAttribute('data-artid')===id) matches[j].checked=checkbox.checked; |
||||
} |
||||
} |
||||
function srExpand(button) { |
||||
var div=document.getElementById(button.getAttribute('aria-controls')); |
||||
div.hidden=!div.hidden; |
||||
button.setAttribute('aria-expanded',div.hidden?'false':'true'); |
||||
button.textContent=div.hidden?'Sichten anzeigen ▾':'Sichten ausblenden ▴'; |
||||
} |
||||
function srResetSelections() { window.setTimeout(function() { |
||||
var arts=document.querySelectorAll('input[data-artid]'); |
||||
for(var i=0;i<arts.length;i++) srToggleArt(arts[i]); |
||||
},0); } |
||||
</script> |
||||
</head> |
||||
<body class="clsPageBody"> |
||||
<main class="sr-wrap"> |
||||
<a class="sr-back" href="<%= srHtml(backUrl) %>">← Zurück zur <%= "group".equals(typ)?"Gruppen":"Benutzer" %>verwaltung</a> |
||||
<h1 class="sr-heading">Sichtrechte prüfen und ergänzen</h1> |
||||
<p class="sr-sub"><strong>Maske <%= maskId %> – <%= srHtml(maskName) %></strong><br> |
||||
<%= "group".equals(typ)?"Gruppe":"Benutzer" %> (TID <%= subjectId %>)</p> |
||||
<% if(!writable) { %><div class="sr-alert">Nur Leseberechtigung: Eine neue Rechtevergabe ist mit dieser Anmeldung nicht möglich.</div><% } |
||||
if(saved>0) { %><div class="sr-success"><%= saved %> neue Berechtigung(en) gespeichert. Der SuperX-Cache muss ggf. aktualisiert werden; betroffene Benutzer müssen sich anschließend neu anmelden.</div><% } |
||||
if(error!=null) { %><div class="sr-alert"><%= srHtml(error) %></div><% } %> |
||||
<p>Für jeden Filter werden die durch die Maskenrelation ermittelten Sichtarten angezeigt. Sie können eine ganze Sichtart oder darunter einzelne Sichten freigeben. Bereits wirksame Berechtigungen sind gekennzeichnet.</p> |
||||
<form method="post" action="masken_sichtrechte_edit.jsp?typ=<%= typ %>&id=<%= subjectId %>&maskeninfo_id=<%= maskId %>" onreset="srResetSelections()"> |
||||
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"> |
||||
<% if(fields.isEmpty()) { %><div class="sr-success">Diese Maske besitzt keine Sichtfilter (felderinfo.art = 12). Es werden keine zusätzlichen Sichtrechte benötigt.</div><% } |
||||
int fieldNum=0; |
||||
Map<String,String> domArtIds=new HashMap<String,String>(); |
||||
for(SRField f:fields) { |
||||
String fieldStatus=srFieldStatus(f,data.rights); |
||||
String label="ok".equals(fieldStatus)?"✓ Sichtrechte ausreichend":"missing".equals(fieldStatus)?"✕ Sichtenrechte fehlen":"? Prüfung unvollständig"; |
||||
%> |
||||
<section class="sr-field"> |
||||
<div class="sr-field-head"><h2>Filter: <%= srHtml(f.name) %></h2> |
||||
<span class="sr-pill <%= "ok".equals(fieldStatus)?"sr-ok":"missing".equals(fieldStatus)?"sr-missing":"sr-unknown" %>"><%= label %></span></div> |
||||
<p class="sr-note">Feld-TID: <%= f.id %></p> |
||||
<% if(f.problem!=null) { %><div class="sr-alert"><%= srHtml(f.problem) %> Für diesen Filter können hier keine Rechte automatisch vorgeschlagen werden.</div><% } |
||||
else { |
||||
Map<String,List<SRView>> byArt=new LinkedHashMap<String,List<SRView>>(); |
||||
for(SRView view:f.views) { |
||||
if(!byArt.containsKey(view.art)) byArt.put(view.art,new ArrayList<SRView>()); |
||||
byArt.get(view.art).add(view); |
||||
} |
||||
int artNum=0; |
||||
for(Map.Entry<String,List<SRView>> art:byArt.entrySet()) { |
||||
boolean existingArt=data.rights.admin || data.rights.arts.contains(art.getKey()); |
||||
String artDomId="art_"+(fieldNum)+"_"+(artNum++); |
||||
// Der data-artid-Wert ist HTML-escaped; DOM-Attribute sind eindeutige Indizes |
||||
// statt freier Sichtartnamen, damit Sonderzeichen die JavaScript-Auswahl nicht stören. |
||||
String commonId=domArtIds.get(art.getKey()); |
||||
if(commonId==null) { commonId="artkey"+domArtIds.size(); domArtIds.put(art.getKey(),commonId); } |
||||
%> |
||||
<div class="sr-art"> |
||||
<div class="sr-art-top"> |
||||
<label class="sr-art-label"><input type="checkbox" name="art" value="<%= srHtml(art.getKey()) %>" |
||||
data-artid="<%= commonId %>" onchange="srToggleArt(this)" <%= existingArt?"checked disabled":"" %>> |
||||
<strong><%= srHtml(art.getKey()) %></strong> <span class="sr-note">Ganze Sichtart freigeben <%= existingArt?"(bereits berechtigt)":"" %></span></label> |
||||
<button type="button" aria-controls="<%= artDomId %>" aria-expanded="false" onclick="srExpand(this)">Sichten anzeigen ▾</button> |
||||
</div> |
||||
<div class="sr-art-body" id="<%= artDomId %>" hidden> |
||||
<span class="sr-note"><%= art.getValue().size() %> Sichten dieser Sichtart</span> |
||||
<% for(SRView view:art.getValue()) { |
||||
boolean existingView=data.rights.allows(view); |
||||
%> |
||||
<label class="sr-view"><input type="checkbox" name="sicht" value="<%= srHtml(view.intern) %>" |
||||
data-parent-art="<%= commonId %>" data-already="<%= existingView?"1":"0" %>" |
||||
<%= existingView?"checked disabled":"" %>> |
||||
<%= srHtml(view.name) %> <span class="sr-note">(<%= srHtml(view.intern) %>) <%= existingView?"– bereits berechtigt":"" %></span></label> |
||||
<% } %> |
||||
</div> |
||||
</div> |
||||
<% } } %> |
||||
</section> |
||||
<% fieldNum++; } %> |
||||
<div class="sr-buttons"> |
||||
<button type="submit" class="sr-save" <%= fields.isEmpty() || !writable?"disabled":"" %>>Auswahl speichern</button> |
||||
<button type="reset">Auswahl zurücksetzen</button> |
||||
<a href="<%= srHtml(backUrl) %>">Zurück ohne Änderungen</a> |
||||
</div> |
||||
<p class="sr-note">Die Funktion ergänzt ausschließlich Rechte. Vorhandene Rechte werden nicht entfernt. Für das Entziehen verwenden Sie weiterhin die ursprüngliche Benutzer-/Gruppenverwaltung. Bei dynamischen Relationen (z.B. FreeMarker) erfolgt keine automatische Freigabe.</p> |
||||
</form> |
||||
</main> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,262 @@
@@ -0,0 +1,262 @@
|
||||
<%@ page import="java.sql.*,java.util.*,org.dbforms.config.DbFormsConfigRegistry" %> |
||||
<%! |
||||
/* Gemeinsame Ermittlung der Sichtrechte für userinfo_edit.jsp, |
||||
groupinfo_edit.jsp und masken_sichtrechte_edit.jsp. |
||||
Alle Methoden sind request-lokal nutzbar und enthalten keine globalen Daten/Caches. */ |
||||
private static class SRView { |
||||
int id; String intern, art, name; |
||||
SRView(int i,String n,String a,String label) { id=i; intern=n; art=a; name=label; } |
||||
} |
||||
private static class SRField { |
||||
int id; String name, relation, problem; |
||||
List<SRView> views = new ArrayList<SRView>(); |
||||
SRField(int i,String n,String r) { id=i; name=n; relation=r; } |
||||
} |
||||
private static class SRRights { |
||||
Set<String> arts = new HashSet<String>(); |
||||
Set<String> views = new HashSet<String>(); |
||||
boolean admin = false; |
||||
boolean allows(SRView v) { return admin || arts.contains(v.art) || views.contains(v.intern); } |
||||
} |
||||
private static class SRData { |
||||
Map<Integer,List<SRField>> fields = new LinkedHashMap<Integer,List<SRField>>(); |
||||
SRRights rights = new SRRights(); |
||||
Map<Integer,String> maskStatus = new HashMap<Integer,String>(); |
||||
} |
||||
private static String srHtml(Object value) { |
||||
if (value==null) return ""; |
||||
return String.valueOf(value).replace("&","&").replace("<","<") |
||||
.replace(">",">").replace("\"",""").replace("'","'"); |
||||
} |
||||
private static int srInt(Object value) { |
||||
if (value==null) return -1; |
||||
try { int n=Integer.parseInt(String.valueOf(value).trim()); return n>=0?n:-1; } |
||||
catch (Exception e) { return -1; } |
||||
} |
||||
private static String srTrim(String s) { return s==null?"":s.trim(); } |
||||
private static String srStatus(List<SRField> fields, SRRights rights) { |
||||
if (fields==null) return "unknown"; |
||||
boolean unknown=false, missing=false; |
||||
for (SRField f:fields) { |
||||
if (f.problem!=null || f.views.isEmpty()) { unknown=true; continue; } |
||||
boolean has=false; |
||||
for (SRView view:f.views) if (rights.allows(view)) { has=true; break; } |
||||
if (!has) missing=true; |
||||
} |
||||
return missing?"missing":(unknown?"unknown":"ok"); |
||||
} |
||||
private static String srFieldStatus(SRField f, SRRights rights) { |
||||
if (f.problem!=null || f.views.isEmpty()) return "unknown"; |
||||
for (SRView v:f.views) if (rights.allows(v)) return "ok"; |
||||
return "missing"; |
||||
} |
||||
private static Connection srConnection(String mandant) throws Exception { |
||||
return DbFormsConfigRegistry.instance().lookup().getConnection(mandant); |
||||
} |
||||
private static boolean srExists(Connection con,String table,String column,int id) throws SQLException { |
||||
try(PreparedStatement ps=con.prepareStatement("select 1 from "+table+" where "+column+"=?")) { |
||||
ps.setInt(1,id); try(ResultSet rs=ps.executeQuery()) { return rs.next(); } |
||||
} |
||||
} |
||||
private static boolean srMaskAssigned(Connection con,String typ,int id,int mask) throws SQLException { |
||||
String table="group".equals(typ)?"group_masken_bez":"user_masken_bez"; |
||||
String col="group".equals(typ)?"groupinfo_id":"userinfo_id"; |
||||
try(PreparedStatement ps=con.prepareStatement("select 1 from "+table+" where "+col+"=? and maskeninfo_id=?")) { |
||||
ps.setInt(1,id); ps.setInt(2,mask); |
||||
try(ResultSet rs=ps.executeQuery()) { return rs.next(); } |
||||
} |
||||
} |
||||
private static Set<Integer> srAssignedMasks(Connection con,String typ,int id) throws SQLException { |
||||
Set<Integer> result=new LinkedHashSet<Integer>(); |
||||
String table="group".equals(typ)?"group_masken_bez":"user_masken_bez"; |
||||
String col="group".equals(typ)?"groupinfo_id":"userinfo_id"; |
||||
try(PreparedStatement ps=con.prepareStatement("select maskeninfo_id from "+table+" where "+col+"=?")) { |
||||
ps.setInt(1,id); try(ResultSet rs=ps.executeQuery()) { while(rs.next()) result.add(rs.getInt(1)); } |
||||
} |
||||
return result; |
||||
} |
||||
private static void srAddRights(Connection con, Set<String> target, String sql, int id) throws SQLException { |
||||
try(PreparedStatement ps=con.prepareStatement(sql)) { |
||||
ps.setInt(1,id); |
||||
try(ResultSet rs=ps.executeQuery()) { |
||||
while(rs.next()) if(rs.getString(1)!=null) target.add(rs.getString(1).trim()); |
||||
} |
||||
} |
||||
} |
||||
private static SRRights srLoadRights(Connection con,String typ,int id) throws SQLException { |
||||
SRRights r=new SRRights(); |
||||
if("group".equals(typ)) { |
||||
srAddRights(con,r.views,"select sicht_name_intern from group_sichten where groupinfo_id=?",id); |
||||
srAddRights(con,r.arts,"select sichtart from group_sichtarten where groupinfo_id=?",id); |
||||
srAddRights(con,r.views,"select ss.sicht_name_intern from sachgeb_sichten ss join group_sachgeb_bez b on b.sachgebiete_id=ss.sachgebiete_id where b.groupinfo_id=?",id); |
||||
srAddRights(con,r.arts,"select ss.sichtart from sachgeb_sichtarten ss join group_sachgeb_bez b on b.sachgebiete_id=ss.sachgebiete_id where b.groupinfo_id=?",id); |
||||
} else { |
||||
try(PreparedStatement ps=con.prepareStatement("select administration from userinfo where tid=?")) { |
||||
ps.setInt(1,id); |
||||
try(ResultSet rs=ps.executeQuery()) { if(rs.next()) r.admin=rs.getInt(1)==1; } |
||||
} |
||||
srAddRights(con,r.views,"select sicht_name_intern from user_sichten where userinfo_id=?",id); |
||||
srAddRights(con,r.arts,"select sichtart from user_sichtarten where userinfo_id=?",id); |
||||
srAddRights(con,r.views,"select gs.sicht_name_intern from group_sichten gs join user_group_bez ug on ug.groupinfo_id=gs.groupinfo_id where ug.userinfo_id=?",id); |
||||
srAddRights(con,r.arts,"select gs.sichtart from group_sichtarten gs join user_group_bez ug on ug.groupinfo_id=gs.groupinfo_id where ug.userinfo_id=?",id); |
||||
srAddRights(con,r.views,"select ss.sicht_name_intern from sachgeb_sichten ss join user_sachgeb_bez b on b.sachgebiete_id=ss.sachgebiete_id where b.userinfo_id=?",id); |
||||
srAddRights(con,r.arts,"select ss.sichtart from sachgeb_sichtarten ss join user_sachgeb_bez b on b.sachgebiete_id=ss.sachgebiete_id where b.userinfo_id=?",id); |
||||
srAddRights(con,r.views,"select ss.sicht_name_intern from sachgeb_sichten ss join group_sachgeb_bez gb on gb.sachgebiete_id=ss.sachgebiete_id join user_group_bez ug on ug.groupinfo_id=gb.groupinfo_id where ug.userinfo_id=?",id); |
||||
srAddRights(con,r.arts,"select ss.sichtart from sachgeb_sichtarten ss join group_sachgeb_bez gb on gb.sachgebiete_id=ss.sachgebiete_id join user_group_bez ug on ug.groupinfo_id=gb.groupinfo_id where ug.userinfo_id=?",id); |
||||
} |
||||
return r; |
||||
} |
||||
/* Nur unparametrisierte SELECTs mit Sichten-Zugriff. Dynamische Relationen werden |
||||
nie automatisch ausgeführt und erhalten den Status "nicht prüfbar". */ |
||||
private static String srNormalizeSql(String raw) { |
||||
String s=srTrim(raw); |
||||
if (!s.regionMatches(true,0,"<<SQL>>",0,7)) return null; |
||||
s=s.substring(7).trim(); |
||||
if (s.contains("<#") || s.contains("</#") || s.contains("${") || s.contains("<<") || |
||||
s.toLowerCase(Locale.ROOT).contains("--freemarker")) return null; |
||||
// SQL-Zeilenkommentare sind erlaubt (z.B. der MAN-Dummy-Sicht-Kommentar). |
||||
s=s.replaceAll("(?m)--[^\\r\\n]*", " ").trim(); |
||||
if (s.endsWith(";")) s=s.substring(0,s.length()-1).trim(); |
||||
if (s.contains(";") || !s.matches("(?is)^select\\s+.*") || |
||||
!s.matches("(?is).*\\bfrom\\s+sichten\\b.*")) return null; |
||||
// Defensive Schranke, auch für SQL, das später erweitert wird. |
||||
if (s.matches("(?is).*(?:\\b(insert|update|delete|drop|alter|create|truncate|call|execute|grant|revoke)\\b).*")) return null; |
||||
return s; |
||||
} |
||||
private static Map<Integer,SRView> srAllViews(Connection con) throws SQLException { |
||||
Map<Integer,SRView> res=new LinkedHashMap<Integer,SRView>(); |
||||
try(PreparedStatement ps=con.prepareStatement("select tid, name_intern, art, name from sichten order by art, name, tid")) { |
||||
try(ResultSet rs=ps.executeQuery()) { |
||||
while(rs.next()) { |
||||
int n=rs.getInt(1); |
||||
res.put(n,new SRView(n,srTrim(rs.getString(2)),srTrim(rs.getString(3)),srTrim(rs.getString(4)))); |
||||
} |
||||
} |
||||
} |
||||
return res; |
||||
} |
||||
private static SRData srLoad(Connection con,String typ,int id, Set<Integer> maskIds) throws SQLException { |
||||
SRData data=new SRData(); |
||||
data.rights=srLoadRights(con,typ,id); |
||||
if(maskIds==null || maskIds.isEmpty()) return data; |
||||
for(Integer m:maskIds) data.fields.put(m,new ArrayList<SRField>()); |
||||
Map<Integer,SRView> allViews=srAllViews(con); |
||||
Map<String,List<SRView>> queryCache=new HashMap<String,List<SRView>>(); |
||||
StringBuilder placeholders=new StringBuilder(); |
||||
for(int i=0;i<maskIds.size();i++) { if(i>0) placeholders.append(','); placeholders.append('?'); } |
||||
String getFields="select b.maskeninfo_id, f.tid, f.name, f.relation " + |
||||
"from masken_felder_bez b join felderinfo f on f.tid=b.felderinfo_id " + |
||||
"where f.art=12 and b.maskeninfo_id in ("+placeholders+") order by b.maskeninfo_id, f.nummer, f.tid"; |
||||
try(PreparedStatement ps=con.prepareStatement(getFields)) { |
||||
int n=1; for(Integer m:maskIds) ps.setInt(n++,m); |
||||
try(ResultSet rs=ps.executeQuery()) { |
||||
while(rs.next()) { |
||||
int mask=rs.getInt(1); SRField f=new SRField(rs.getInt(2),srTrim(rs.getString(3)),rs.getString(4)); |
||||
List<SRField> ff=data.fields.get(mask); if(ff!=null) ff.add(f); |
||||
} |
||||
} |
||||
} |
||||
for(List<SRField> fields:data.fields.values()) for(SRField field:fields) { |
||||
String normalized=srNormalizeSql(field.relation); |
||||
if(normalized==null) { field.problem="Dynamische oder nicht unterstützte SQL-Relation (z.B. FreeMarker/Platzhalter)."; continue; } |
||||
if(!queryCache.containsKey(normalized)) { |
||||
try(PreparedStatement ps=con.prepareStatement(normalized)) { |
||||
ps.setMaxRows(5001); ps.setQueryTimeout(4); |
||||
List<SRView> found=new ArrayList<SRView>(); |
||||
Set<Integer> seen=new HashSet<Integer>(); |
||||
try(ResultSet rs=ps.executeQuery()) { |
||||
while(rs.next()) { |
||||
int tid=rs.getInt(1); |
||||
SRView view=allViews.get(tid); |
||||
if(!rs.wasNull() && view!=null && seen.add(tid)) found.add(view); |
||||
} |
||||
} |
||||
// Ein Trefferlimit darf nicht stillschweigend als vollständige Auswertung gelten. |
||||
queryCache.put(normalized,found.size()>5000?null:found); |
||||
} catch(SQLException ex) { queryCache.put(normalized,null); } |
||||
} |
||||
List<SRView> found=queryCache.get(normalized); |
||||
if(found==null) field.problem="SQL konnte nicht sicher ausgewertet werden."; |
||||
else if(found.isEmpty()) field.problem="SQL liefert derzeit keine vorhandenen Sichten."; |
||||
else field.views.addAll(found); |
||||
} |
||||
for(Map.Entry<Integer,List<SRField>> entry:data.fields.entrySet()) |
||||
data.maskStatus.put(entry.getKey(),srStatus(entry.getValue(),data.rights)); |
||||
return data; |
||||
} |
||||
private static String srInlineStatus(SRData data,int mask,String typ,int subjectId) { |
||||
if(mask<=0 || subjectId<0) return ""; |
||||
String state=data==null?"unknown":data.maskStatus.get(mask); |
||||
if(state==null) state="unknown"; |
||||
String status="ok".equals(state)?"✓":"missing".equals(state)?"✗":"?"; |
||||
String title="ok".equals(state)?"Sichtrechte ausreichend":"missing".equals(state)?"Sichtenrechte fehlen":"Sichtrechte nicht vollständig prüfbar"; |
||||
String color="ok".equals(state)?"#188342":"missing".equals(state)?"#ba2222":"#aa7200"; |
||||
String link="masken_sichtrechte_edit.jsp?typ="+typ+"&id="+subjectId+"&maskeninfo_id="+mask; |
||||
return "<span title=\""+srHtml(title)+"\" style=\"margin-left:5px;white-space:nowrap;color:"+color+";font-size:12px;\"><b>"+status+"</b>" + |
||||
("ok".equals(state)?"":" "+srHtml(title))+"</span>"+ |
||||
" <a href=\""+link+"\" title=\"Sichtrechte prüfen/anpassen (gespeicherte Maske)\" style=\"margin-left:5px;display:inline-block;vertical-align:middle\">"+ |
||||
"<img src=\"../../images/pencil.svg\" alt=\"Sichtrechte anpassen\" style=\"width:15px;height:15px;border:0\"></a>"; |
||||
} |
||||
/* Direktes JDBC-Schreiben muss Schreibrechte eigenständig prüfen: |
||||
check_authentication.inc allein gewährt auch lesenden DBForms-Berechtigten Zugang. */ |
||||
private static boolean srMayWrite(Connection con,int actingUser,String formName) throws SQLException { |
||||
if (actingUser<=0) return false; |
||||
try(PreparedStatement ps=con.prepareStatement("select administration from userinfo where tid=?")) { |
||||
ps.setInt(1,actingUser);try(ResultSet rs=ps.executeQuery()) { |
||||
if(rs.next() && rs.getInt(1)==1) return true; |
||||
} |
||||
} |
||||
// Die SuperX-Administratoren-Gruppe besitzt generell Schreibrechte für DBForms. |
||||
try(PreparedStatement ps=con.prepareStatement("select 1 from user_group_bez ug join groupinfo g on g.tid=ug.groupinfo_id where ug.userinfo_id=? and trim(g.name)='Administratoren'")) { |
||||
ps.setInt(1,actingUser);try(ResultSet rs=ps.executeQuery()) { if(rs.next()) return true; } |
||||
} |
||||
// Delegierte Bearbeiter nur für die dem konkreten DBForm zugeordneten Admin-Sachgebiete. |
||||
String query="select 1 from db_forms df join sachgeb_dbform_bez b on b.dbform_id=df.tid " + |
||||
"join sachgebiete sg on sg.tid=b.sachgebiete_id " + |
||||
"where trim(df.name)=? and (trim(sg.name) like 'Administration%' or trim(sg.name) like 'Bearbeitung%') " + |
||||
"and (exists(select 1 from user_sachgeb_bez us where us.sachgebiete_id=sg.tid and us.userinfo_id=?) " + |
||||
"or exists(select 1 from user_group_bez ug join group_sachgeb_bez gs on gs.groupinfo_id=ug.groupinfo_id where ug.userinfo_id=? and gs.sachgebiete_id=sg.tid))"; |
||||
try(PreparedStatement ps=con.prepareStatement(query)) { |
||||
ps.setString(1,formName);ps.setInt(2,actingUser);ps.setInt(3,actingUser); |
||||
try(ResultSet rs=ps.executeQuery()) { return rs.next(); } |
||||
} |
||||
} |
||||
private static String srMaskName(Connection con,int mask) throws SQLException { |
||||
try(PreparedStatement ps=con.prepareStatement("select name from maskeninfo where tid=?")) { |
||||
ps.setInt(1,mask);try(ResultSet rs=ps.executeQuery()) { return rs.next()?srTrim(rs.getString(1)):""; } |
||||
} |
||||
} |
||||
private static boolean srRightExists(Connection con,String table,String idColumn,String keyColumn,int id,String value) throws SQLException { |
||||
try(PreparedStatement ps=con.prepareStatement("select 1 from "+table+" where "+idColumn+"=? and "+keyColumn+"=?")) { |
||||
ps.setInt(1,id);ps.setString(2,value); |
||||
try(ResultSet rs=ps.executeQuery()) { return rs.next(); } |
||||
} |
||||
} |
||||
private static int srSave(Connection con,String typ,int id,Set<String> newArts,Set<String> newViews) throws SQLException { |
||||
String suffix="group".equals(typ)?"group":"user"; |
||||
String idCol="group".equals(typ)?"groupinfo_id":"userinfo_id"; |
||||
boolean oldAutoCommit=con.getAutoCommit(); int added=0; |
||||
try { |
||||
con.setAutoCommit(false); |
||||
for(String art:newArts) { |
||||
String table=suffix+"_sichtarten"; |
||||
if(!srRightExists(con,table,idCol,"sichtart",id,art)) { |
||||
try(PreparedStatement ps=con.prepareStatement("insert into "+table+" ("+idCol+",sichtart) values (?,?)")) { |
||||
ps.setInt(1,id);ps.setString(2,art);added+=ps.executeUpdate(); |
||||
} |
||||
} |
||||
} |
||||
for(String view:newViews) { |
||||
String table=suffix+"_sichten"; |
||||
if(!srRightExists(con,table,idCol,"sicht_name_intern",id,view)) { |
||||
try(PreparedStatement ps=con.prepareStatement("insert into "+table+" ("+idCol+",sicht_name_intern) values (?,?)")) { |
||||
ps.setInt(1,id);ps.setString(2,view);added+=ps.executeUpdate(); |
||||
} |
||||
} |
||||
} |
||||
con.commit(); |
||||
return added; |
||||
} catch(SQLException e) { con.rollback(); throw e; } |
||||
finally { con.setAutoCommit(oldAutoCommit); } |
||||
} |
||||
%> |
||||
Loading…
Reference in new issue