import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.databind.JsonNode;
import edu.internet2.middleware.grouper.GrouperSession;
import edu.internet2.middleware.grouper.Member;
import edu.internet2.middleware.grouper.MemberFinder;
import edu.internet2.middleware.grouper.SubjectFinder;
import edu.internet2.middleware.grouper.app.loader.GrouperLoaderConfig;
import edu.internet2.middleware.grouper.app.loader.OtherJobScript;
import edu.internet2.middleware.grouper.misc.GrouperStartup;
import edu.internet2.middleware.grouper.util.GrouperHttpClient;
import edu.internet2.middleware.grouper.util.GrouperHttpMethod;
import edu.internet2.middleware.grouper.util.GrouperUtil;
import edu.internet2.middleware.grouperClient.jdbc.GcDbAccess;
import edu.internet2.middleware.grouperClient.util.GrouperClientUtils;
import edu.internet2.middleware.subject.Subject;
//public class Test79splunkSpLogins {
public static Set<Long> memberIdIndexInGroup(String groupName, Collection<Long> memberIdIndexesCollection) {
int theBatchSize = 1000;
List<Long> memberIdIndexesList = new ArrayList<>(GrouperUtil.nonNull(memberIdIndexesCollection));
int theNumberOfBatches = GrouperUtil.batchNumberOfBatches(memberIdIndexesList, theBatchSize, false);
Set<Long> resultMemberIdIndexes = new HashSet<>();
// go through in batches
for (int i=0;i<theNumberOfBatches;i++) {
List<Long> memberIdIndexesBatch = GrouperUtil.batchList(memberIdIndexesList, theBatchSize, i);
List<Long> memberIdIndexesExist = new GcDbAccess().connectionName("awsProdReadonly").sql(
"select gm.id_index from grouper_memberships_lw_v gmlv, grouper_members gm where " +
" gm.id = gmlv.member_id and gmlv.list_name = 'members' " +
" and gmlv.group_name = ? and gm.id_index in (" +
GrouperClientUtils.appendQuestions(memberIdIndexesBatch.size()) + ")").
addBindVar(groupName).addBindVars(memberIdIndexesBatch).selectList(Long.class);
resultMemberIdIndexes.addAll(memberIdIndexesExist);
}
return resultMemberIdIndexes;
}
// public static void main(String[] args) {
GrouperStartup.startup();
GrouperSession grouperSession = GrouperSession.startRootSession();
Map<String, Object> debugMap = new LinkedHashMap<>();
String splunkBaseUrl = GrouperUtil.stripLastSlashIfExists(GrouperLoaderConfig.retrieveConfig().propertyValueStringRequired("splunk.url"));
String splunkBearerToken = GrouperLoaderConfig.retrieveConfig().propertyValueStringRequired("splunk.bearerTokenSecret");
long startedMillis = System.currentTimeMillis();
// https://sls.isc.upenn.edu:8089/servicesNS/-/upenn_isc_attribution/search/jobs/export?search=search%20%60sso_authentications%60&exec_mode=oneshot&output_mode=json
GrouperHttpClient grouperHttpClient = new GrouperHttpClient().
assignGrouperHttpMethod(GrouperHttpMethod.get).
addHeader("Content-Type", "application/json").
addHeader("Accept", "application/json").
addHeader("Authorization", "Bearer " + splunkBearerToken).
assignUrl(splunkBaseUrl + "/servicesNS/-/upenn_isc_attribution/search/jobs/export").
addUrlParameter("search", "search `sso_authentications`").
addUrlParameter("output_mode", "json").
addUrlParameter("exec_mode", "oneshot").
executeRequest();
int responseCode = grouperHttpClient.getResponseCode();
String body = grouperHttpClient.getResponseBody();
if (responseCode != 200) {
debugMap.put("splunkResponseCode", responseCode);
throw new RuntimeException(responseCode + "," + body);
}
debugMap.put("splunkQueryTookMs", System.currentTimeMillis() - startedMillis);
// {"preview":false,"offset":37239,"result":{"_time":"2024-01-25 11:08:31.132 EST","user":"aaravrr","dest_app":"courses.upenn.edu/sam_rMReby8Vl3M1BiyVWMAT"}}
// {"preview":false,"offset":37252,"result":{"_time":"2024-01-25 11:08:28.546 EST","user":"baronche","dest_app":"https://cluster-prod.apps.upenn.edu/shibboleth"}}
String[] bodyLines = new String[0];
if (!StringUtils.isBlank(body)) {
bodyLines = GrouperUtil.splitTrim(body, "\n");
}
int linesTotal = bodyLines.length;
debugMap.put("linesTotal", linesTotal);
int linesDontMatch = 0;
int invalidDates = 0;
int invalidUsers = 0;
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("America/New_York"));
Set<String> pennkeys = new HashSet<String>();
Set<String> pennids = new HashSet<String>();
Set<String> entityIds = new HashSet<String>();
Timestamp minTimestamp = null;
List<Object[]> timeUserSps = new ArrayList<Object[]>();
Timestamp maxTimestampAlreadyLogged = new GcDbAccess().sql("select max(login_timestamp) from sso_prod_logs_person_sp").select(Timestamp.class);
if (maxTimestampAlreadyLogged == null) {
maxTimestampAlreadyLogged = new Timestamp(0L);
}
int recordsAlreadyLogged = 0;
Map<String, Set<String>> entityIdToPennkeyOrPennid = new HashMap<>();
for (String bodyLine : bodyLines) {
if (StringUtils.isBlank(bodyLine)) {
continue;
}
JsonNode mainNode = GrouperUtil.jsonJacksonNode(bodyLine);
JsonNode resultNode = mainNode == null ? null : GrouperUtil.jsonJacksonGetNode(mainNode, "result");
String time = resultNode == null ? null : GrouperUtil.jsonJacksonGetString(resultNode, "_time");
String user = resultNode == null ? null : GrouperUtil.jsonJacksonGetString(resultNode, "user");
String dest_app = resultNode == null ? null : GrouperUtil.jsonJacksonGetString(resultNode, "dest_app");
String timeSuffix1 = " EST";
String timeSuffix2 = " EDT";
if (StringUtils.isBlank(time) || StringUtils.isBlank(user) || StringUtils.isBlank(dest_app) || (!time.endsWith(timeSuffix1) && !time.endsWith(timeSuffix2))) {
if (linesDontMatch < 10) {
debugMap.put("linesDontMatchExample_" + linesDontMatch, bodyLine);
}
linesDontMatch++;
continue;
}
time = time.substring(0, time.length() - timeSuffix1.length());
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
df.setTimeZone(cal.getTimeZone());
Timestamp timestamp = null;
try {
Date date = null;
date = df.parse(time);
timestamp = new Timestamp(date.getTime());
} catch (Exception e) {
if (invalidDates < 10) {
debugMap.put("invalidDatesExample_" + invalidDates, time);
}
invalidDates++;
}
if (timestamp.before(maxTimestampAlreadyLogged)) {
recordsAlreadyLogged++;
continue;
}
if (!user.matches("[a-z0-9]{2,8}")) {
if (invalidUsers < 10) {
debugMap.put("invalidUser_" + invalidUsers, user);
}
if (OtherJobScript.retrieveFromThreadLocal() == null) {
System.out.println("Invalid user: " + user);
}
invalidUsers++;
}
if (user.matches("[0-9]{2,8}")) {
pennids.add(user);
} else {
pennkeys.add(user);
}
entityIds.add(dest_app);
if (minTimestamp == null || timestamp.before(minTimestamp)) {
minTimestamp = timestamp;
}
timeUserSps.add(GrouperUtil.toArrayObject(timestamp, user, dest_app));
Set<String> pennkeyOrPennids = entityIdToPennkeyOrPennid.get(dest_app);
if (pennkeyOrPennids == null) {
pennkeyOrPennids = new HashSet<>();
entityIdToPennkeyOrPennid.put(dest_app, pennkeyOrPennids);
}
pennkeyOrPennids.add(user);
}
if (OtherJobScript.retrieveFromThreadLocal() != null) {
OtherJobScript.retrieveFromThreadLocal().getOtherJobInput().getHib3GrouperLoaderLog().setTotalCount(bodyLines.length);
}
debugMap.put("pennkeysCount", pennkeys.size());
debugMap.put("pennidsCount", pennids.size());
debugMap.put("recordsAlreadyLogged", recordsAlreadyLogged);
debugMap.put("linesDontMatch", linesDontMatch);
debugMap.put("invalidUsers", invalidUsers);
debugMap.put("matchedLinesTotal", timeUserSps.size());
Map<String, Long> loginIdToIdIndex = new HashMap<String, Long>();
Map<String, String> loginIdToPennid = new HashMap<String, String>();
// retrieve / create users
List<String> pennkeyList = new ArrayList<String>(pennkeys);
int batchSize = 1000;
int numberOfBatches = GrouperUtil.batchNumberOfBatches(pennkeyList.size(), batchSize, false);
int membersCreated = 0;
int subjectsNotFound = 0;
for (int i=0;i<numberOfBatches;i++) {
List<String> batchPennkeys = GrouperUtil.batchList(pennkeyList, batchSize, i);
Set<String> batchPennkeysSet = new HashSet<String>(batchPennkeys);
String sql = """
select gm.subject_identifier0 AS pennkey, id_index, subject_id from grouper_members gm
""" + " where subject_identifier0 in (" + GrouperClientUtils.appendQuestions(batchPennkeys.size()) + ")";
GcDbAccess gcDbAccess = new GcDbAccess().sql(sql).connectionName("awsProdReadonly");
for (String pennkey : batchPennkeys) {
gcDbAccess.addBindVar(pennkey);
}
List<Object[]> subjectIdentifierIdIndexes = gcDbAccess.selectList(Object[].class);
for (Object[] subjectIdentifierIdIndex : subjectIdentifierIdIndexes) {
String subjectIdentifier = (String)subjectIdentifierIdIndex[0];
Long idIndex = GrouperUtil.longValue(subjectIdentifierIdIndex[1]);
batchPennkeysSet.remove(subjectIdentifier);
loginIdToIdIndex.put(subjectIdentifier, idIndex);
String pennId = (String)subjectIdentifierIdIndex[2];
loginIdToPennid.put(subjectIdentifier, pennId);
}
for (String pennkey : batchPennkeysSet) {
Subject subject = SubjectFinder.findByIdentifierAndSource(pennkey, "pennperson", false);
if (subject == null) {
if (subjectsNotFound < 10) {
debugMap.put("subjectNotFound_" + subjectsNotFound, pennkey);
}
subjectsNotFound++;
continue;
}
loginIdToPennid.put(pennkey, subject.getId());
Member member = MemberFinder.findBySubject(grouperSession, subject, true);
if (OtherJobScript.retrieveFromThreadLocal() != null) {
OtherJobScript.retrieveFromThreadLocal().getOtherJobInput().getHib3GrouperLoaderLog().addInsertCount(1);
}
loginIdToIdIndex.put(pennkey, member.getIdIndex());
if (membersCreated < 10) {
debugMap.put("memberCreated_" + membersCreated, pennkey);
}
membersCreated++;
}
}
List<String> pennidList = new ArrayList<String>(pennids);
numberOfBatches = GrouperUtil.batchNumberOfBatches(pennidList.size(), batchSize, false);
int membersPennIdCreated = 0;
int subjectsPennIdNotFound = 0;
for (int i=0;i<numberOfBatches;i++) {
List<String> batchPennids = GrouperUtil.batchList(pennidList, batchSize, i);
Set<String> batchPennidsSet = new HashSet<String>(batchPennids);
String sql = """
select gm.subject_id AS subject_id, id_index from grouper_members gm
""" + " where subject_id in (" + GrouperClientUtils.appendQuestions(batchPennids.size()) + ")";
GcDbAccess gcDbAccess = new GcDbAccess().sql(sql).connectionName("awsProdReadonly");
for (String pennid : batchPennids) {
gcDbAccess.addBindVar(pennid);
loginIdToPennid.put(pennid, pennid);
}
List<Object[]> subjectIdIdIndexes = gcDbAccess.selectList(Object[].class);
for (Object[] subjectIdIdIndex : subjectIdIdIndexes) {
String subjectId = (String)subjectIdIdIndex[0];
Long idIndex = GrouperUtil.longValue(subjectIdIdIndex[1]);
batchPennidsSet.remove(subjectId);
loginIdToIdIndex.put(subjectId, idIndex);
}
for (String pennid : batchPennidsSet) {
Subject subject = SubjectFinder.findByIdAndSource(pennid, "pennperson", false);
if (subject == null) {
if (subjectsPennIdNotFound < 10) {
debugMap.put("subjectNotFound_" + subjectsPennIdNotFound, pennid);
}
subjectsPennIdNotFound++;
continue;
}
Member member = MemberFinder.findBySubject(grouperSession, subject, true);
if (OtherJobScript.retrieveFromThreadLocal() != null) {
OtherJobScript.retrieveFromThreadLocal().getOtherJobInput().getHib3GrouperLoaderLog().addInsertCount(1);
}
loginIdToIdIndex.put(pennid, member.getIdIndex());
if (membersPennIdCreated < 10) {
debugMap.put("memberCreated_" + membersPennIdCreated, pennid);
}
membersPennIdCreated++;
}
}
debugMap.put("membersPennIdCreated", membersPennIdCreated);
debugMap.put("subjectsPennIdNotFound", subjectsPennIdNotFound);
// retrieve / create sps
Map<String, Long> entityIdToIdIndex = new HashMap<String, Long>();
List<String> entityIdList = new ArrayList<String>(entityIds);
numberOfBatches = GrouperUtil.batchNumberOfBatches(entityIdList.size(), batchSize, false);
int entityIdsCreated = 0;
Long maxEntityIdIndex = new GcDbAccess().sql("select max(id_index) from sso_prod_entity_id").select(long.class);
if (maxEntityIdIndex == null) {
maxEntityIdIndex = 0L;
} else {
maxEntityIdIndex++;
}
List<List<Object>> entityIdIdIndexToCreate = new ArrayList<>();
Map<String, String> entityIdToFrontDoorGroupName = new HashMap<>();
for (int i=0;i<numberOfBatches;i++) {
List<String> batchEntityIds = GrouperUtil.batchList(entityIdList, batchSize, i);
Set<String> batchEntityIdsSet = new HashSet<String>(batchEntityIds);
String sql = "select spei.entity_id, spei.id_index, spefd.front_door_policy_group from sso_prod_entity_id spei " +
" left join sso_prod_entity_front_door spefd on spei.entity_id = spefd.entity_id " +
" where spei.entity_id in (" + GrouperClientUtils.appendQuestions(batchEntityIds.size()) + ")";
GcDbAccess gcDbAccess = new GcDbAccess().connectionName("awsProdReadonly").sql(sql);
for (String sp : batchEntityIds) {
gcDbAccess.addBindVar(sp);
}
List<Object[]> entityIdIdIndexGroupNames = gcDbAccess.selectList(Object[].class);
for (Object[] entityIdIdIndexGroupName : entityIdIdIndexGroupNames) {
String entityId = (String)entityIdIdIndexGroupName[0];
Long idIndex = GrouperUtil.longValue(entityIdIdIndexGroupName[1]);
String frontDoorGroupName = (String)entityIdIdIndexGroupName[2];
batchEntityIdsSet.remove(entityId);
entityIdToIdIndex.put(entityId, idIndex);
if (!StringUtils.isBlank(frontDoorGroupName)) {
entityIdToFrontDoorGroupName.put(entityId, frontDoorGroupName);
}
}
for (String entityId : batchEntityIdsSet) {
entityIdToIdIndex.put(entityId, maxEntityIdIndex);
entityIdIdIndexToCreate.add(GrouperUtil.toListObject(entityId, maxEntityIdIndex));
if (OtherJobScript.retrieveFromThreadLocal() != null) {
OtherJobScript.retrieveFromThreadLocal().getOtherJobInput().getHib3GrouperLoaderLog().addInsertCount(1);
}
if (entityIdsCreated < 10) {
debugMap.put("entityIdCreated_" + entityIdsCreated, entityId);
}
entityIdsCreated++;
maxEntityIdIndex++;
}
}
new GcDbAccess().sql("insert into sso_prod_entity_id (entity_id, id_index) values (?, ?)").batchBindVars(entityIdIdIndexToCreate).executeBatchSql();
// insert data
int logRecordsCreated = 0;
int subjectsIdIndexNotFound = 0;
Long maxLogRecordIdIndex = new GcDbAccess().sql("select max(id_index) from sso_prod_logs_person_sp").select(long.class);
if (maxLogRecordIdIndex == null) {
maxLogRecordIdIndex = 0L;
} else {
maxLogRecordIdIndex++;
}
Map<String, Set<Long>> entityIdHasFrontDoorGroupHasMemberIdIndexes = new HashMap();
for (String entityId : entityIdToFrontDoorGroupName.keySet()) {
String frontDoorGroupName = entityIdToFrontDoorGroupName.get(entityId);
Set<String> pennkeyOrPennids = entityIdToPennkeyOrPennid.get(entityId);
List<Long> memberIdIndexes = new ArrayList<>();
for (String pennkeyOrPennid : pennkeyOrPennids) {
Long memberIdIndex = loginIdToIdIndex.get(pennkeyOrPennid);
if (memberIdIndex != null) {
memberIdIndexes.add(memberIdIndex);
}
}
Set<Long> memberIdsInGroup = memberIdIndexInGroup(frontDoorGroupName, memberIdIndexes);
entityIdHasFrontDoorGroupHasMemberIdIndexes.put(entityId, memberIdsInGroup);
}
Set<Long> memberIdIndexes = new HashSet<>(loginIdToIdIndex.values());
Set<Long> twoStepMemberIdIndexes = memberIdIndexInGroup("penn:isc:ts:iam:weblogin:service:policy:idpCoarseGrained:enrolledInTwoStep", memberIdIndexes);
Set<Long> workforceMemberIdIndexes = memberIdIndexInGroup("penn:isc:ts:iam:weblogin:service:policy:idpCoarseGrained:idpWorkforce", memberIdIndexes);
Set<Long> memberMemberIdIndexes = memberIdIndexInGroup("penn:isc:ts:iam:weblogin:service:policy:idpCoarseGrained:member", memberIdIndexes);
Set<Long> affiliateMemberIdIndexes = memberIdIndexInGroup("penn:isc:ts:iam:weblogin:service:policy:idpCoarseGrained:idpAffiliate", memberIdIndexes);
Set<Long> recentAffiliateMemberIdIndexes = memberIdIndexInGroup("penn:isc:ts:iam:weblogin:service:policy:idpCoarseGrained:idpRecentAffiliate", memberIdIndexes);
Set<Long> alumOrAffiliateMemberIdIndexes = memberIdIndexInGroup("penn:isc:ts:iam:weblogin:service:policy:idpCoarseGrained:idpAffiliateOrAlum", memberIdIndexes);
Set<Long> uphsOnlyMemberIdIndexes = memberIdIndexInGroup("penn:community:uphsOnly", memberIdIndexes);
Set<Long> alumOnlyMemberIdIndexes = memberIdIndexInGroup("penn:community:alumni:alumniOnly", memberIdIndexes);
Set<Long> lockedOutMemberIdIndexes = memberIdIndexInGroup("penn:etc:deprovisioning:usersWhoHaveBeenDeprovisioned_employee", memberIdIndexes);
Set<Long> uphsNotPennpayNotStudentMemberIdIndexes = memberIdIndexInGroup("penn:community:uphsNotPennpayNotStudent", memberIdIndexes);
List<List<Object>> logRecordIdIndexUserIndexSpIndexTimestamp = new ArrayList<>();
for (Object[] timeUserSp : timeUserSps) {
Timestamp timestamp = (Timestamp)timeUserSp[0];
String pennkey = (String)timeUserSp[1];
Long userIdIndex = loginIdToIdIndex.get(pennkey);
String pennid = loginIdToPennid.get(pennkey);
String entityId = (String)timeUserSp[2];
Long entityIdIdIndex = entityIdToIdIndex.get(entityId);
if (userIdIndex == null) {
if (subjectsIdIndexNotFound < 10) {
debugMap.put("subjectsIdIndexNotFound_" + subjectsIdIndexNotFound, entityId);
}
subjectsIdIndexNotFound++;
continue;
}
Integer inFrontDoor = null;
String frontDoorGroupName = entityIdToFrontDoorGroupName.get(entityId);
if (!StringUtils.isBlank(frontDoorGroupName)) {
Set<Long> memberIdsInFrontDoorGroup = entityIdHasFrontDoorGroupHasMemberIdIndexes.get(entityId);
inFrontDoor = memberIdsInFrontDoorGroup != null && memberIdsInFrontDoorGroup.contains(userIdIndex) ? 1 : 0;
}
Integer nonPersistent = null;
if (!StringUtils.isBlank(pennid)) {
nonPersistent = pennid.startsWith("9") ? 1 : 0;
}
logRecordIdIndexUserIndexSpIndexTimestamp.add(GrouperUtil.toListObject(maxLogRecordIdIndex, userIdIndex, entityIdIdIndex, timestamp,
twoStepMemberIdIndexes.contains(userIdIndex) ? 1 : 0, workforceMemberIdIndexes.contains(userIdIndex) ? 1 : 0,
memberMemberIdIndexes.contains(userIdIndex) ? 1 : 0, affiliateMemberIdIndexes.contains(userIdIndex) ? 1 : 0,
recentAffiliateMemberIdIndexes.contains(userIdIndex) ? 1 : 0, alumOrAffiliateMemberIdIndexes.contains(userIdIndex) ? 1 : 0,
uphsOnlyMemberIdIndexes.contains(userIdIndex) ? 1 : 0, alumOnlyMemberIdIndexes.contains(userIdIndex) ? 1 : 0,
inFrontDoor, lockedOutMemberIdIndexes.contains(userIdIndex) ? 1 : 0, nonPersistent, uphsNotPennpayNotStudentMemberIdIndexes.contains(userIdIndex) ? 1 : 0));
if (OtherJobScript.retrieveFromThreadLocal() != null) {
OtherJobScript.retrieveFromThreadLocal().getOtherJobInput().getHib3GrouperLoaderLog().addInsertCount(1);
}
if (logRecordsCreated < 10) {
debugMap.put("logRecordsCreated_" + logRecordsCreated, timestamp.toString() + ", " + pennkey + ", " + entityId);
}
logRecordsCreated++;
maxLogRecordIdIndex++;
}
new GcDbAccess().sql("insert into sso_prod_logs_person_sp (id_index, user_member_id_index, sp_entity_id_index, login_timestamp, two_step_enrolled, workforce, member, "
+ " affiliate, recent_affiliate, alum_or_affiliate, uphs_only, alum_only, in_front_door, locked_out, "
+ "non_persistent, uphs_not_pennpay_not_student) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").
batchBindVars(logRecordIdIndexUserIndexSpIndexTimestamp).executeBatchSql();
debugMap.put("subjectsIdIndexNotFound", subjectsIdIndexNotFound);
debugMap.put("logRecordsCreated", logRecordsCreated);
if (OtherJobScript.retrieveFromThreadLocal() != null) {
OtherJobScript.retrieveFromThreadLocal().getOtherJobInput().getHib3GrouperLoaderLog().appendJobMessage(GrouperUtil.mapToString(debugMap));
} else {
System.exit(0);
}
// }
//}