diff --git a/bindings/java/jni/auto_db_java.cpp b/bindings/java/jni/auto_db_java.cpp
index 7cc3888cf390a52966d1f52d3cfa0e432844ae71..20e915d81c21412a12f83536ad618421b4cfcc1a 100644
--- a/bindings/java/jni/auto_db_java.cpp
+++ b/bindings/java/jni/auto_db_java.cpp
@@ -110,7 +110,7 @@ TskAutoDbJava::initializeJni(JNIEnv * jniEnv, jobject jobj) {
         return TSK_ERR;
     }
 
-    m_addFileMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addFile", "(JJJIIILjava/lang/String;JJIIIIJJJJJIIILjava/lang/String;Ljava/lang/String;JJJ)J");
+    m_addFileMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addFile", "(JJJIIILjava/lang/String;JJIIIIJJJJJIIILjava/lang/String;Ljava/lang/String;JJJLjava/lang/String;)J");
     if (m_addFileMethodID == NULL) {
         return TSK_ERR;
     }
@@ -631,6 +631,17 @@ TskAutoDbJava::addFile(TSK_FS_FILE* fs_file,
     }
     TSK_INUM_T par_meta_addr = fs_file->name->par_addr;
  
+	char *sid_str = NULL;
+	jstring sidj = NULL;	// return null across JNI if sid is not available
+	
+	if (tsk_fs_file_get_owner_sid(fs_file, &sid_str) == 0) {
+		if (createJString(sid_str, sidj) != TSK_OK) {
+			free(sid_str);
+			return TSK_ERR;
+		}
+		free(sid_str);	
+	}
+		
     // Add the file to the database
     jlong ret_val = m_jniEnv->CallLongMethod(m_javaDbObj, m_addFileMethodID,
         parObjId, fsObjId,
@@ -643,7 +654,7 @@ TskAutoDbJava::addFile(TSK_FS_FILE* fs_file,
         (unsigned long long)crtime, (unsigned long long)ctime, (unsigned long long) atime, (unsigned long long) mtime,
         meta_mode, gid, uid, 
         pathj, extj, 
-        (uint64_t)meta_seq, par_meta_addr, par_seqj);
+        (uint64_t)meta_seq, par_meta_addr, par_seqj, sidj);
 
     if (ret_val < 0) {
         free(name);
@@ -690,7 +701,7 @@ TskAutoDbJava::addFile(TSK_FS_FILE* fs_file,
             (unsigned long long)crtime, (unsigned long long)ctime, (unsigned long long) atime, (unsigned long long) mtime,
             meta_mode, gid, uid, // md5TextPtr, known,
             pathj, slackExtj, 
-            (uint64_t)meta_seq, par_meta_addr, par_seqj);
+            (uint64_t)meta_seq, par_meta_addr, par_seqj, sidj);
 
         if (ret_val < 0) {
             free(name);
diff --git a/bindings/java/src/org/sleuthkit/datamodel/AbstractFile.java b/bindings/java/src/org/sleuthkit/datamodel/AbstractFile.java
index 512ebef21e67fb94ca685b34c2ba57912a60a3cd..a0bbe2c3763a1717871637de8cd58b18fb5697c3 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/AbstractFile.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/AbstractFile.java
@@ -21,11 +21,13 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.RandomAccessFile;
+import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.ResourceBundle;
 import java.util.Set;
 import java.util.SortedSet;
@@ -91,6 +93,8 @@ public abstract class AbstractFile extends AbstractContent {
 	private long dataSourceObjectId;
 	private final String extension;
 
+	private final String uidStr;	// SID/uid
+	private final long userRowId;
 	/**
 	 * Initializes common fields used by AbstactFile implementations (objects in
 	 * tsk_files table)
@@ -125,8 +129,11 @@ public abstract class AbstractFile extends AbstractContent {
 	 *                           unknown (default)
 	 * @param parentPath
 	 * @param mimeType           The MIME type of the file, can be null.
-	 * @param extension		        The extension part of the file name (not
+	 * @param extension          The extension part of the file name (not
 	 *                           including the '.'), can be null.
+	 * @param uidStr			 String uid/SID, can be null if not available.
+	 * @param userRowId	         The row id of user in tsk_os_accounts.
+	 * 
 	 */
 	AbstractFile(SleuthkitCase db,
 			long objId,
@@ -144,7 +151,9 @@ public abstract class AbstractFile extends AbstractContent {
 			String md5Hash, String sha256Hash, FileKnown knownState,
 			String parentPath,
 			String mimeType,
-			String extension) {
+			String extension,
+			String uidStr,
+			long userRowId) {
 		super(db, objId, name);
 		this.dataSourceObjectId = dataSourceObjectId;
 		this.attrType = attrType;
@@ -176,6 +185,8 @@ public abstract class AbstractFile extends AbstractContent {
 		this.mimeType = mimeType;
 		this.extension = extension == null ? "" : extension;
 		this.encodingType = TskData.EncodingType.NONE;
+		this.uidStr = uidStr;
+		this.userRowId = userRowId;
 	}
 
 	/**
@@ -1174,6 +1185,52 @@ public void save() throws TskCoreException {
 		}
 	}
 	
+	/**
+	 * Get the uidStr.
+	 * 
+	 * @return String uid. Returns an empty string if no uid is recorded.
+	 */
+	public String getUidStr() {
+		return Objects.nonNull(uidStr) ? uidStr
+				: "";
+	}
+		
+	/**
+	 * Get the row id of the owing user. 
+	 * 
+	 * @return rowId, or OsAccount.NO_USER
+	 */
+	public long getUserRowId() {
+		return userRowId;
+	}
+	
+	/**
+	 * Gets the user for the file.
+	 * 
+	 * @return OsAccount  
+	 * 
+	 * @throws TskCoreException  If there is an error getting the user
+	 */
+	public OsAccount getUser() throws TskCoreException {
+		
+		// run a query to get the user row id from the tsk_files
+		String queryStr = "SELECT os_account_row_id FROM tsk_files WHERE obj_id = " + this.getId();
+		try (SleuthkitCase.CaseDbConnection connection = getSleuthkitCase().getConnection();
+				Statement statement = connection.createStatement();
+				ResultSet resultSet = connection.executeQuery(statement, queryStr);) {
+
+			if (resultSet.next()) {
+					long userRowId = resultSet.getLong("os_account_row_id");
+					return getSleuthkitCase().getOsAccountManager().getOsAccount(userRowId);
+			} else {
+				throw new TskCoreException(String.format("Error getting user account id for file (obj_id = %d)", this.getId()));
+			}
+			
+		} catch (SQLException ex) {
+			throw new TskCoreException(String.format("Error getting user account id for file (obj_id = %d)", this.getId()), ex);
+		} 
+	}
+	
 	@Override
 	public BlackboardArtifact newArtifact(int artifactTypeID) throws TskCoreException {
 		// don't let them make more than 1 GEN_INFO
@@ -1221,7 +1278,7 @@ protected AbstractFile(SleuthkitCase db, long objId, TskData.TSK_FS_ATTR_TYPE_EN
 			TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType, TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags,
 			long size, long ctime, long crtime, long atime, long mtime, short modes, int uid, int gid, String md5Hash, FileKnown knownState,
 			String parentPath) {
-		this(db, objId, db.getDataSourceObjectId(objId), attrType, (int) attrId, name, fileType, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, null, null);
+		this(db, objId, db.getDataSourceObjectId(objId), attrType, (int) attrId, name, fileType, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, null, null, OsAccount.NULL_UID_STR, OsAccount.NO_USER);
 	}
 
 	/**
@@ -1266,7 +1323,7 @@ protected AbstractFile(SleuthkitCase db, long objId, TskData.TSK_FS_ATTR_TYPE_EN
 			String name, TskData.TSK_DB_FILES_TYPE_ENUM fileType, long metaAddr, int metaSeq, TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
 			TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, long size, long ctime, long crtime, long atime, long mtime, short modes,
 			int uid, int gid, String md5Hash, FileKnown knownState, String parentPath, String mimeType) {
-		this(db, objId, dataSourceObjectId, attrType, (int) attrId, name, fileType, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, null, null);
+		this(db, objId, dataSourceObjectId, attrType, (int) attrId, name, fileType, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, null, null, OsAccount.NULL_UID_STR, OsAccount.NO_USER);
 	}
 
 	/**
diff --git a/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java b/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java
index b6c262f9d57b0b1d532201108e8ef08dd20a109b..34bf523ad858a212e3f2e41bfb9ba1bfdfcb8407 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java
@@ -208,9 +208,12 @@ private void createFileTables(Statement stmt) throws SQLException {
 				+ "mtime " + dbQueryHelper.getBigIntType() + ", mode INTEGER, uid INTEGER, gid INTEGER, md5 TEXT, sha256 TEXT, "
 				+ "known INTEGER, "
 				+ "parent_path TEXT, mime_type TEXT, extension TEXT, "
+				+ "uid_str TEXT, "
+				+ "os_account_row_id " + dbQueryHelper.getBigIntType() + ", "
 				+ "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
 				+ "FOREIGN KEY(fs_obj_id) REFERENCES tsk_fs_info(obj_id) ON DELETE CASCADE, "
-				+ "FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id) ON DELETE CASCADE)");
+				+ "FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id) ON DELETE CASCADE, "
+				+ "FOREIGN KEY(os_account_row_id) REFERENCES tsk_os_accounts(id)) " );
 
 		stmt.execute("CREATE TABLE file_encoding_types (encoding_type INTEGER PRIMARY KEY, name TEXT NOT NULL)");
 
@@ -270,7 +273,7 @@ private void createArtifactTables(Statement stmt) throws SQLException {
 				+ "value_text TEXT, value_int32 INTEGER, value_int64 " + dbQueryHelper.getBigIntType() + ", value_double NUMERIC(20, 10), "
 				+ "FOREIGN KEY(artifact_id) REFERENCES blackboard_artifacts(artifact_id) ON DELETE CASCADE, "
 				+ "FOREIGN KEY(artifact_type_id) REFERENCES blackboard_artifact_types(artifact_type_id), "
-				+ "FOREIGN KEY(attribute_type_id) REFERENCES blackboard_attribute_types(attribute_type_id))");		
+				+ "FOREIGN KEY(attribute_type_id) REFERENCES blackboard_attribute_types(attribute_type_id))");	
 	}
 	
 	private void createAnalysisResultsTables(Statement stmt) throws SQLException  {
@@ -408,6 +411,23 @@ private void createAccountTables(Statement stmt) throws SQLException {
 				+ "FOREIGN KEY(account2_id) REFERENCES accounts(account_id), "
 				+ "FOREIGN KEY(relationship_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
 				+ "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
+		
+		stmt.execute("CREATE TABLE tsk_os_accounts (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+				+ "data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+				+ "user_name TEXT, "	// username if available
+				+ "realm TEXT, "		// domain or host
+				+ "unique_id TEXT, "	// SID/UID, if available
+				+ "signature TEXT NOT NULL, "	// realm/username or sid 
+				+ "artifact_obj_id " + dbQueryHelper.getBigIntType() + ","
+				+ "UNIQUE(data_source_obj_id, signature), "
+				+ "FOREIGN KEY(artifact_obj_id) REFERENCES blackboard_artifacts(artifact_obj_id) ON DELETE CASCADE, "
+				+ "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id))");
+		
+		stmt.execute("CREATE TABLE tsk_data_artifact_data (id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+				+ "artifact_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+				+ "os_account_row_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+				+ "FOREIGN KEY(artifact_obj_id) REFERENCES blackboard_artifacts(artifact_obj_id) ON DELETE CASCADE, "
+				+ "FOREIGN KEY(os_account_row_id) REFERENCES tsk_os_accounts(id)) ");	
 	}
 	
 	private void createEventTables(Statement stmt) throws SQLException {
diff --git a/bindings/java/src/org/sleuthkit/datamodel/DataArtifact.java b/bindings/java/src/org/sleuthkit/datamodel/DataArtifact.java
new file mode 100644
index 0000000000000000000000000000000000000000..996535bb1c714e13cdd4d90fee8e1c802789a0f3
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/DataArtifact.java
@@ -0,0 +1,50 @@
+/*
+ * Sleuth Kit Data Model
+ *
+ * Copyright 2020 Basis Technology Corp.
+ * Contact: carrier <at> sleuthkit <dot> org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.datamodel;
+
+
+/**
+ * DataArtifact is a category of artifact types that are simply data directly
+ * extracted from a data source.
+ *
+ */
+public class DataArtifact extends BlackboardArtifact {
+	
+	private final OsAccount osAccount;
+	
+	
+	DataArtifact(SleuthkitCase sleuthkitCase, long artifactID, long sourceObjId, long artifactObjId, long dataSourceObjId, int artifactTypeID, String artifactTypeName, String displayName, ReviewStatus reviewStatus, OsAccount osAccount) {
+		super(sleuthkitCase, artifactID, sourceObjId, artifactObjId, dataSourceObjId, artifactTypeID, artifactTypeName, displayName, reviewStatus);
+		this.osAccount = osAccount;
+	}
+	
+	
+	/**
+	 * Gets the user for this artifact.
+	 *
+	 * @return OsAccount
+	 *
+	 * @throws TskCoreException If there is an error getting the user
+	 */
+	public OsAccount getOsAccount() throws TskCoreException {
+		return osAccount;
+	}
+	
+	
+}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/DerivedFile.java b/bindings/java/src/org/sleuthkit/datamodel/DerivedFile.java
index ef147fb10c980eb874594e42931342e0ba336dd2..0e07226d7499b3d37c2b15af3701e94d030c7bd0 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/DerivedFile.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/DerivedFile.java
@@ -82,6 +82,10 @@ public class DerivedFile extends AbstractFile {
 	 * @param encodingType		     The encoding type of the file.
 	 * @param extension          The extension part of the file name (not
 	 *                           including the '.'), can be null.
+	 * @param uidStr			 String UID of the user as found in in the file
+	 *                           system, can be null.
+	 * @param osAccountRowId	 Row id of the user in the tsk_os_accounts
+	 *                           table.
 	 */
 	DerivedFile(SleuthkitCase db,
 			long objId,
@@ -97,12 +101,14 @@ public class DerivedFile extends AbstractFile {
 			long parentId,
 			String mimeType,
 			TskData.EncodingType encodingType,
-			String extension) {
+			String extension, 
+			String uidStr,
+			long osAccountRowId) {
 		// TODO (AUT-1904): The parent id should be passed to AbstractContent 
 		// through the class hierarchy contructors.
 		super(db, objId, dataSourceObjectId, TskData.TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0,
 				name, TSK_DB_FILES_TYPE_ENUM.LOCAL, 0L, 0, dirType, metaType, dirFlag,
-				metaFlags, size, ctime, crtime, atime, mtime, (short) 0, 0, 0, md5Hash, sha256Hash, knownState, parentPath, mimeType, extension);
+				metaFlags, size, ctime, crtime, atime, mtime, (short) 0, 0, 0, md5Hash, sha256Hash, knownState, parentPath, mimeType, extension, uidStr, osAccountRowId);
 		setLocalFilePath(localPath);
 		setEncodingType(encodingType);
 	}
@@ -306,7 +312,7 @@ protected DerivedFile(SleuthkitCase db,
 		this(db, objId, db.getDataSourceObjectId(objId), name, dirType, metaType, dirFlag, metaFlags, size,
 				ctime, crtime, atime, mtime,
 				md5Hash, null, knownState,
-				parentPath, localPath, parentId, null, TskData.EncodingType.NONE, null);
+				parentPath, localPath, parentId, null, TskData.EncodingType.NONE, null, OsAccount.NULL_UID_STR, OsAccount.NO_USER);
 	}
 
 }
diff --git a/bindings/java/src/org/sleuthkit/datamodel/Directory.java b/bindings/java/src/org/sleuthkit/datamodel/Directory.java
index 39b0c62188ef261354eedeb1596ba6f6f2fd0e6f..3aec920c6226d804024f8c2b497912831a51208a 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/Directory.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/Directory.java
@@ -72,6 +72,10 @@ public class Directory extends FsContent {
 	 * @param knownState         The known state of the file from a hash
 	 *                           database lookup, null if not yet looked up.
 	 * @param parentPath         The path of the parent of the file.
+	 * @param uidStr			 String UID of the user as found in in the file
+	 *                           system, can be null.
+	 * @param osAccountRowId	 Row id of the user in the tsk_os_accounts
+	 *                           table.
 	 */
 	Directory(SleuthkitCase db,
 			long objId,
@@ -85,8 +89,9 @@ public class Directory extends FsContent {
 			long size,
 			long ctime, long crtime, long atime, long mtime,
 			short modes, int uid, int gid,
-			String md5Hash, String sha256Hash, FileKnown knownState, String parentPath) {
-		super(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TskData.TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, null, null);
+			String md5Hash, String sha256Hash, FileKnown knownState, String parentPath, 
+			String uidStr, long osAccountRowId ) {
+		super(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TskData.TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, null, null, uidStr, osAccountRowId);
 	}
 
 	/**
@@ -247,6 +252,6 @@ protected Directory(SleuthkitCase db,
 			long ctime, long crtime, long atime, long mtime,
 			short modes, int uid, int gid,
 			String md5Hash, FileKnown knownState, String parentPath) {
-		this(db, objId, dataSourceObjectId, fsObjId, attrType, (int) attrId, name, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath);
+		this(db, objId, dataSourceObjectId, fsObjId, attrType, (int) attrId, name, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, OsAccount.NULL_UID_STR, OsAccount.NO_USER);
 	}
 }
diff --git a/bindings/java/src/org/sleuthkit/datamodel/File.java b/bindings/java/src/org/sleuthkit/datamodel/File.java
index c6758c0ff88657acb513b55810acbc445b518df3..26315e1d07465f50f3e208393d58a49055c7cccc 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/File.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/File.java
@@ -76,6 +76,10 @@ public class File extends FsContent {
 	 *                           yet been determined.
 	 * @param extension	         The extension part of the file name (not
 	 *                           including the '.'), can be null.
+	 * @param uidStr			 String UID of the user as found in in the file
+	 *                           system, can be null.
+	 * @param osAccountRowId	 Row id of the user in the tsk_os_accounts
+	 *                           table.
 	 */
 	File(SleuthkitCase db,
 			long objId,
@@ -90,8 +94,10 @@ public class File extends FsContent {
 			long ctime, long crtime, long atime, long mtime,
 			short modes, int uid, int gid,
 			String md5Hash, String sha256Hash, FileKnown knownState, String parentPath, String mimeType,
-			String extension) {
-		super(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TskData.TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, mimeType, extension);
+			String extension,
+			String uidStr,
+			long osAccountRowId) {
+		super(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TskData.TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, mimeType, extension, uidStr, osAccountRowId);
 	}
 
 	/**
@@ -245,6 +251,6 @@ protected File(SleuthkitCase db,
 			String name, long metaAddr, int metaSeq, TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
 			TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, long size, long ctime, long crtime, long atime, long mtime,
 			short modes, int uid, int gid, String md5Hash, FileKnown knownState, String parentPath, String mimeType) {
-		this(db, objId, dataSourceObjectId, fsObjId, attrType, (int) attrId, name, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, mimeType, null);
+		this(db, objId, dataSourceObjectId, fsObjId, attrType, (int) attrId, name, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, mimeType, null, OsAccount.NULL_UID_STR, OsAccount.NO_USER);
 	}
 }
diff --git a/bindings/java/src/org/sleuthkit/datamodel/FsContent.java b/bindings/java/src/org/sleuthkit/datamodel/FsContent.java
index aa4bb739d06a0bad71b343ab9766c75f5f38511a..2f4afaefcc874af9d1118c65150e22379304ae5e 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/FsContent.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/FsContent.java
@@ -105,6 +105,10 @@ public abstract class FsContent extends AbstractFile {
 	 *                           yet been determined.
 	 * @param extension          The extension part of the file name (not
 	 *                           including the '.'), can be null.
+	 * @param uidStr			 String UID of the user as found in in the file
+	 *                           system, can be null.
+	 * @param osAccountRowId	 Row id of the user in the tsk_os_accounts
+	 *                           table.
 	 */
 	@SuppressWarnings("deprecation")
 	FsContent(SleuthkitCase db,
@@ -123,8 +127,10 @@ public abstract class FsContent extends AbstractFile {
 			String md5Hash, String sha256Hash, FileKnown knownState,
 			String parentPath,
 			String mimeType,
-			String extension) {
-		super(db, objId, dataSourceObjectId, attrType, attrId, name, fileType, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, mimeType, extension);
+			String extension,
+			String uidStr,
+			long osAccountRowId) {
+		super(db, objId, dataSourceObjectId, attrType, attrId, name, fileType, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, mimeType, extension, uidStr, osAccountRowId);
 		this.fsObjId = fsObjId;
 	}
 
@@ -383,7 +389,7 @@ public String toString(boolean preserveState) {
 			String name, long metaAddr, int metaSeq, TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
 			TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, long size, long ctime, long crtime, long atime, long mtime,
 			short modes, int uid, int gid, String md5Hash, FileKnown knownState, String parentPath) {
-		this(db, objId, db.getDataSourceObjectId(objId), fsObjId, attrType, (int) attrId, name, TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, null, null);
+		this(db, objId, db.getDataSourceObjectId(objId), fsObjId, attrType, (int) attrId, name, TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, null, null, OsAccount.NULL_UID_STR, OsAccount.NO_USER);
 	}
 
 	/**
@@ -442,6 +448,6 @@ public String toString(boolean preserveState) {
 			String name, long metaAddr, int metaSeq, TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
 			TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, long size, long ctime, long crtime, long atime, long mtime,
 			short modes, int uid, int gid, String md5Hash, FileKnown knownState, String parentPath, String mimeType) {
-		this(db, objId, dataSourceObjectId, fsObjId, attrType, (int) attrId, name, TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, mimeType, null);
+		this(db, objId, dataSourceObjectId, fsObjId, attrType, (int) attrId, name, TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, mimeType, null, OsAccount.NULL_UID_STR, OsAccount.NO_USER);
 	}
 }
diff --git a/bindings/java/src/org/sleuthkit/datamodel/LayoutFile.java b/bindings/java/src/org/sleuthkit/datamodel/LayoutFile.java
index ab98c64e75a8ffc50219df350fbe2c63b2a539f6..9a44ed4d0b2a73e2d83fcc828c100a137f491125 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/LayoutFile.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/LayoutFile.java
@@ -81,6 +81,10 @@ public class LayoutFile extends AbstractFile {
 	 * @param parentPath         The path of the parent of the file.
 	 * @param mimeType           The MIME type of the file, null if it has not
 	 *                           yet been determined.
+	 * @param uidStr			 String UID of the user as found in in the file
+	 *                           system, can be null.
+	 * @param osAccountRowId		 Row id of the user in the tsk_os_accounts
+	 *                           table.
 	 */
 	LayoutFile(SleuthkitCase db,
 			long objId,
@@ -92,8 +96,11 @@ public class LayoutFile extends AbstractFile {
 			long size,
 			long ctime, long crtime, long atime, long mtime,
 			String md5Hash, String sha256Hash, FileKnown knownState,
-			String parentPath, String mimeType) {
-		super(db, objId, dataSourceObjectId, TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0, name, fileType, 0L, 0, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, (short) 0, 0, 0, md5Hash, sha256Hash, knownState, parentPath, mimeType, SleuthkitCase.extractExtension(name));
+			String parentPath, String mimeType,
+			String uidStr,
+			long osAccountRowId) {
+			
+		super(db, objId, dataSourceObjectId, TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0, name, fileType, 0L, 0, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, (short) 0, 0, 0, md5Hash, sha256Hash, knownState, parentPath, mimeType, SleuthkitCase.extractExtension(name), uidStr, osAccountRowId);
 	}
 
 	/**
@@ -280,6 +287,6 @@ protected LayoutFile(SleuthkitCase db, long objId, String name,
 			TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
 			TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags,
 			long size, String md5Hash, FileKnown knownState, String parentPath) {
-		this(db, objId, db.getDataSourceObjectId(objId), name, fileType, dirType, metaType, dirFlag, metaFlags, size, 0L, 0L, 0L, 0L, md5Hash, null, knownState, parentPath, null);
+		this(db, objId, db.getDataSourceObjectId(objId), name, fileType, dirType, metaType, dirFlag, metaFlags, size, 0L, 0L, 0L, 0L, md5Hash, null, knownState, parentPath, null, OsAccount.NULL_UID_STR, OsAccount.NO_USER);
 	}
 }
diff --git a/bindings/java/src/org/sleuthkit/datamodel/LocalFile.java b/bindings/java/src/org/sleuthkit/datamodel/LocalFile.java
index 99abf74e70cd7f77cbaa062e0fa1316072064611..bf5edbd804d22f08b605856e57722a2b86eab861 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/LocalFile.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/LocalFile.java
@@ -74,6 +74,10 @@ public class LocalFile extends AbstractFile {
 	 * @param encodingType		     The encoding type of the file.
 	 * @param extension          The extension part of the file name (not
 	 *                           including the '.'), can be null.
+	 * @param uidStr			 String UID of the user as found in in the file
+	 *                           system, can be null.
+	 * @param osAccountRowId	 Row id of the user in the tsk_os_accounts
+	 *                           table.
 	 */
 	LocalFile(SleuthkitCase db,
 			long objId,
@@ -88,10 +92,12 @@ public class LocalFile extends AbstractFile {
 			long dataSourceObjectId,
 			String localPath,
 			TskData.EncodingType encodingType,
-			String extension) {
+			String extension,
+			String uidStr,
+			long osAccountRowId) {
 		super(db, objId, dataSourceObjectId, TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0,
 				name, fileType, 0L, 0, dirType, metaType, dirFlag,
-				metaFlags, size, ctime, crtime, atime, mtime, (short) 0, 0, 0, md5Hash, sha256Hash, knownState, parentPath, mimeType, extension);
+				metaFlags, size, ctime, crtime, atime, mtime, (short) 0, 0, 0, md5Hash, sha256Hash, knownState, parentPath, mimeType, extension, uidStr, osAccountRowId);
 		// TODO (AUT-1904): The parent id should be passed to AbstractContent 
 		// through the class hierarchy contructors, using 
 		// AbstractContent.UNKNOWN_ID as needed.
@@ -222,7 +228,7 @@ protected LocalFile(SleuthkitCase db,
 				AbstractContent.UNKNOWN_ID, parentPath,
 				db.getDataSourceObjectId(objId),
 				localPath,
-				TskData.EncodingType.NONE, null);
+				TskData.EncodingType.NONE, null, OsAccount.NULL_UID_STR, OsAccount.NO_USER);
 	}
 
 	/**
diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccount.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccount.java
new file mode 100644
index 0000000000000000000000000000000000000000..829614322da8ef40a0a214af409a138f6796ba52
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccount.java
@@ -0,0 +1,85 @@
+/*
+ * Sleuth Kit Data Model
+ *
+ * Copyright 2020 Basis Technology Corp.
+ * Contact: carrier <at> sleuthkit <dot> org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sleuthkit.datamodel;
+
+import java.util.Optional;
+
+/**
+ * Abstracts an OS User account. 
+ * 
+ * A user may own  files and (some) artifacts.
+ * 
+ */
+public class OsAccount {
+
+	final static long NO_USER = -1;
+	final static String NULL_UID_STR = null;
+	
+	private final long rowId;	// row id in the tsk_os_accounts table
+	private final long dataSourceObjId;
+	private final String userName;	// user login name - may be null
+	private final String realm;		// realm where the username is unique - a domain or a host name, may be null
+	private final String uniqueId;	// a unique sid/uid, may be null
+	private final String signature; // some to uniquely identify this user - either the uid or the realm/userName.
+	private final Long artifactObjId; // object id of the backing artifact, may be null if one hasnt been created yet.
+
+	
+	/** 
+	 * Creates an OsAccount with a realm/username and unique id, and signature
+	 */
+	OsAccount(long rowId, long dataSourceObjId, String userName, String realm,  String uniqueId, String signature, long artifactObjId ) {
+		
+		this.rowId = rowId;
+		this.dataSourceObjId = dataSourceObjId;
+		this.uniqueId = uniqueId;
+		this.userName = userName;
+		this.realm = realm;
+		this.signature = signature;
+		this.artifactObjId = artifactObjId;
+	}
+	
+	public long getRowId() {
+		return rowId;
+	}
+	
+	public long getDataSourceObjId() {
+		return dataSourceObjId;
+	}
+	
+	public Optional<String> getUniqueId() {
+		return Optional.ofNullable(uniqueId);
+	}
+
+	public String getSignature() {
+		return signature;
+	}
+	
+	public Optional<String> getRealm() {
+		return Optional.ofNullable(realm);
+	}
+
+	public Optional<String> getUserName() {
+		return Optional.ofNullable(userName);
+	}
+	
+	public Optional<Long> getArtifactObjId() {
+		return Optional.ofNullable(artifactObjId);
+	}
+}
\ No newline at end of file
diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..6dfe206e25b78ac7d021c28771e7ec08412c8be8
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java
@@ -0,0 +1,299 @@
+/*
+ * Sleuth Kit Data Model
+ *
+ * Copyright 2020 Basis Technology Corp.
+ * Contact: carrier <at> sleuthkit <dot> org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.datamodel;
+
+import com.google.common.base.Strings;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection;
+import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
+
+/**
+ * Responsible for creating/updating/retrieving the OS user accounts for files
+ * and artifacts.
+ *
+ */
+public class OsAccountManager {
+
+	private static final Logger LOGGER = Logger.getLogger(OsAccountManager.class.getName());
+
+	private final SleuthkitCase db;
+
+	/**
+	 * Construct a OsUserManager for the given SleuthkitCase.
+	 *
+	 * @param skCase The SleuthkitCase
+	 *
+	 */
+	OsAccountManager(SleuthkitCase skCase) {
+		this.db = skCase;
+	}
+
+	/**
+	 * Gets or creates a OS user account with given unique id or given user name
+	 * for the given data source.
+	 *
+	 * @param dataSourceId    Data source object id.
+	 * @param uniqueAccountId User sid/uid.
+	 * @param userName        User name.
+	 * @param realm           Realm within which the accountId/userName is
+	 *                        unique.
+	 * @param transaction     Transaction to use for database operation.
+	 *
+	 * @return Row id of the row matching the given user id and data source.
+	 */
+	long createOrGetOsAccount(long dataSourceId, String uniqueAccountId, String userName, String realm, CaseDbTransaction transaction) throws TskCoreException {
+
+		// ensure at least one of the two is supplied - unique id or user name
+		if (Strings.isNullOrEmpty(uniqueAccountId) && Strings.isNullOrEmpty(userName)) {
+			throw new IllegalArgumentException("Cannot create OS User with both uniqueId and userName as null.");
+		}
+
+		CaseDbConnection connection = transaction.getConnection();
+
+		// First search for user by uniqueId
+		OsAccount osAccount = getOsAccountByUniqueId(dataSourceId, uniqueAccountId, connection);
+		if (osAccount != null) {
+			return osAccount.getRowId();
+		}
+
+		// search by user name
+		osAccount = getOsAccountByName(dataSourceId, userName, realm, connection);
+		if (osAccount != null) {
+			return osAccount.getRowId();
+		}
+
+		// could'nt find it, create a new account
+		return createOsAccount(dataSourceId, uniqueAccountId, userName, realm, connection);
+
+	}
+
+	/**
+	 * Creates a user with the given uid, name, and realm.
+	 *
+	 * @param dataSourceId Data source object id.
+	 * @param uniqueId     User sid/uid. May be null/empty.
+	 * @param userName     User name. may be null or empty.
+	 * @param realm	       Realm - domain or host name.
+	 * @param connection   Database connection to use.
+	 *
+	 * @return
+	 *
+	 * @throws TskCoreException
+	 */
+	private long createOsAccount(long dataSourceId, String uniqueId, String userName, String realm, CaseDbConnection connection) throws TskCoreException {
+
+		String signature;
+		if (Strings.isNullOrEmpty(uniqueId) == false) {
+			signature = uniqueId;
+		} else {
+			if (Strings.isNullOrEmpty(realm)) {
+				signature = userName;
+			} else {
+				signature = String.format("%s/%s", realm, userName);
+			}
+		}
+
+		db.acquireSingleUserCaseWriteLock();
+		try {
+			String userInsertSQL = "INSERT INTO tsk_os_accounts(data_source_obj_id, user_name, realm, unique_id, signature, artifact_obj_id)"
+					+ " VALUES (?, ?, ?, ?, ?, ?)"; // NON-NLS
+
+			PreparedStatement preparedStatement = connection.getPreparedStatement(userInsertSQL, Statement.RETURN_GENERATED_KEYS);
+			preparedStatement.clearParameters();
+
+			preparedStatement.setLong(1, dataSourceId);
+			preparedStatement.setString(2, userName);
+			preparedStatement.setString(3, realm);
+			preparedStatement.setString(4, uniqueId);
+			preparedStatement.setString(5, signature);
+			preparedStatement.setNull(6, java.sql.Types.BIGINT);
+			
+
+			connection.executeUpdate(preparedStatement);
+
+			// Read back the row id
+			try (ResultSet resultSet = preparedStatement.getGeneratedKeys();) {
+				if (resultSet.next()) {
+					return resultSet.getLong(1); //last_insert_rowid()
+				} else {
+					throw new SQLException("Error executing  " + userInsertSQL);
+				}
+			}
+		} catch (SQLException ex) {
+			LOGGER.log(Level.SEVERE, null, ex);
+			throw new TskCoreException(String.format("Error adding user with uniqueId = %s, userName = %s for data source object id %d", uniqueId, userName, dataSourceId), ex);
+		} finally {
+			db.releaseSingleUserCaseWriteLock();
+		}
+	}
+
+	/**
+	 * Get the OsUser with the given unique user id and the specified data
+	 * source. This should be called only if its certain that this user with
+	 * uniqueId exists in the database.
+	 *
+	 * @param dataSourceId Data source object id.
+	 * @param uniqueId     User sid/uid.
+	 *
+	 * @return OsUser.
+	 *
+	 * @throws TskCoreException         If there is an error getting the user.
+	 * @throws IllegalArgumentException If no matching user is found.
+	 */
+	public OsAccount getOsAccount(long dataSourceId, String uniqueId) throws TskCoreException {
+
+		try (CaseDbConnection connection = this.db.getConnection()) {
+
+			OsAccount osAccount = getOsAccountByUniqueId(dataSourceId, uniqueId, connection);
+			if (osAccount == null) {
+				throw new IllegalArgumentException(String.format("No user found with id %s and data source object id = %d ", uniqueId, dataSourceId));
+			}
+
+			return osAccount;
+		}
+	}
+
+	/**
+	 * Gets a user by the uniqueId. Returns null if no matching user is found.
+	 *
+	 * @param dataSourceId
+	 * @param uniqueId
+	 * @param connection
+	 *
+	 * @return OsUser, null if no user with matching uniqueId is found.
+	 *
+	 * @throws TskCoreException
+	 */
+	private OsAccount getOsAccountByUniqueId(long dataSourceId, String uniqueId, CaseDbConnection connection) throws TskCoreException {
+
+		String queryString = "SELECT * FROM tsk_os_accounts"
+				+ " WHERE data_source_obj_id = " + dataSourceId
+				+ " AND LOWER(unique_id) = LOWER('" + uniqueId + "')";
+
+		try (Statement s = connection.createStatement();
+				ResultSet rs = connection.executeQuery(s, queryString)) {
+
+			if (!rs.next()) {
+				return null;	// no match found
+			} else {
+				return new OsAccount(rs.getLong("id"), rs.getLong("data_source_obj_id"), rs.getString("user_name"), rs.getString("realm"), rs.getString("unique_id"), rs.getString("signature"), rs.getLong("artifact_obj_id"));
+			}
+		} catch (SQLException ex) {
+			throw new TskCoreException(String.format("Error getting OS user account for unique id = %s,  data source  objId = %d ", uniqueId, dataSourceId), ex);
+		}
+	}
+
+	/**
+	 * Gets a user by the realm/userName. Returns null if no matching user is
+	 * found.
+	 *
+	 * @param dataSourceId Data source object id.
+	 * @param userName     User name.
+	 * @param realm	       Realm - domain or host name.
+	 * @param connection   Database connection to use.
+	 *
+	 * @return OsUser, null if no user with matching uniqueId is found.
+	 *
+	 * @throws TskCoreException
+	 */
+	private OsAccount getOsAccountByName(long dataSourceId, String userName, String realm, CaseDbConnection connection) throws TskCoreException {
+
+		String queryString = "SELECT * FROM tsk_os_accounts"
+				+ " WHERE data_source_obj_id = " + dataSourceId
+				+ " AND LOWER(user_name) = LOWER('" + userName + "')"
+				+ " AND LOWER(realm) = LOWER('" + realm + "')";
+
+		try (Statement s = connection.createStatement();
+				ResultSet rs = connection.executeQuery(s, queryString)) {
+
+			if (!rs.next()) {
+				return null;	// no match found
+			} else {
+				return new OsAccount(rs.getLong("id"), rs.getLong("data_source_obj_id"), rs.getString("user_name"), rs.getString("realm"), rs.getString("unique_id"), rs.getString("signature"), rs.getLong("artifact_obj_id"));
+			}
+		} catch (SQLException ex) {
+			throw new TskCoreException(String.format("Error getting OS user account for realm = %s and userName = %s,  data source  objId = %d ", realm, userName, dataSourceId), ex);
+		}
+	}
+
+	/**
+	 * Get the OsUser with the given row id.
+	 *
+	 * @param userRowId Row id for the user account.
+	 *
+	 * @return OsUser.
+	 *
+	 * @throws TskCoreException         If there is an error getting the user.
+	 * @throws IllegalArgumentException If no matching user is found.
+	 */
+	public OsAccount getOsAccount(long userRowId) throws TskCoreException {
+
+		String queryString = "SELECT * FROM tsk_os_accounts"
+				+ " WHERE id = " + userRowId;
+
+		try (CaseDbConnection connection = this.db.getConnection();
+				Statement s = connection.createStatement();
+				ResultSet rs = connection.executeQuery(s, queryString)) {
+
+			if (!rs.next()) {
+				throw new IllegalArgumentException(String.format("No user found with  row id = %d ", userRowId));
+			} else {
+				Long artifactObjId = rs.getLong("artifact_obj_id");
+				if (rs.wasNull()) {
+					throw new TskCoreException(String.format("No artifact created yet for user, row id = %d ", userRowId));
+				}
+
+				// BlackboardArtifact accountArtifact = db.getArtifactById(artifactObjId);
+				return new OsAccount(rs.getLong("id"), rs.getLong("data_source_obj_id"), rs.getString("user_name"),
+						rs.getString("realm"), rs.getString("unique_id"), rs.getString("signature"), artifactObjId);
+			}
+		} catch (SQLException ex) {
+			throw new TskCoreException("Error getting OS user account ", ex);
+		}
+	}
+
+	/**
+	 * Updates the artifact object id for the user with specified row id.
+	 *
+	 * @param userRowId     Row id of the row to be updated
+	 * @param artifactObjId Artifact object id to be updated with.
+	 * @param transaction   Transaction to use for database operation.
+	 *
+	 * @throws TskCoreException
+	 */
+	public void updateOsAccount(long userRowId, long artifactObjId, CaseDbTransaction transaction) throws TskCoreException {
+
+		CaseDbConnection connection = transaction.getConnection();
+
+		db.acquireSingleUserCaseWriteLock();
+		try (Statement updateStatement = connection.createStatement()) {
+			connection.executeUpdate(updateStatement, "UPDATE tsk_os_accounts SET artifact_obj_id = " + artifactObjId + " WHERE id = " + userRowId);
+		} catch (SQLException ex) {
+			LOGGER.log(Level.SEVERE, null, ex);
+			throw new TskCoreException(String.format("Error updating user row id %s", userRowId), ex);
+		} finally {
+			db.releaseSingleUserCaseWriteLock();
+		}
+	}
+}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/SlackFile.java b/bindings/java/src/org/sleuthkit/datamodel/SlackFile.java
index df038e6021013a8f0a5b706e27721a721b2856d5..bebc1037d1aec437dcdc8038ca92fd02d64a67aa 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/SlackFile.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/SlackFile.java
@@ -76,6 +76,11 @@ public class SlackFile extends FsContent {
 	 *                           yet been determined.
 	 * @param extension	         The extension part of the file name (not
 	 *                           including the '.'), can be null.
+	 * @param uidStr			 String UID of the user as found in in the file
+	 *                           system, can be null.
+	 * @param osAccountRowId		 Row id of the user in the tsk_os_accounts
+	 *                           table.
+	 * 
 	 */
 	SlackFile(SleuthkitCase db,
 			long objId,
@@ -90,8 +95,10 @@ public class SlackFile extends FsContent {
 			long ctime, long crtime, long atime, long mtime,
 			short modes, int uid, int gid,
 			String md5Hash, String sha256Hash, FileKnown knownState, String parentPath, String mimeType,
-			String extension) {
-		super(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TskData.TSK_DB_FILES_TYPE_ENUM.SLACK, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, mimeType, extension);
+			String extension,
+			String uidStr,
+			long osAccountRowId) {
+		super(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TskData.TSK_DB_FILES_TYPE_ENUM.SLACK, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, mimeType, extension, uidStr, osAccountRowId);
 	}
 
 	/**
diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
index 753a130bc961d67f5052dd937deeac496e74f47f..8458ab49727ca9aa21620a8ef86850318b6a84d3 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
@@ -212,6 +212,7 @@ public class SleuthkitCase {
 	private CaseDbAccessManager dbAccessManager;
 	private TaggingManager taggingMgr;
 	private ScoringManager scoringManager;
+	private OsAccountManager osAccountManager;
 
 	private final Map<String, Set<Long>> deviceIdToDatasourceObjIdMap = new HashMap<>();
 
@@ -391,6 +392,7 @@ private void init() throws Exception {
 		dbAccessManager = new CaseDbAccessManager(this);
 		taggingMgr = new TaggingManager(this);
 		scoringManager = new ScoringManager(this);
+		osAccountManager = new OsAccountManager(this);
 	}
 
 	/**
@@ -514,6 +516,17 @@ public ScoringManager getScoringManager() throws TskCoreException {
 		return scoringManager;
 	}
 	
+	/**
+	 * Gets the OS account manager for this case.
+	 *
+	 * @return The per case OsAccountManager object.
+	 *
+	 * @throws TskCoreException
+	 */
+	public OsAccountManager getOsAccountManager() throws TskCoreException {
+		return osAccountManager;
+	}
+	
 	/**
 	 * Make sure the predefined artifact types are in the artifact types table.
 	 *
@@ -5765,8 +5778,8 @@ public VirtualDirectory addVirtualDirectory(long parentId, String directoryName,
 
 			// Insert a row for the virtual directory into the tsk_files table.
 			// INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type,
-			// dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, known, mime_type, parent_path, data_source_obj_id,extension)
-			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)
+			// dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, known, mime_type, parent_path, data_source_obj_id,extension,uid_str, os_account_row_id)
+			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?,?,?)
 			PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
 			statement.clearParameters();
 			statement.setLong(1, newObjId);
@@ -5831,8 +5844,13 @@ public VirtualDirectory addVirtualDirectory(long parentId, String directoryName,
 
 			//extension, since this is not really file we just set it to null
 			statement.setString(21, null);
+			
+			statement.setString(22, OsAccount.NULL_UID_STR); // uidStr
+			statement.setNull(23, java.sql.Types.BIGINT); // osAccountRowId
+			
 			connection.executeUpdate(statement);
 
+			
 			return new VirtualDirectory(this, newObjId, dataSourceObjectId, directoryName, dirType,
 					metaType, dirFlag, metaFlags, null, null, FileKnown.UNKNOWN,
 					parentPath);
@@ -5910,8 +5928,8 @@ public LocalDirectory addLocalDirectory(long parentId, String directoryName, Cas
 
 			// Insert a row for the local directory into the tsk_files table.
 			// INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type,
-			// dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, sha256, known, mime_type, parent_path, data_source_obj_id)
-			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+			// dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, sha256, known, mime_type, parent_path, data_source_obj_id, extension, uid_str, os_account_row_id)
+			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
 			PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
 			statement.clearParameters();
 			statement.setLong(1, newObjId);
@@ -5963,6 +5981,9 @@ public LocalDirectory addLocalDirectory(long parentId, String directoryName, Cas
 			//extension, since this is a directory we just set it to null
 			statement.setString(21, null);
 
+			statement.setString(22, OsAccount.NULL_UID_STR); // uidStr
+			statement.setNull(23, java.sql.Types.BIGINT); // osAccountRowId
+			
 			connection.executeUpdate(statement);
 
 			return new LocalDirectory(this, newObjId, dataSourceObjectId, directoryName, dirType,
@@ -6014,8 +6035,8 @@ public LocalFilesDataSource addLocalFilesDataSource(String deviceId, String root
 			// its own object id.
 			// INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path,
 			// dir_type, meta_type, dir_flags, meta_flags, size, ctime, crtime,
-			// atime, mtime, md5, known, mime_type, parent_path, data_source_obj_id, extension)
-			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)
+			// atime, mtime, md5, known, mime_type, parent_path, data_source_obj_id, extension, uid_str, os_account_row_id)
+			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?, ?, ?)
 			PreparedStatement preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
 			preparedStatement.clearParameters();
 			preparedStatement.setLong(1, newObjId);
@@ -6045,6 +6066,8 @@ public LocalFilesDataSource addLocalFilesDataSource(String deviceId, String root
 			preparedStatement.setString(19, parentPath);
 			preparedStatement.setLong(20, newObjId);
 			preparedStatement.setString(21, null); //extension, just set it to null
+			preparedStatement.setString(22, OsAccount.NULL_UID_STR); // uidStr
+			preparedStatement.setNull(23, java.sql.Types.BIGINT); // osAccountRowId
 			connection.executeUpdate(preparedStatement);
 
 			return new LocalFilesDataSource(this, newObjId, newObjId, deviceId, rootDirectoryName, dirType, metaType, dirFlag, metaFlags, timeZone, null, null, FileKnown.UNKNOWN, parentPath);
@@ -6391,7 +6414,7 @@ public FsContent addFileSystemFile(long dataSourceObjId, long fsObjId,
 			} else {
 				parentPath = "/";
 			}
-
+			
 			PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE_SYSTEM_FILE);
 			statement.clearParameters();
 			statement.setLong(1, objectId);											// obj_is
@@ -6422,7 +6445,7 @@ public FsContent addFileSystemFile(long dataSourceObjId, long fsObjId,
 			connection.executeUpdate(statement);
 
 			DerivedFile derivedFile = new DerivedFile(this, objectId, dataSourceObjId, fileName, dirType, metaType, dirFlag, metaFlags,
-					size, ctime, crtime, atime, mtime, null, null, null, parentPath, null, parent.getId(), null, null, extension);
+					size, ctime, crtime, atime, mtime, null, null, null, parentPath, null, parent.getId(), null, null, extension, OsAccount.NULL_UID_STR, OsAccount.NO_USER);
 
 			timelineManager.addEventsForNewFile(derivedFile, connection);
 
@@ -6434,7 +6457,7 @@ public FsContent addFileSystemFile(long dataSourceObjId, long fsObjId,
 					dirType, metaType, dirFlag, metaFlags,
 					size, ctime, crtime, atime, mtime,
 					(short) 0, 0, 0, null, null, null, parentPath, null,
-					extension);
+					extension, OsAccount.NULL_UID_STR, OsAccount.NO_USER);
 
 		} catch (SQLException ex) {
 			logger.log(Level.WARNING, "Failed to add file system file", ex);
@@ -6530,8 +6553,8 @@ public final List<LayoutFile> addLayoutFiles(Content parent, List<TskFileRange>
 				 * INSERT INTO tsk_files (obj_id, fs_obj_id, name, type,
 				 * has_path, dir_type, meta_type, dir_flags, meta_flags, size,
 				 * ctime, crtime, atime, mtime, md5, known, mime_type,
-				 * parent_path, data_source_obj_id,extension) VALUES (?, ?, ?,
-				 * ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)
+				 * parent_path, data_source_obj_id,extension, uid_str, os_account_row_id) VALUES (?, ?, ?,
+				 * ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?, ?, ?)
 				 */
 				PreparedStatement prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
 				prepStmt.clearParameters();
@@ -6558,6 +6581,10 @@ public final List<LayoutFile> addLayoutFiles(Content parent, List<TskFileRange>
 
 				//extension, since this is not a FS file we just set it to null
 				prepStmt.setString(21, null);
+				
+				prepStmt.setString(22, OsAccount.NULL_UID_STR); // uidStr
+				prepStmt.setNull(23, java.sql.Types.BIGINT); // osAccountRowId
+				
 				connection.executeUpdate(prepStmt);
 
 				/*
@@ -6590,7 +6617,9 @@ public final List<LayoutFile> addLayoutFiles(Content parent, List<TskFileRange>
 						null, null,
 						FileKnown.UNKNOWN,
 						parent.getUniquePath(),
-						null));
+						null,
+						OsAccount.NULL_UID_STR,
+						OsAccount.NO_USER));
 			}
 
 			transaction.commit();
@@ -6711,8 +6740,8 @@ public final List<LayoutFile> addCarvedFiles(CarvingResult carvingResult) throws
 				 * INSERT INTO tsk_files (obj_id, fs_obj_id, name, type,
 				 * has_path, dir_type, meta_type, dir_flags, meta_flags, size,
 				 * ctime, crtime, atime, mtime, md5, known, mime_type,
-				 * parent_path, data_source_obj_id,extenion) VALUES (?, ?, ?, ?,
-				 * ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)
+				 * parent_path, data_source_obj_id,extenion, uid_str, os_account_row_id) 
+				 * VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
 				 */
 				PreparedStatement prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
 				prepStmt.clearParameters();
@@ -6741,6 +6770,10 @@ public final List<LayoutFile> addCarvedFiles(CarvingResult carvingResult) throws
 				prepStmt.setString(19, parentPath); // parent path
 				prepStmt.setLong(20, carvedFilesDir.getDataSourceObjectId()); // data_source_obj_id
 				prepStmt.setString(21, extractExtension(carvedFile.getName())); //extension
+				
+				prepStmt.setString(22, OsAccount.NULL_UID_STR); // uidStr
+				prepStmt.setNull(23, java.sql.Types.BIGINT); // osAccountRowId
+				
 				connection.executeUpdate(prepStmt);
 
 				/*
@@ -6775,7 +6808,9 @@ public final List<LayoutFile> addCarvedFiles(CarvingResult carvingResult) throws
 						null, null,
 						FileKnown.UNKNOWN,
 						parentPath,
-						null));
+						null,
+						OsAccount.NULL_UID_STR,
+						OsAccount.NO_USER));
 			}
 
 			transaction.commit();
@@ -6917,6 +6952,9 @@ public DerivedFile addDerivedFile(String fileName, String localPath,
 			final String extension = extractExtension(fileName);
 			//extension
 			statement.setString(21, extension);
+			
+			statement.setString(22, OsAccount.NULL_UID_STR); // uidStr
+			statement.setNull(23, java.sql.Types.BIGINT); // osAccountRowId
 
 			connection.executeUpdate(statement);
 
@@ -6924,7 +6962,7 @@ public DerivedFile addDerivedFile(String fileName, String localPath,
 			addFilePath(connection, newObjId, localPath, encodingType);
 
 			DerivedFile derivedFile = new DerivedFile(this, newObjId, dataSourceObjId, fileName, dirType, metaType, dirFlag, metaFlags,
-					savedSize, ctime, crtime, atime, mtime, null, null, null, parentPath, localPath, parentId, null, encodingType, extension);
+					savedSize, ctime, crtime, atime, mtime, null, null, null, parentPath, localPath, parentId, null, encodingType, extension, OsAccount.NULL_UID_STR, OsAccount.NO_USER);
 
 			timelineManager.addEventsForNewFile(derivedFile, connection);
 			transaction.commit();
@@ -7034,7 +7072,7 @@ public DerivedFile updateDerivedFile(DerivedFile derivedFile, String localPath,
 			long dataSourceObjId = getDataSourceObjectId(connection, parentId);
 			final String extension = extractExtension(derivedFile.getName());
 			return new DerivedFile(this, derivedFile.getId(), dataSourceObjId, derivedFile.getName(), dirType, metaType, dirFlag, metaFlags,
-					savedSize, ctime, crtime, atime, mtime, null, null, null, parentPath, localPath, parentId, null, encodingType, extension);
+					savedSize, ctime, crtime, atime, mtime, null, null, null, parentPath, localPath, parentId, null, encodingType, extension, derivedFile.getUidStr(), derivedFile.getUserRowId());
 		} catch (SQLException ex) {
 			connection.rollbackTransaction();
 			throw new TskCoreException("Failed to add derived file to case database", ex);
@@ -7165,8 +7203,8 @@ public LocalFile addLocalFile(String fileName, String localPath,
 			// Insert a row for the local/logical file into the tsk_files table.
 			// INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type,
 			// dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, known, mime_type,
-			// parent_path, data_source_obj_id,extension)
-			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)
+			// parent_path, data_source_obj_id,extension, uid_str, os_account_row_id)
+			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?, ?, ?)
 			PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
 			statement.clearParameters();
 			statement.setLong(1, objectId);
@@ -7217,6 +7255,9 @@ public LocalFile addLocalFile(String fileName, String localPath,
 			final String extension = extractExtension(fileName);
 			statement.setString(21, extension);
 
+			statement.setString(22, OsAccount.NULL_UID_STR); // uidStr
+			statement.setNull(23, java.sql.Types.BIGINT); // osAccountRowId
+
 			connection.executeUpdate(statement);
 			addFilePath(connection, objectId, localPath, encodingType);
 			LocalFile localFile = new LocalFile(this,
@@ -7233,7 +7274,8 @@ public LocalFile addLocalFile(String fileName, String localPath,
 					parent.getId(), parentPath,
 					dataSourceObjId,
 					localPath,
-					encodingType, extension);
+					encodingType, extension, 
+					OsAccount.NULL_UID_STR, OsAccount.NO_USER);
 			getTimelineManager().addEventsForNewFile(localFile, connection);
 			return localFile;
 
@@ -7345,8 +7387,8 @@ public LayoutFile addLayoutFile(String fileName,
 			 * tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type,
 			 * meta_type, dir_flags, meta_flags, size, ctime, crtime, atime,
 			 * mtime, md5, known, mime_type, parent_path,
-			 * data_source_obj_id,extenion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?,
-			 * ?, ?, ?, ?, ?, ?, ?,?)
+			 * data_source_obj_id,extenion, uid_str, os_account_row_id) 
+			 * VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
 			 */
 			PreparedStatement prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
 			prepStmt.clearParameters();
@@ -7385,6 +7427,10 @@ public LayoutFile addLayoutFile(String fileName,
 			prepStmt.setLong(20, parent.getDataSource().getId()); // data_source_obj_id
 
 			prepStmt.setString(21, extractExtension(fileName)); 				//extension
+			
+			prepStmt.setString(22, OsAccount.NULL_UID_STR); // uidStr
+			prepStmt.setNull(23, java.sql.Types.BIGINT); // osAccountRowId
+			
 			connection.executeUpdate(prepStmt);
 
 			/*
@@ -7419,7 +7465,9 @@ public LayoutFile addLayoutFile(String fileName,
 					null, null,
 					FileKnown.UNKNOWN,
 					parentPath,
-					null);
+					null,
+					OsAccount.NULL_UID_STR,
+					OsAccount.NO_USER);
 
 			transaction.commit();
 			transaction = null;
@@ -8709,6 +8757,12 @@ private List<AbstractFile> resultSetToAbstractFiles(ResultSet rs, CaseDbConnecti
 					if (parentPath == null) {
 						parentPath = "/"; //NON-NLS
 					}
+					
+					long osAccountRowId = rs.getInt("os_account_row_id");
+					if (rs.wasNull()) {
+						osAccountRowId = OsAccount.NO_USER;
+					}
+		
 					LayoutFile lf = new LayoutFile(this,
 							rs.getLong("obj_id"), //NON-NLS
 							rs.getLong("data_source_obj_id"),
@@ -8718,7 +8772,9 @@ private List<AbstractFile> resultSetToAbstractFiles(ResultSet rs, CaseDbConnecti
 							TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), rs.getShort("meta_flags"), //NON-NLS
 							rs.getLong("size"), //NON-NLS
 							rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
-							rs.getString("md5"), rs.getString("sha256"), FileKnown.valueOf(rs.getByte("known")), parentPath, rs.getString("mime_type")); //NON-NLS
+							rs.getString("md5"), rs.getString("sha256"), FileKnown.valueOf(rs.getByte("known")), parentPath, 
+							rs.getString("mime_type"), 
+							rs.getString("uid_str"), osAccountRowId ); //NON-NLS
 					results.add(lf);
 				} else if (type == TSK_DB_FILES_TYPE_ENUM.DERIVED.getFileType()) {
 					final DerivedFile df;
@@ -8753,6 +8809,11 @@ private List<AbstractFile> resultSetToAbstractFiles(ResultSet rs, CaseDbConnecti
 	 * @throws SQLException
 	 */
 	org.sleuthkit.datamodel.File file(ResultSet rs, FileSystem fs) throws SQLException {
+		long osAccountRowId = rs.getInt("os_account_row_id");
+		if (rs.wasNull()) {
+			osAccountRowId = OsAccount.NO_USER;
+		}
+				
 		org.sleuthkit.datamodel.File f = new org.sleuthkit.datamodel.File(this, rs.getLong("obj_id"), //NON-NLS
 				rs.getLong("data_source_obj_id"), rs.getLong("fs_obj_id"), //NON-NLS
 				TskData.TSK_FS_ATTR_TYPE_ENUM.valueOf(rs.getShort("attr_type")), //NON-NLS
@@ -8764,7 +8825,7 @@ org.sleuthkit.datamodel.File file(ResultSet rs, FileSystem fs) throws SQLExcepti
 				rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
 				(short) rs.getInt("mode"), rs.getInt("uid"), rs.getInt("gid"), //NON-NLS
 				rs.getString("md5"), rs.getString("sha256"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS
-				rs.getString("parent_path"), rs.getString("mime_type"), rs.getString("extension")); //NON-NLS
+				rs.getString("parent_path"), rs.getString("mime_type"), rs.getString("extension"), rs.getString("uid_str"), osAccountRowId); //NON-NLS
 		f.setFileSystem(fs);
 		return f;
 	}
@@ -8781,6 +8842,11 @@ org.sleuthkit.datamodel.File file(ResultSet rs, FileSystem fs) throws SQLExcepti
 	 * @throws SQLException thrown if SQL error occurred
 	 */
 	Directory directory(ResultSet rs, FileSystem fs) throws SQLException {
+		long osAccountRowId = rs.getInt("os_account_row_id");
+		if (rs.wasNull()) {
+			osAccountRowId = OsAccount.NO_USER;
+		}
+		
 		Directory dir = new Directory(this, rs.getLong("obj_id"), rs.getLong("data_source_obj_id"), rs.getLong("fs_obj_id"), //NON-NLS
 				TskData.TSK_FS_ATTR_TYPE_ENUM.valueOf(rs.getShort("attr_type")), //NON-NLS
 				rs.getInt("attr_id"), rs.getString("name"), rs.getLong("meta_addr"), rs.getInt("meta_seq"), //NON-NLS
@@ -8791,7 +8857,7 @@ Directory directory(ResultSet rs, FileSystem fs) throws SQLException {
 				rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
 				rs.getShort("mode"), rs.getInt("uid"), rs.getInt("gid"), //NON-NLS
 				rs.getString("md5"), rs.getString("sha256"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS
-				rs.getString("parent_path")); //NON-NLS
+				rs.getString("parent_path"), rs.getString("uid_str"), osAccountRowId); //NON-NLS
 		dir.setFileSystem(fs);
 		return dir;
 	}
@@ -8928,6 +8994,12 @@ private DerivedFile derivedFile(ResultSet rs, CaseDbConnection connection, long
 		if (parentPath == null) {
 			parentPath = "";
 		}
+		
+		long osAccountRowId = rs.getInt("os_account_row_id");
+		if (rs.wasNull()) {
+			osAccountRowId = OsAccount.NO_USER;
+		}
+				
 		final DerivedFile df = new DerivedFile(this, objId, rs.getLong("data_source_obj_id"),
 				rs.getString("name"), //NON-NLS
 				TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), //NON-NLS
@@ -8937,7 +9009,8 @@ private DerivedFile derivedFile(ResultSet rs, CaseDbConnection connection, long
 				rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
 				rs.getString("md5"), rs.getString("sha256"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS
 				parentPath, localPath, parentId, rs.getString("mime_type"),
-				encodingType, rs.getString("extension"));
+				encodingType, rs.getString("extension"), 
+				rs.getString("uid_str"), osAccountRowId);
 		return df;
 	}
 
@@ -8981,6 +9054,11 @@ private LocalFile localFile(ResultSet rs, CaseDbConnection connection, long pare
 		if (null == parentPath) {
 			parentPath = "";
 		}
+		long osAccountRowId = rs.getInt("os_account_row_id");
+		if (rs.wasNull()) {
+			osAccountRowId = OsAccount.NO_USER;
+		}
+		
 		LocalFile file = new LocalFile(this, objId, rs.getString("name"), //NON-NLS
 				TSK_DB_FILES_TYPE_ENUM.valueOf(rs.getShort("type")), //NON-NLS
 				TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), //NON-NLS
@@ -8990,7 +9068,8 @@ private LocalFile localFile(ResultSet rs, CaseDbConnection connection, long pare
 				rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
 				rs.getString("mime_type"), rs.getString("md5"), rs.getString("sha256"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS
 				parentId, parentPath, rs.getLong("data_source_obj_id"),
-				localPath, encodingType, rs.getString("extension"));
+				localPath, encodingType, rs.getString("extension"),
+				rs.getString("uid_str"), osAccountRowId);
 		return file;
 	}
 
@@ -9006,6 +9085,10 @@ private LocalFile localFile(ResultSet rs, CaseDbConnection connection, long pare
 	 * @throws SQLException
 	 */
 	org.sleuthkit.datamodel.SlackFile slackFile(ResultSet rs, FileSystem fs) throws SQLException {
+		long osAccountRowId = rs.getInt("os_account_row_id");
+		if (rs.wasNull()) {
+			osAccountRowId = OsAccount.NO_USER;
+		}
 		org.sleuthkit.datamodel.SlackFile f = new org.sleuthkit.datamodel.SlackFile(this, rs.getLong("obj_id"), //NON-NLS
 				rs.getLong("data_source_obj_id"), rs.getLong("fs_obj_id"), //NON-NLS
 				TskData.TSK_FS_ATTR_TYPE_ENUM.valueOf(rs.getShort("attr_type")), //NON-NLS
@@ -9017,7 +9100,8 @@ org.sleuthkit.datamodel.SlackFile slackFile(ResultSet rs, FileSystem fs) throws
 				rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
 				(short) rs.getInt("mode"), rs.getInt("uid"), rs.getInt("gid"), //NON-NLS
 				rs.getString("md5"), rs.getString("sha256"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS
-				rs.getString("parent_path"), rs.getString("mime_type"), rs.getString("extension")); //NON-NLS
+				rs.getString("parent_path"), rs.getString("mime_type"), rs.getString("extension"), 
+				rs.getString("uid_str"), osAccountRowId); //NON-NLS
 		f.setFileSystem(fs);
 		return f;
 	}
@@ -9071,6 +9155,10 @@ List<Content> fileChildren(ResultSet rs, CaseDbConnection connection, long paren
 						if (parentPath == null) {
 							parentPath = "";
 						}
+						long osAccountRowId = rs.getInt("os_account_row_id");
+						if (rs.wasNull()) {
+							osAccountRowId = OsAccount.NO_USER;
+						}
 						final LayoutFile lf = new LayoutFile(this, rs.getLong("obj_id"),
 								rs.getLong("data_source_obj_id"), rs.getString("name"), type,
 								TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")),
@@ -9079,7 +9167,8 @@ List<Content> fileChildren(ResultSet rs, CaseDbConnection connection, long paren
 								rs.getLong("size"),
 								rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"),
 								rs.getString("md5"), rs.getString("sha256"),
-								FileKnown.valueOf(rs.getByte("known")), parentPath, rs.getString("mime_type"));
+								FileKnown.valueOf(rs.getByte("known")), parentPath, rs.getString("mime_type"), 
+								rs.getString("uid_str"), osAccountRowId);
 						children.add(lf);
 						break;
 					}
@@ -11486,8 +11575,8 @@ private enum PREPARED_STATEMENT {
 		SELECT_FILE_DERIVATION_METHOD("SELECT tool_name, tool_version, other FROM tsk_files_derived_method WHERE derived_id = ?"), //NON-NLS
 		SELECT_MAX_OBJECT_ID("SELECT MAX(obj_id) AS max_obj_id FROM tsk_objects"), //NON-NLS
 		INSERT_OBJECT("INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?)"), //NON-NLS
-		INSERT_FILE("INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type, dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, sha256, known, mime_type, parent_path, data_source_obj_id,extension) " //NON-NLS
-				+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), //NON-NLS
+		INSERT_FILE("INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type, dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, sha256, known, mime_type, parent_path, data_source_obj_id, extension, uid_str, os_account_row_id  ) " //NON-NLS
+				+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), //NON-NLS
 		INSERT_FILE_SYSTEM_FILE("INSERT INTO tsk_files(obj_id, fs_obj_id, data_source_obj_id, attr_type, attr_id, name, meta_addr, meta_seq, type, has_path, dir_type, meta_type, dir_flags, meta_flags, size, ctime, crtime, atime, mtime, parent_path, extension)"
 				+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), // NON-NLS
 		UPDATE_DERIVED_FILE("UPDATE tsk_files SET type = ?, dir_type = ?, meta_type = ?, dir_flags = ?,  meta_flags = ?, size= ?, ctime= ?, crtime= ?, atime= ?, mtime= ?, mime_type = ?  "
diff --git a/bindings/java/src/org/sleuthkit/datamodel/SpecialDirectory.java b/bindings/java/src/org/sleuthkit/datamodel/SpecialDirectory.java
index 5db5888d5b34d8a0ebbd926d18b1fa52559c2512..fe00c3e42edcf38984ee1f46d722136ed8efc70a 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/SpecialDirectory.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/SpecialDirectory.java
@@ -48,7 +48,7 @@ public abstract class SpecialDirectory extends AbstractFile {
 			String mimeType) {
 		super(db, objId, dataSourceObjectId, attrType, attrId, name,
 				fileType, metaAddr, metaSeq, dirType, metaType, dirFlag,
-				metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, mimeType, null);
+				metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, mimeType, null, OsAccount.NULL_UID_STR, OsAccount.NO_USER);
 	}
 
 	/**
diff --git a/bindings/java/src/org/sleuthkit/datamodel/TskCaseDbBridge.java b/bindings/java/src/org/sleuthkit/datamodel/TskCaseDbBridge.java
index cee51945acc4b4f0b86cc06ab7dcab0e29548c2d..97fef20429d464f8d0d3cb48ffbc52b461d8f163 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/TskCaseDbBridge.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/TskCaseDbBridge.java
@@ -18,6 +18,7 @@
  */
 package org.sleuthkit.datamodel;
 
+import com.google.common.base.Strings;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
 import java.sql.Statement;
@@ -306,6 +307,7 @@ long addFileSystem(long parentObjId, long imgOffset, int fsType, long blockSize,
      * @param seq         The sequence number from fs_file->meta->seq. 
      * @param parMetaAddr The metadata address of the parent
      * @param parSeq      The parent sequence number if NTFS, -1 otherwise.
+	 * @param ownerUid	  String uid of the file owner.  May be an empty string.
      * 
      * @return 0 if successful, -1 if not
      */
@@ -319,7 +321,7 @@ long addFile(long parentObjId,
         long crtime, long ctime, long atime, long mtime,
         int meta_mode, int gid, int uid,
         String escaped_path, String extension, 
-        long seq, long parMetaAddr, long parSeq) {
+        long seq, long parMetaAddr, long parSeq, String ownerUid) {
         
         // Add the new file to the list
         batchedFiles.add(new FileInfo(parentObjId,
@@ -332,7 +334,7 @@ long addFile(long parentObjId,
                 crtime, ctime, atime, mtime,
                 meta_mode, gid, uid,
                 escaped_path, extension,
-                seq, parMetaAddr, parSeq));
+                seq, parMetaAddr, parSeq, ownerUid));
         
         // Add the current files to the database if we've exceeded the threshold or if we
         // have the root folder.
@@ -371,7 +373,7 @@ private long addBatchedFilesToDb() {
                         fileInfo.crtime, fileInfo.ctime, fileInfo.atime, fileInfo.mtime,
                         fileInfo.meta_mode, fileInfo.gid, fileInfo.uid,
                         null, TskData.FileKnown.UNKNOWN,
-                        fileInfo.escaped_path, fileInfo.extension, 
+                        fileInfo.escaped_path, fileInfo.extension, fileInfo.ownerUid,
                         false, trans);
                     if (fileInfo.fsObjId != fileInfo.parentObjId) {
                         // Add new file ID to the list to send to ingest unless it is the root folder
@@ -486,7 +488,7 @@ long addLayoutFile(long parentObjId,
                 null, null, null, null,
                 null, null, null,
                 null, TskData.FileKnown.UNKNOWN,
-                null, null, 
+                null, null, null,
                 true, trans);
             commitTransaction();
 
@@ -692,6 +694,7 @@ private class FileInfo {
         long seq;
         long parMetaAddr;
         long parSeq;
+		String ownerUid;
         
         FileInfo(long parentObjId, 
             long fsObjId, long dataSourceObjId,
@@ -703,7 +706,7 @@ private class FileInfo {
             long crtime, long ctime, long atime, long mtime,
             int meta_mode, int gid, int uid,
             String escaped_path, String extension, 
-            long seq, long parMetaAddr, long parSeq) {
+            long seq, long parMetaAddr, long parSeq, String ownerUid) {
             
             this.parentObjId = parentObjId;
             this.fsObjId = fsObjId;
@@ -731,6 +734,7 @@ private class FileInfo {
             this.seq = seq;
             this.parMetaAddr = parMetaAddr;
             this.parSeq = parSeq;
+			this.ownerUid = ownerUid;
         }
     }
 	
@@ -768,6 +772,7 @@ private class FileInfo {
 	 * @param known           The file known status.
 	 * @param escaped_path    The escaped path to the file.
 	 * @param extension       The file extension.
+	 * @param ownerUid		  The string user id of the file owner.
 	 * @param hasLayout       True if this is a layout file, false otherwise.
 	 * @param transaction     The open transaction.
 	 *
@@ -785,8 +790,8 @@ private long addFileToDb(long parentObjId,
 			Long crtime, Long ctime, Long atime, Long mtime,
 			Integer meta_mode, Integer gid, Integer uid,
 			String md5, TskData.FileKnown known,
-			String escaped_path, String extension,
-			boolean hasLayout, CaseDbTransaction transaction) throws TskCoreException {
+			String escaped_path, String extension, String ownerUid,
+			boolean hasLayout,  CaseDbTransaction transaction) throws TskCoreException {
 
 		try {
 			SleuthkitCase.CaseDbConnection connection = transaction.getConnection();
@@ -795,8 +800,13 @@ private long addFileToDb(long parentObjId,
 			// INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?)
 			long objectId = caseDb.addObject(parentObjId, TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection);
 			
-			String fileInsert = "INSERT INTO tsk_files (fs_obj_id, obj_id, data_source_obj_id, type, attr_type, attr_id, name, meta_addr, meta_seq, dir_type, meta_type, dir_flags, meta_flags, size, crtime, ctime, atime, mtime, mode, gid, uid, md5, known, parent_path, extension, has_layout)"
-				+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; // NON-NLS
+			//  add the ownerUid to the database. Use that rowId 
+			long osAccountRowId = Strings.isNullOrEmpty(ownerUid) 
+								? OsAccount.NO_USER
+								: caseDb.getOsAccountManager().createOrGetOsAccount(dataSourceObjId, ownerUid, null, null, transaction);
+			
+			String fileInsert = "INSERT INTO tsk_files (fs_obj_id, obj_id, data_source_obj_id, type, attr_type, attr_id, name, meta_addr, meta_seq, dir_type, meta_type, dir_flags, meta_flags, size, crtime, ctime, atime, mtime, mode, gid, uid, md5, known, parent_path, extension, has_layout, uid_str, os_account_row_id)"
+				+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; // NON-NLS
 			PreparedStatement preparedStatement = connection.getPreparedStatement(fileInsert, Statement.NO_GENERATED_KEYS);			
 			preparedStatement.clearParameters();
 			
@@ -878,6 +888,14 @@ private long addFileToDb(long parentObjId,
 			} else {
 				preparedStatement.setNull(26, java.sql.Types.INTEGER);
 			}
+			
+			preparedStatement.setString(27, ownerUid); // uidStr
+			
+			if (osAccountRowId != OsAccount.NO_USER) {
+				preparedStatement.setLong(28, osAccountRowId);
+			} else {
+				preparedStatement.setNull(28, java.sql.Types.BIGINT);
+			}
 			connection.executeUpdate(preparedStatement);
 
 			// If this is not a slack file create the timeline events
@@ -890,7 +908,7 @@ private long addFileToDb(long parentObjId,
 						TskData.TSK_FS_META_TYPE_ENUM.valueOf((short) metaType),
 						TskData.TSK_FS_NAME_FLAG_ENUM.valueOf(dirFlags),
 						(short) metaFlags,
-						size, ctime, crtime, atime, mtime, null, null, null, escaped_path, null, parentObjId, null, null, extension);
+						size, ctime, crtime, atime, mtime, null, null, null, escaped_path, null, parentObjId, null, null, extension, ownerUid, osAccountRowId);
 
 				timelineManager.addEventsForNewFileQuiet(derivedFile, connection);
 			}