diff --git a/.gitignore b/.gitignore index 9faa0a3d..5fffa0e5 100755 --- a/.gitignore +++ b/.gitignore @@ -320,4 +320,7 @@ _config/* _output/ _output/* *_output/* -output/ \ No newline at end of file +output/ + +**/.vscode/ +*.vscode* \ No newline at end of file diff --git a/scripts/.hypothesis/examples/0ce681936ddabb94/7398dfcbefca0a95 b/scripts/.hypothesis/examples/0ce681936ddabb94/7398dfcbefca0a95 new file mode 100644 index 00000000..96fd13ad Binary files /dev/null and b/scripts/.hypothesis/examples/0ce681936ddabb94/7398dfcbefca0a95 differ diff --git a/scripts/.hypothesis/examples/0ce681936ddabb94/8f2f7530d70d1af2 b/scripts/.hypothesis/examples/0ce681936ddabb94/8f2f7530d70d1af2 new file mode 100644 index 00000000..83629a29 Binary files /dev/null and b/scripts/.hypothesis/examples/0ce681936ddabb94/8f2f7530d70d1af2 differ diff --git a/scripts/.hypothesis/examples/0ce681936ddabb94/ac6f37185ea662aa b/scripts/.hypothesis/examples/0ce681936ddabb94/ac6f37185ea662aa new file mode 100644 index 00000000..418bd539 Binary files /dev/null and b/scripts/.hypothesis/examples/0ce681936ddabb94/ac6f37185ea662aa differ diff --git a/scripts/.hypothesis/examples/0ce681936ddabb94/b3fbceeac7edc940 b/scripts/.hypothesis/examples/0ce681936ddabb94/b3fbceeac7edc940 new file mode 100644 index 00000000..19eb6cab Binary files /dev/null and b/scripts/.hypothesis/examples/0ce681936ddabb94/b3fbceeac7edc940 differ diff --git a/scripts/.hypothesis/examples/0ce681936ddabb94/d038791dc8d55304 b/scripts/.hypothesis/examples/0ce681936ddabb94/d038791dc8d55304 new file mode 100644 index 00000000..4a3c5b1b Binary files /dev/null and b/scripts/.hypothesis/examples/0ce681936ddabb94/d038791dc8d55304 differ diff --git a/scripts/.hypothesis/examples/0e55623b35aed0bd/38b060a751ac9638 b/scripts/.hypothesis/examples/0e55623b35aed0bd/38b060a751ac9638 new file mode 100644 index 00000000..e69de29b diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/01aba940d630ab75 b/scripts/.hypothesis/examples/1b1484081ed3fd25/01aba940d630ab75 new file mode 100644 index 00000000..757ad763 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/01aba940d630ab75 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/0890d05451f4164c b/scripts/.hypothesis/examples/1b1484081ed3fd25/0890d05451f4164c new file mode 100644 index 00000000..7ae300da Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/0890d05451f4164c differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/134770466275ea58 b/scripts/.hypothesis/examples/1b1484081ed3fd25/134770466275ea58 new file mode 100644 index 00000000..7a7280e1 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/134770466275ea58 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/1511dcc1d964032c b/scripts/.hypothesis/examples/1b1484081ed3fd25/1511dcc1d964032c new file mode 100644 index 00000000..7aa31c1c Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/1511dcc1d964032c differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/19aa13ac8d7428e9 b/scripts/.hypothesis/examples/1b1484081ed3fd25/19aa13ac8d7428e9 new file mode 100644 index 00000000..493c6f03 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/19aa13ac8d7428e9 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/1f82eaae236e5fe4 b/scripts/.hypothesis/examples/1b1484081ed3fd25/1f82eaae236e5fe4 new file mode 100644 index 00000000..5d19e24e Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/1f82eaae236e5fe4 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/20c05b2dbed35c25 b/scripts/.hypothesis/examples/1b1484081ed3fd25/20c05b2dbed35c25 new file mode 100644 index 00000000..9213da4c Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/20c05b2dbed35c25 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/32447b50dc7f43c4 b/scripts/.hypothesis/examples/1b1484081ed3fd25/32447b50dc7f43c4 new file mode 100644 index 00000000..2fb1e028 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/32447b50dc7f43c4 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/41ecafa23e5a93f3 b/scripts/.hypothesis/examples/1b1484081ed3fd25/41ecafa23e5a93f3 new file mode 100644 index 00000000..3d9dbfa3 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/41ecafa23e5a93f3 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/430b057bc1b3b61d b/scripts/.hypothesis/examples/1b1484081ed3fd25/430b057bc1b3b61d new file mode 100644 index 00000000..fc3889f7 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/430b057bc1b3b61d differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/589ccc28f750849e b/scripts/.hypothesis/examples/1b1484081ed3fd25/589ccc28f750849e new file mode 100644 index 00000000..9c3b6827 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/589ccc28f750849e differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/5ca364d046e168aa b/scripts/.hypothesis/examples/1b1484081ed3fd25/5ca364d046e168aa new file mode 100644 index 00000000..cd95bfe0 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/5ca364d046e168aa differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/6b2c9168d6a2ad16 b/scripts/.hypothesis/examples/1b1484081ed3fd25/6b2c9168d6a2ad16 new file mode 100644 index 00000000..79c06158 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/6b2c9168d6a2ad16 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/6bf47176b6c4a57f b/scripts/.hypothesis/examples/1b1484081ed3fd25/6bf47176b6c4a57f new file mode 100644 index 00000000..14ecdd76 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/6bf47176b6c4a57f differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/6f2e79924fa0146d b/scripts/.hypothesis/examples/1b1484081ed3fd25/6f2e79924fa0146d new file mode 100644 index 00000000..b86e113f Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/6f2e79924fa0146d differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/71b277a5410e9db9 b/scripts/.hypothesis/examples/1b1484081ed3fd25/71b277a5410e9db9 new file mode 100644 index 00000000..e33b1620 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/71b277a5410e9db9 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/83012edca4c9b642 b/scripts/.hypothesis/examples/1b1484081ed3fd25/83012edca4c9b642 new file mode 100644 index 00000000..8f43bfcc Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/83012edca4c9b642 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/833a81447c0132d8 b/scripts/.hypothesis/examples/1b1484081ed3fd25/833a81447c0132d8 new file mode 100644 index 00000000..b623b585 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/833a81447c0132d8 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/966efb8e8f125fa7 b/scripts/.hypothesis/examples/1b1484081ed3fd25/966efb8e8f125fa7 new file mode 100644 index 00000000..6d6d9417 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/966efb8e8f125fa7 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/9bb4524b97454320 b/scripts/.hypothesis/examples/1b1484081ed3fd25/9bb4524b97454320 new file mode 100644 index 00000000..cef44974 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/9bb4524b97454320 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/9cfbc2eace8f4fc9 b/scripts/.hypothesis/examples/1b1484081ed3fd25/9cfbc2eace8f4fc9 new file mode 100644 index 00000000..22e252f1 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/9cfbc2eace8f4fc9 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/a2330994dc316cc7 b/scripts/.hypothesis/examples/1b1484081ed3fd25/a2330994dc316cc7 new file mode 100644 index 00000000..ca00deb3 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/a2330994dc316cc7 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/af202c14b4e434c9 b/scripts/.hypothesis/examples/1b1484081ed3fd25/af202c14b4e434c9 new file mode 100644 index 00000000..8feb3f7d Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/af202c14b4e434c9 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/ba46ab62ca3cd453 b/scripts/.hypothesis/examples/1b1484081ed3fd25/ba46ab62ca3cd453 new file mode 100644 index 00000000..a5a4f3fe Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/ba46ab62ca3cd453 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/beb22e8fda6d3624 b/scripts/.hypothesis/examples/1b1484081ed3fd25/beb22e8fda6d3624 new file mode 100644 index 00000000..59bf82fa Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/beb22e8fda6d3624 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/bfbaef26436147c7 b/scripts/.hypothesis/examples/1b1484081ed3fd25/bfbaef26436147c7 new file mode 100644 index 00000000..510411f2 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/bfbaef26436147c7 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/c1cc056370ebfcb9 b/scripts/.hypothesis/examples/1b1484081ed3fd25/c1cc056370ebfcb9 new file mode 100644 index 00000000..62bba953 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/c1cc056370ebfcb9 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/c3f6ed0ecc258a1d b/scripts/.hypothesis/examples/1b1484081ed3fd25/c3f6ed0ecc258a1d new file mode 100644 index 00000000..8de9b889 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/c3f6ed0ecc258a1d differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/c810786f61f8ee62 b/scripts/.hypothesis/examples/1b1484081ed3fd25/c810786f61f8ee62 new file mode 100644 index 00000000..cc84cd37 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/c810786f61f8ee62 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/cbbe0110b60eab1e b/scripts/.hypothesis/examples/1b1484081ed3fd25/cbbe0110b60eab1e new file mode 100644 index 00000000..6544d0a7 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/cbbe0110b60eab1e differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/d0fe54bd0d08e1b1 b/scripts/.hypothesis/examples/1b1484081ed3fd25/d0fe54bd0d08e1b1 new file mode 100644 index 00000000..bc10a154 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/d0fe54bd0d08e1b1 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/ddae71054bfb727a b/scripts/.hypothesis/examples/1b1484081ed3fd25/ddae71054bfb727a new file mode 100644 index 00000000..c0a5e95b Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/ddae71054bfb727a differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/df8a018270849f17 b/scripts/.hypothesis/examples/1b1484081ed3fd25/df8a018270849f17 new file mode 100644 index 00000000..fa9ea2eb Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/df8a018270849f17 differ diff --git a/scripts/.hypothesis/examples/1b1484081ed3fd25/e8bb8c507b565eef b/scripts/.hypothesis/examples/1b1484081ed3fd25/e8bb8c507b565eef new file mode 100644 index 00000000..8cd5e1d3 Binary files /dev/null and b/scripts/.hypothesis/examples/1b1484081ed3fd25/e8bb8c507b565eef differ diff --git a/scripts/.hypothesis/examples/1cf837658f59bc59/23a5c6304f36e515 b/scripts/.hypothesis/examples/1cf837658f59bc59/23a5c6304f36e515 new file mode 100644 index 00000000..d3b180a4 Binary files /dev/null and b/scripts/.hypothesis/examples/1cf837658f59bc59/23a5c6304f36e515 differ diff --git a/scripts/.hypothesis/examples/1cf837658f59bc59/743c063de21f6906 b/scripts/.hypothesis/examples/1cf837658f59bc59/743c063de21f6906 new file mode 100644 index 00000000..09c846c3 Binary files /dev/null and b/scripts/.hypothesis/examples/1cf837658f59bc59/743c063de21f6906 differ diff --git a/scripts/.hypothesis/examples/1cf837658f59bc59/891be2b13cf99268 b/scripts/.hypothesis/examples/1cf837658f59bc59/891be2b13cf99268 new file mode 100644 index 00000000..ec99ca5b Binary files /dev/null and b/scripts/.hypothesis/examples/1cf837658f59bc59/891be2b13cf99268 differ diff --git a/scripts/.hypothesis/examples/1cf837658f59bc59/8efb8b90bc6d5d1b b/scripts/.hypothesis/examples/1cf837658f59bc59/8efb8b90bc6d5d1b new file mode 100644 index 00000000..3debc3aa Binary files /dev/null and b/scripts/.hypothesis/examples/1cf837658f59bc59/8efb8b90bc6d5d1b differ diff --git a/scripts/.hypothesis/examples/1cf837658f59bc59/98b97ef23a59af65 b/scripts/.hypothesis/examples/1cf837658f59bc59/98b97ef23a59af65 new file mode 100644 index 00000000..4a473781 Binary files /dev/null and b/scripts/.hypothesis/examples/1cf837658f59bc59/98b97ef23a59af65 differ diff --git a/scripts/.hypothesis/examples/1cf837658f59bc59/fb7b2469c0f63dd2 b/scripts/.hypothesis/examples/1cf837658f59bc59/fb7b2469c0f63dd2 new file mode 100644 index 00000000..b53b6a39 Binary files /dev/null and b/scripts/.hypothesis/examples/1cf837658f59bc59/fb7b2469c0f63dd2 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/00ca6898984673e8 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/00ca6898984673e8 new file mode 100644 index 00000000..3eebd638 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/00ca6898984673e8 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/01cfdd03c93a4bc0 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/01cfdd03c93a4bc0 new file mode 100644 index 00000000..635193a2 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/01cfdd03c93a4bc0 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/0f570acf7a2d9185 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/0f570acf7a2d9185 new file mode 100644 index 00000000..bd060c06 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/0f570acf7a2d9185 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/1dfcb9cb2a1bed4f b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/1dfcb9cb2a1bed4f new file mode 100644 index 00000000..e03a9f8c Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/1dfcb9cb2a1bed4f differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/2af0830ff7cd2496 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/2af0830ff7cd2496 new file mode 100644 index 00000000..70e31ba1 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/2af0830ff7cd2496 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/347a01bcef2cf694 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/347a01bcef2cf694 new file mode 100644 index 00000000..ba917306 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/347a01bcef2cf694 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/3669298f38513558 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/3669298f38513558 new file mode 100644 index 00000000..6239096f Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/3669298f38513558 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/3a02ef7ebcab2b88 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/3a02ef7ebcab2b88 new file mode 100644 index 00000000..b26ba48c Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/3a02ef7ebcab2b88 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/430b057bc1b3b61d b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/430b057bc1b3b61d new file mode 100644 index 00000000..fc3889f7 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/430b057bc1b3b61d differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/4abbbc53950daa66 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/4abbbc53950daa66 new file mode 100644 index 00000000..94697724 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/4abbbc53950daa66 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/51f2607972a60cb0 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/51f2607972a60cb0 new file mode 100644 index 00000000..ab744c50 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/51f2607972a60cb0 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/6bf47176b6c4a57f b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/6bf47176b6c4a57f new file mode 100644 index 00000000..14ecdd76 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/6bf47176b6c4a57f differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/83012edca4c9b642 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/83012edca4c9b642 new file mode 100644 index 00000000..8f43bfcc Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/83012edca4c9b642 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/8d7ff9bb3c085289 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/8d7ff9bb3c085289 new file mode 100644 index 00000000..0068e114 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/8d7ff9bb3c085289 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/8dfa16e61c04bf72 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/8dfa16e61c04bf72 new file mode 100644 index 00000000..78f7258c Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/8dfa16e61c04bf72 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/96f7e9c7d5290323 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/96f7e9c7d5290323 new file mode 100644 index 00000000..32829b48 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/96f7e9c7d5290323 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/9ab01f4c4cac1750 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/9ab01f4c4cac1750 new file mode 100644 index 00000000..daa0c28a Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/9ab01f4c4cac1750 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/9bb4524b97454320 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/9bb4524b97454320 new file mode 100644 index 00000000..cef44974 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/9bb4524b97454320 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/9c385d2553706367 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/9c385d2553706367 new file mode 100644 index 00000000..73c845e9 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/9c385d2553706367 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/a2330994dc316cc7 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/a2330994dc316cc7 new file mode 100644 index 00000000..ca00deb3 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/a2330994dc316cc7 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/a6a1177199a6df6b b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/a6a1177199a6df6b new file mode 100644 index 00000000..fb6223cb Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/a6a1177199a6df6b differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/aad353552de021f6 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/aad353552de021f6 new file mode 100644 index 00000000..27f83a7c Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/aad353552de021f6 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/b004d68338284599 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/b004d68338284599 new file mode 100644 index 00000000..83cb88c5 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/b004d68338284599 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/b6345318ea8292a0 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/b6345318ea8292a0 new file mode 100644 index 00000000..4e68b8b9 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/b6345318ea8292a0 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/d0fe54bd0d08e1b1 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/d0fe54bd0d08e1b1 new file mode 100644 index 00000000..bc10a154 Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/d0fe54bd0d08e1b1 differ diff --git a/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/dce91dce749f6c92 b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/dce91dce749f6c92 new file mode 100644 index 00000000..eceaa99a Binary files /dev/null and b/scripts/.hypothesis/examples/24e9e4a6a41ae1d8/dce91dce749f6c92 differ diff --git a/scripts/.hypothesis/examples/24f3a58f3bfd9f31/38b060a751ac9638 b/scripts/.hypothesis/examples/24f3a58f3bfd9f31/38b060a751ac9638 new file mode 100644 index 00000000..e69de29b diff --git a/scripts/.hypothesis/examples/3638c18229c6a7aa/38b060a751ac9638 b/scripts/.hypothesis/examples/3638c18229c6a7aa/38b060a751ac9638 new file mode 100644 index 00000000..e69de29b diff --git a/scripts/.hypothesis/examples/3c59094c0ffd5073/526ed20c15d7d31f b/scripts/.hypothesis/examples/3c59094c0ffd5073/526ed20c15d7d31f new file mode 100644 index 00000000..0d2d6d3a Binary files /dev/null and b/scripts/.hypothesis/examples/3c59094c0ffd5073/526ed20c15d7d31f differ diff --git a/scripts/.hypothesis/examples/4cf0e7054cf98cae/38b060a751ac9638 b/scripts/.hypothesis/examples/4cf0e7054cf98cae/38b060a751ac9638 new file mode 100644 index 00000000..e69de29b diff --git a/scripts/.hypothesis/examples/60dc7c268a9a7d12/38b060a751ac9638 b/scripts/.hypothesis/examples/60dc7c268a9a7d12/38b060a751ac9638 new file mode 100644 index 00000000..e69de29b diff --git a/scripts/.hypothesis/examples/692c48c9c3c189f9/38b060a751ac9638 b/scripts/.hypothesis/examples/692c48c9c3c189f9/38b060a751ac9638 new file mode 100644 index 00000000..e69de29b diff --git a/scripts/.hypothesis/examples/711ef8dd1d4b67af/38b060a751ac9638 b/scripts/.hypothesis/examples/711ef8dd1d4b67af/38b060a751ac9638 new file mode 100644 index 00000000..e69de29b diff --git a/scripts/.hypothesis/examples/7cf50e557e3b6afd/38b060a751ac9638 b/scripts/.hypothesis/examples/7cf50e557e3b6afd/38b060a751ac9638 new file mode 100644 index 00000000..e69de29b diff --git a/scripts/.hypothesis/examples/7f6330c4ea890236/645f9a2740327990 b/scripts/.hypothesis/examples/7f6330c4ea890236/645f9a2740327990 new file mode 100644 index 00000000..53cd5342 Binary files /dev/null and b/scripts/.hypothesis/examples/7f6330c4ea890236/645f9a2740327990 differ diff --git a/scripts/.hypothesis/examples/7f6330c4ea890236/e87cdf9cf213a382 b/scripts/.hypothesis/examples/7f6330c4ea890236/e87cdf9cf213a382 new file mode 100644 index 00000000..d7e99f0f Binary files /dev/null and b/scripts/.hypothesis/examples/7f6330c4ea890236/e87cdf9cf213a382 differ diff --git a/scripts/.hypothesis/examples/80ca7cda50842c96/38b060a751ac9638 b/scripts/.hypothesis/examples/80ca7cda50842c96/38b060a751ac9638 new file mode 100644 index 00000000..e69de29b diff --git a/scripts/.hypothesis/examples/8da4e8903b5c24a5/38b060a751ac9638 b/scripts/.hypothesis/examples/8da4e8903b5c24a5/38b060a751ac9638 new file mode 100644 index 00000000..e69de29b diff --git a/scripts/.hypothesis/examples/972da276db8e758a/029afea5286208fd b/scripts/.hypothesis/examples/972da276db8e758a/029afea5286208fd new file mode 100644 index 00000000..ebf48101 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/029afea5286208fd differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/02e3843ffeffe8de b/scripts/.hypothesis/examples/972da276db8e758a/02e3843ffeffe8de new file mode 100644 index 00000000..2e7c99e4 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/02e3843ffeffe8de differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/05754999ddad7b3e b/scripts/.hypothesis/examples/972da276db8e758a/05754999ddad7b3e new file mode 100644 index 00000000..011e2dc0 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/05754999ddad7b3e differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/068435dbfebb1103 b/scripts/.hypothesis/examples/972da276db8e758a/068435dbfebb1103 new file mode 100644 index 00000000..526f76ac Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/068435dbfebb1103 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/07bf2341d68a915f b/scripts/.hypothesis/examples/972da276db8e758a/07bf2341d68a915f new file mode 100644 index 00000000..1baab077 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/07bf2341d68a915f differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/083574e6d9a050e8 b/scripts/.hypothesis/examples/972da276db8e758a/083574e6d9a050e8 new file mode 100644 index 00000000..6e7e577b Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/083574e6d9a050e8 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/0876196d8c1cc719 b/scripts/.hypothesis/examples/972da276db8e758a/0876196d8c1cc719 new file mode 100644 index 00000000..5d120ea8 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/0876196d8c1cc719 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/08ca86db03730942 b/scripts/.hypothesis/examples/972da276db8e758a/08ca86db03730942 new file mode 100644 index 00000000..c39446c8 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/08ca86db03730942 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/08d1b332d42b337a b/scripts/.hypothesis/examples/972da276db8e758a/08d1b332d42b337a new file mode 100644 index 00000000..340456e3 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/08d1b332d42b337a differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/0b467a037be792e7 b/scripts/.hypothesis/examples/972da276db8e758a/0b467a037be792e7 new file mode 100644 index 00000000..35475d69 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/0b467a037be792e7 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/0b7ef15d6cd32065 b/scripts/.hypothesis/examples/972da276db8e758a/0b7ef15d6cd32065 new file mode 100644 index 00000000..e2336c6e Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/0b7ef15d6cd32065 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/0c7012a3a39f3a8d b/scripts/.hypothesis/examples/972da276db8e758a/0c7012a3a39f3a8d new file mode 100644 index 00000000..9c16bfe4 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/0c7012a3a39f3a8d differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/0ccdeff809c50925 b/scripts/.hypothesis/examples/972da276db8e758a/0ccdeff809c50925 new file mode 100644 index 00000000..9a6be144 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/0ccdeff809c50925 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/0e191b776e2c3699 b/scripts/.hypothesis/examples/972da276db8e758a/0e191b776e2c3699 new file mode 100644 index 00000000..54680921 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/0e191b776e2c3699 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/0fa0e7e44844d62f b/scripts/.hypothesis/examples/972da276db8e758a/0fa0e7e44844d62f new file mode 100644 index 00000000..b7e82016 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/0fa0e7e44844d62f differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/0fec29ba04f4a391 b/scripts/.hypothesis/examples/972da276db8e758a/0fec29ba04f4a391 new file mode 100644 index 00000000..4f197108 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/0fec29ba04f4a391 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/1777dd7eb72c0eb1 b/scripts/.hypothesis/examples/972da276db8e758a/1777dd7eb72c0eb1 new file mode 100644 index 00000000..8f9102ec Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/1777dd7eb72c0eb1 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/17f113df5837a90a b/scripts/.hypothesis/examples/972da276db8e758a/17f113df5837a90a new file mode 100644 index 00000000..18567e54 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/17f113df5837a90a differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/19fb647bb3ccd464 b/scripts/.hypothesis/examples/972da276db8e758a/19fb647bb3ccd464 new file mode 100644 index 00000000..d74526b8 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/19fb647bb3ccd464 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/1a0c039784e55e57 b/scripts/.hypothesis/examples/972da276db8e758a/1a0c039784e55e57 new file mode 100644 index 00000000..e51adf90 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/1a0c039784e55e57 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/1b1d129bddb55304 b/scripts/.hypothesis/examples/972da276db8e758a/1b1d129bddb55304 new file mode 100644 index 00000000..4b4260ed Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/1b1d129bddb55304 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/1c2783b8dc98eea8 b/scripts/.hypothesis/examples/972da276db8e758a/1c2783b8dc98eea8 new file mode 100644 index 00000000..0b762df8 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/1c2783b8dc98eea8 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/1cbef7fc528c56c5 b/scripts/.hypothesis/examples/972da276db8e758a/1cbef7fc528c56c5 new file mode 100644 index 00000000..daf70f4e Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/1cbef7fc528c56c5 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/21b50cb21bde560d b/scripts/.hypothesis/examples/972da276db8e758a/21b50cb21bde560d new file mode 100644 index 00000000..be7f8899 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/21b50cb21bde560d differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/227c4cf1de61ec07 b/scripts/.hypothesis/examples/972da276db8e758a/227c4cf1de61ec07 new file mode 100644 index 00000000..5fec939b Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/227c4cf1de61ec07 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/242a68d4fec6b0a7 b/scripts/.hypothesis/examples/972da276db8e758a/242a68d4fec6b0a7 new file mode 100644 index 00000000..10f173aa Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/242a68d4fec6b0a7 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/257e17739921f7d4 b/scripts/.hypothesis/examples/972da276db8e758a/257e17739921f7d4 new file mode 100644 index 00000000..61fadce7 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/257e17739921f7d4 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/25c6c63a4fadf8a5 b/scripts/.hypothesis/examples/972da276db8e758a/25c6c63a4fadf8a5 new file mode 100644 index 00000000..d3c368c1 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/25c6c63a4fadf8a5 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/260565954b6824bc b/scripts/.hypothesis/examples/972da276db8e758a/260565954b6824bc new file mode 100644 index 00000000..5b6c6646 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/260565954b6824bc differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/26ae9cfe2e6f3c03 b/scripts/.hypothesis/examples/972da276db8e758a/26ae9cfe2e6f3c03 new file mode 100644 index 00000000..c55df867 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/26ae9cfe2e6f3c03 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/27558a17a64f7ddf b/scripts/.hypothesis/examples/972da276db8e758a/27558a17a64f7ddf new file mode 100644 index 00000000..1703603a Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/27558a17a64f7ddf differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/287fdf02a3c5519d b/scripts/.hypothesis/examples/972da276db8e758a/287fdf02a3c5519d new file mode 100644 index 00000000..916d0841 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/287fdf02a3c5519d differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/2a506e179c21fd67 b/scripts/.hypothesis/examples/972da276db8e758a/2a506e179c21fd67 new file mode 100644 index 00000000..e16ba629 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/2a506e179c21fd67 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/2b466b85438b26c0 b/scripts/.hypothesis/examples/972da276db8e758a/2b466b85438b26c0 new file mode 100644 index 00000000..e33265e7 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/2b466b85438b26c0 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/339d42b139b9dfbd b/scripts/.hypothesis/examples/972da276db8e758a/339d42b139b9dfbd new file mode 100644 index 00000000..20afe12a Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/339d42b139b9dfbd differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/34b1b27282a4adfd b/scripts/.hypothesis/examples/972da276db8e758a/34b1b27282a4adfd new file mode 100644 index 00000000..1b3ae755 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/34b1b27282a4adfd differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/34be48d4729037f4 b/scripts/.hypothesis/examples/972da276db8e758a/34be48d4729037f4 new file mode 100644 index 00000000..23b76ae0 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/34be48d4729037f4 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/3559a9554fa8e9b4 b/scripts/.hypothesis/examples/972da276db8e758a/3559a9554fa8e9b4 new file mode 100644 index 00000000..20e82490 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/3559a9554fa8e9b4 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/357afa6b048385e4 b/scripts/.hypothesis/examples/972da276db8e758a/357afa6b048385e4 new file mode 100644 index 00000000..7a4633fe Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/357afa6b048385e4 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/359a31c9a57f03e1 b/scripts/.hypothesis/examples/972da276db8e758a/359a31c9a57f03e1 new file mode 100644 index 00000000..9b9b9adc Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/359a31c9a57f03e1 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/37a64b7d9f57cfa5 b/scripts/.hypothesis/examples/972da276db8e758a/37a64b7d9f57cfa5 new file mode 100644 index 00000000..6609382b Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/37a64b7d9f57cfa5 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/389ff3fc8b29f9cb b/scripts/.hypothesis/examples/972da276db8e758a/389ff3fc8b29f9cb new file mode 100644 index 00000000..c9f36d59 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/389ff3fc8b29f9cb differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/392767782dc00f6b b/scripts/.hypothesis/examples/972da276db8e758a/392767782dc00f6b new file mode 100644 index 00000000..edd80980 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/392767782dc00f6b differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/3a4a41f7a5fcaed9 b/scripts/.hypothesis/examples/972da276db8e758a/3a4a41f7a5fcaed9 new file mode 100644 index 00000000..8077976e Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/3a4a41f7a5fcaed9 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/3bd6a482df088d91 b/scripts/.hypothesis/examples/972da276db8e758a/3bd6a482df088d91 new file mode 100644 index 00000000..6ecb1e71 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/3bd6a482df088d91 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/3dade6ff2a86fdd1 b/scripts/.hypothesis/examples/972da276db8e758a/3dade6ff2a86fdd1 new file mode 100644 index 00000000..39b14c7f Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/3dade6ff2a86fdd1 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/3f95558bfd1a4200 b/scripts/.hypothesis/examples/972da276db8e758a/3f95558bfd1a4200 new file mode 100644 index 00000000..e238a4e6 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/3f95558bfd1a4200 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/3fea931248d3a3f1 b/scripts/.hypothesis/examples/972da276db8e758a/3fea931248d3a3f1 new file mode 100644 index 00000000..3aeecf5b Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/3fea931248d3a3f1 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/44a5f3010f1f4dd0 b/scripts/.hypothesis/examples/972da276db8e758a/44a5f3010f1f4dd0 new file mode 100644 index 00000000..23f1fa61 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/44a5f3010f1f4dd0 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/45e13567599e8a88 b/scripts/.hypothesis/examples/972da276db8e758a/45e13567599e8a88 new file mode 100644 index 00000000..dc38b79c Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/45e13567599e8a88 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/4680e69661a2ed6c b/scripts/.hypothesis/examples/972da276db8e758a/4680e69661a2ed6c new file mode 100644 index 00000000..dacdda57 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/4680e69661a2ed6c differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/473055d659fe0ab3 b/scripts/.hypothesis/examples/972da276db8e758a/473055d659fe0ab3 new file mode 100644 index 00000000..26f5f3db Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/473055d659fe0ab3 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/474f46dd4323d3c4 b/scripts/.hypothesis/examples/972da276db8e758a/474f46dd4323d3c4 new file mode 100644 index 00000000..5f0ae156 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/474f46dd4323d3c4 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/475f9843b3de8544 b/scripts/.hypothesis/examples/972da276db8e758a/475f9843b3de8544 new file mode 100644 index 00000000..d6ca9b4c Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/475f9843b3de8544 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/47a605b9c09873ee b/scripts/.hypothesis/examples/972da276db8e758a/47a605b9c09873ee new file mode 100644 index 00000000..e860cc72 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/47a605b9c09873ee differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/4c269d80e5f3ea03 b/scripts/.hypothesis/examples/972da276db8e758a/4c269d80e5f3ea03 new file mode 100644 index 00000000..422e162a Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/4c269d80e5f3ea03 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/4cb886acce73eb83 b/scripts/.hypothesis/examples/972da276db8e758a/4cb886acce73eb83 new file mode 100644 index 00000000..158b05d1 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/4cb886acce73eb83 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/4dbbed8d782bd9e9 b/scripts/.hypothesis/examples/972da276db8e758a/4dbbed8d782bd9e9 new file mode 100644 index 00000000..1c0bf87c Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/4dbbed8d782bd9e9 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/4dec1d6eda5d2975 b/scripts/.hypothesis/examples/972da276db8e758a/4dec1d6eda5d2975 new file mode 100644 index 00000000..af8456c9 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/4dec1d6eda5d2975 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/50c07dd5285c028d b/scripts/.hypothesis/examples/972da276db8e758a/50c07dd5285c028d new file mode 100644 index 00000000..d72c3c87 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/50c07dd5285c028d differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/51e175f59290b93b b/scripts/.hypothesis/examples/972da276db8e758a/51e175f59290b93b new file mode 100644 index 00000000..066dfed8 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/51e175f59290b93b differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/527d00d01bb9fdd2 b/scripts/.hypothesis/examples/972da276db8e758a/527d00d01bb9fdd2 new file mode 100644 index 00000000..91fcbc9b Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/527d00d01bb9fdd2 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/52e8be375458549f b/scripts/.hypothesis/examples/972da276db8e758a/52e8be375458549f new file mode 100644 index 00000000..3ab17aa6 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/52e8be375458549f differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/54f3dc6e211b5185 b/scripts/.hypothesis/examples/972da276db8e758a/54f3dc6e211b5185 new file mode 100644 index 00000000..2d9a1dd5 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/54f3dc6e211b5185 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/5548191b52bf0151 b/scripts/.hypothesis/examples/972da276db8e758a/5548191b52bf0151 new file mode 100644 index 00000000..bb2ce1e3 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/5548191b52bf0151 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/55dfa55b86838da2 b/scripts/.hypothesis/examples/972da276db8e758a/55dfa55b86838da2 new file mode 100644 index 00000000..259b7b45 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/55dfa55b86838da2 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/56735cfb13bd9669 b/scripts/.hypothesis/examples/972da276db8e758a/56735cfb13bd9669 new file mode 100644 index 00000000..1fc6ad8a Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/56735cfb13bd9669 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/56df4e2b71c2fe39 b/scripts/.hypothesis/examples/972da276db8e758a/56df4e2b71c2fe39 new file mode 100644 index 00000000..4993cfc6 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/56df4e2b71c2fe39 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/57826d2867bdd132 b/scripts/.hypothesis/examples/972da276db8e758a/57826d2867bdd132 new file mode 100644 index 00000000..5127a551 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/57826d2867bdd132 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/580e0f1e926d6ae7 b/scripts/.hypothesis/examples/972da276db8e758a/580e0f1e926d6ae7 new file mode 100644 index 00000000..13ff4a5c Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/580e0f1e926d6ae7 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/586417c6f7a4c41d b/scripts/.hypothesis/examples/972da276db8e758a/586417c6f7a4c41d new file mode 100644 index 00000000..5ccb77a7 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/586417c6f7a4c41d differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/5aac9ed9f1e2ef5e b/scripts/.hypothesis/examples/972da276db8e758a/5aac9ed9f1e2ef5e new file mode 100644 index 00000000..085d7467 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/5aac9ed9f1e2ef5e differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/5d2a46b1404125af b/scripts/.hypothesis/examples/972da276db8e758a/5d2a46b1404125af new file mode 100644 index 00000000..d3af0d9b Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/5d2a46b1404125af differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/5f0f6bf95410410b b/scripts/.hypothesis/examples/972da276db8e758a/5f0f6bf95410410b new file mode 100644 index 00000000..a6628213 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/5f0f6bf95410410b differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/5f66066ed6e612f2 b/scripts/.hypothesis/examples/972da276db8e758a/5f66066ed6e612f2 new file mode 100644 index 00000000..b0cbeea1 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/5f66066ed6e612f2 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/61cd7c4ec836fe3e b/scripts/.hypothesis/examples/972da276db8e758a/61cd7c4ec836fe3e new file mode 100644 index 00000000..6a5e140c Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/61cd7c4ec836fe3e differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/6361d9e05211444b b/scripts/.hypothesis/examples/972da276db8e758a/6361d9e05211444b new file mode 100644 index 00000000..b5c3dd6f Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/6361d9e05211444b differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/638731351dc3bfa8 b/scripts/.hypothesis/examples/972da276db8e758a/638731351dc3bfa8 new file mode 100644 index 00000000..4c7ae35e Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/638731351dc3bfa8 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/649c8a4e574bc328 b/scripts/.hypothesis/examples/972da276db8e758a/649c8a4e574bc328 new file mode 100644 index 00000000..3c2a5be2 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/649c8a4e574bc328 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/653a75e9986a553a b/scripts/.hypothesis/examples/972da276db8e758a/653a75e9986a553a new file mode 100644 index 00000000..0bc4477a Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/653a75e9986a553a differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/6752d4feae057d5f b/scripts/.hypothesis/examples/972da276db8e758a/6752d4feae057d5f new file mode 100644 index 00000000..1c787c5d Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/6752d4feae057d5f differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/67f64afe878a5e65 b/scripts/.hypothesis/examples/972da276db8e758a/67f64afe878a5e65 new file mode 100644 index 00000000..a2cf5377 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/67f64afe878a5e65 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/68b0244d212daf6c b/scripts/.hypothesis/examples/972da276db8e758a/68b0244d212daf6c new file mode 100644 index 00000000..6ddb1d7b Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/68b0244d212daf6c differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/691a4136d6f1b4cc b/scripts/.hypothesis/examples/972da276db8e758a/691a4136d6f1b4cc new file mode 100644 index 00000000..fbb2c7d5 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/691a4136d6f1b4cc differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/6c3f4a9b13e2edcf b/scripts/.hypothesis/examples/972da276db8e758a/6c3f4a9b13e2edcf new file mode 100644 index 00000000..9833b9b1 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/6c3f4a9b13e2edcf differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/6ca3e77a78909ea0 b/scripts/.hypothesis/examples/972da276db8e758a/6ca3e77a78909ea0 new file mode 100644 index 00000000..de64d120 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/6ca3e77a78909ea0 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/6ccc0acd42b04171 b/scripts/.hypothesis/examples/972da276db8e758a/6ccc0acd42b04171 new file mode 100644 index 00000000..2be5fb84 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/6ccc0acd42b04171 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/6e68172d082b368e b/scripts/.hypothesis/examples/972da276db8e758a/6e68172d082b368e new file mode 100644 index 00000000..140c0af0 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/6e68172d082b368e differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/6f291029da3d1de1 b/scripts/.hypothesis/examples/972da276db8e758a/6f291029da3d1de1 new file mode 100644 index 00000000..8c0b2ac5 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/6f291029da3d1de1 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/6f7dff6973cd0c13 b/scripts/.hypothesis/examples/972da276db8e758a/6f7dff6973cd0c13 new file mode 100644 index 00000000..9efb8ad9 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/6f7dff6973cd0c13 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/71d769de8a69af86 b/scripts/.hypothesis/examples/972da276db8e758a/71d769de8a69af86 new file mode 100644 index 00000000..93a40a04 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/71d769de8a69af86 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/72f31cd1b19f26ba b/scripts/.hypothesis/examples/972da276db8e758a/72f31cd1b19f26ba new file mode 100644 index 00000000..aadad164 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/72f31cd1b19f26ba differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/747fd61f307d1663 b/scripts/.hypothesis/examples/972da276db8e758a/747fd61f307d1663 new file mode 100644 index 00000000..5b10573c Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/747fd61f307d1663 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/74d760c976f9b70a b/scripts/.hypothesis/examples/972da276db8e758a/74d760c976f9b70a new file mode 100644 index 00000000..57504743 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/74d760c976f9b70a differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/76bcb06488e394cf b/scripts/.hypothesis/examples/972da276db8e758a/76bcb06488e394cf new file mode 100644 index 00000000..7a94b90a Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/76bcb06488e394cf differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/7794f93f7d176f9a b/scripts/.hypothesis/examples/972da276db8e758a/7794f93f7d176f9a new file mode 100644 index 00000000..0376599e Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/7794f93f7d176f9a differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/78a42c84bbadb163 b/scripts/.hypothesis/examples/972da276db8e758a/78a42c84bbadb163 new file mode 100644 index 00000000..f01f81cb Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/78a42c84bbadb163 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/7971c1191d124bb2 b/scripts/.hypothesis/examples/972da276db8e758a/7971c1191d124bb2 new file mode 100644 index 00000000..e0235218 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/7971c1191d124bb2 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/7a19079a50f5f798 b/scripts/.hypothesis/examples/972da276db8e758a/7a19079a50f5f798 new file mode 100644 index 00000000..4cdf4473 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/7a19079a50f5f798 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/7a8db2b908977b22 b/scripts/.hypothesis/examples/972da276db8e758a/7a8db2b908977b22 new file mode 100644 index 00000000..c8d8682d Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/7a8db2b908977b22 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/7c0b1af19bbbe1db b/scripts/.hypothesis/examples/972da276db8e758a/7c0b1af19bbbe1db new file mode 100644 index 00000000..86855928 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/7c0b1af19bbbe1db differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/7c1acad700f1c89f b/scripts/.hypothesis/examples/972da276db8e758a/7c1acad700f1c89f new file mode 100644 index 00000000..aa40415e Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/7c1acad700f1c89f differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/7c3e15783e058563 b/scripts/.hypothesis/examples/972da276db8e758a/7c3e15783e058563 new file mode 100644 index 00000000..0cb4441a Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/7c3e15783e058563 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/7c5c66ed9fb5b293 b/scripts/.hypothesis/examples/972da276db8e758a/7c5c66ed9fb5b293 new file mode 100644 index 00000000..516efe83 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/7c5c66ed9fb5b293 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/7c99970c2b50d45a b/scripts/.hypothesis/examples/972da276db8e758a/7c99970c2b50d45a new file mode 100644 index 00000000..7946f62e Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/7c99970c2b50d45a differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/7cb7e261445e95bf b/scripts/.hypothesis/examples/972da276db8e758a/7cb7e261445e95bf new file mode 100644 index 00000000..49daaf2d Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/7cb7e261445e95bf differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/7ddcf2f81e1003b1 b/scripts/.hypothesis/examples/972da276db8e758a/7ddcf2f81e1003b1 new file mode 100644 index 00000000..8dc4ba74 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/7ddcf2f81e1003b1 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/7f2932f835f293a8 b/scripts/.hypothesis/examples/972da276db8e758a/7f2932f835f293a8 new file mode 100644 index 00000000..7ff6e717 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/7f2932f835f293a8 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/7f397c9f5279a2fd b/scripts/.hypothesis/examples/972da276db8e758a/7f397c9f5279a2fd new file mode 100644 index 00000000..4e07a265 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/7f397c9f5279a2fd differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/80e2951e61200b3e b/scripts/.hypothesis/examples/972da276db8e758a/80e2951e61200b3e new file mode 100644 index 00000000..9fd324de Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/80e2951e61200b3e differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/8282d34037c89183 b/scripts/.hypothesis/examples/972da276db8e758a/8282d34037c89183 new file mode 100644 index 00000000..7d31b7fc Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/8282d34037c89183 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/82f309871c5f04ec b/scripts/.hypothesis/examples/972da276db8e758a/82f309871c5f04ec new file mode 100644 index 00000000..56aecbe8 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/82f309871c5f04ec differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/833c7d1e442f3a08 b/scripts/.hypothesis/examples/972da276db8e758a/833c7d1e442f3a08 new file mode 100644 index 00000000..81aaf558 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/833c7d1e442f3a08 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/854b5ad550e6bc91 b/scripts/.hypothesis/examples/972da276db8e758a/854b5ad550e6bc91 new file mode 100644 index 00000000..72cf867c Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/854b5ad550e6bc91 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/85aed485b42bc20d b/scripts/.hypothesis/examples/972da276db8e758a/85aed485b42bc20d new file mode 100644 index 00000000..94068d34 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/85aed485b42bc20d differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/869db011b0b446f9 b/scripts/.hypothesis/examples/972da276db8e758a/869db011b0b446f9 new file mode 100644 index 00000000..fc61ecea Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/869db011b0b446f9 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/87acf6dd5c0e6038 b/scripts/.hypothesis/examples/972da276db8e758a/87acf6dd5c0e6038 new file mode 100644 index 00000000..e32c81bb Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/87acf6dd5c0e6038 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/8c91d389355572c1 b/scripts/.hypothesis/examples/972da276db8e758a/8c91d389355572c1 new file mode 100644 index 00000000..1448ad55 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/8c91d389355572c1 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/8d53e7c7f6733ee0 b/scripts/.hypothesis/examples/972da276db8e758a/8d53e7c7f6733ee0 new file mode 100644 index 00000000..b697b037 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/8d53e7c7f6733ee0 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/8f0090ccacb69e75 b/scripts/.hypothesis/examples/972da276db8e758a/8f0090ccacb69e75 new file mode 100644 index 00000000..d6beeba4 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/8f0090ccacb69e75 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/8f1e185860197a10 b/scripts/.hypothesis/examples/972da276db8e758a/8f1e185860197a10 new file mode 100644 index 00000000..4605f08e Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/8f1e185860197a10 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/8f72afddef8217ab b/scripts/.hypothesis/examples/972da276db8e758a/8f72afddef8217ab new file mode 100644 index 00000000..5c746c8e Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/8f72afddef8217ab differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/8fff852a1ce8aa3f b/scripts/.hypothesis/examples/972da276db8e758a/8fff852a1ce8aa3f new file mode 100644 index 00000000..79b67dc6 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/8fff852a1ce8aa3f differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/9302a3a7b9dedfaf b/scripts/.hypothesis/examples/972da276db8e758a/9302a3a7b9dedfaf new file mode 100644 index 00000000..bf0dded7 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/9302a3a7b9dedfaf differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/933ac63db4c19566 b/scripts/.hypothesis/examples/972da276db8e758a/933ac63db4c19566 new file mode 100644 index 00000000..758fad97 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/933ac63db4c19566 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/9665e857aaa4144f b/scripts/.hypothesis/examples/972da276db8e758a/9665e857aaa4144f new file mode 100644 index 00000000..0724d98c Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/9665e857aaa4144f differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/97d0fe57dc4e9a7e b/scripts/.hypothesis/examples/972da276db8e758a/97d0fe57dc4e9a7e new file mode 100644 index 00000000..18a7ccb4 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/97d0fe57dc4e9a7e differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/9a5b6dafea2a0f83 b/scripts/.hypothesis/examples/972da276db8e758a/9a5b6dafea2a0f83 new file mode 100644 index 00000000..2c6deb27 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/9a5b6dafea2a0f83 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/9cb6d7ea1d28b800 b/scripts/.hypothesis/examples/972da276db8e758a/9cb6d7ea1d28b800 new file mode 100644 index 00000000..0ded3f37 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/9cb6d7ea1d28b800 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/9d1523f2ea98267e b/scripts/.hypothesis/examples/972da276db8e758a/9d1523f2ea98267e new file mode 100644 index 00000000..2c22b266 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/9d1523f2ea98267e differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/9d1555c8e1dc0cb4 b/scripts/.hypothesis/examples/972da276db8e758a/9d1555c8e1dc0cb4 new file mode 100644 index 00000000..4b8a9f82 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/9d1555c8e1dc0cb4 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/9dc4570db01cac06 b/scripts/.hypothesis/examples/972da276db8e758a/9dc4570db01cac06 new file mode 100644 index 00000000..91bc011e Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/9dc4570db01cac06 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/9feddd69204e2268 b/scripts/.hypothesis/examples/972da276db8e758a/9feddd69204e2268 new file mode 100644 index 00000000..49b998c9 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/9feddd69204e2268 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/a0178c41b8d476cc b/scripts/.hypothesis/examples/972da276db8e758a/a0178c41b8d476cc new file mode 100644 index 00000000..dbbfe28e Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/a0178c41b8d476cc differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/a070f9c09b351e20 b/scripts/.hypothesis/examples/972da276db8e758a/a070f9c09b351e20 new file mode 100644 index 00000000..9df732c8 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/a070f9c09b351e20 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/a2e57f240b5f4062 b/scripts/.hypothesis/examples/972da276db8e758a/a2e57f240b5f4062 new file mode 100644 index 00000000..3a3e47bc Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/a2e57f240b5f4062 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/a4cc0918fa694d7b b/scripts/.hypothesis/examples/972da276db8e758a/a4cc0918fa694d7b new file mode 100644 index 00000000..ead95237 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/a4cc0918fa694d7b differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/a5b67f40c8e5989b b/scripts/.hypothesis/examples/972da276db8e758a/a5b67f40c8e5989b new file mode 100644 index 00000000..0ead8ff9 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/a5b67f40c8e5989b differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/a9a1f2bd95ad6146 b/scripts/.hypothesis/examples/972da276db8e758a/a9a1f2bd95ad6146 new file mode 100644 index 00000000..8ddfd7cd Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/a9a1f2bd95ad6146 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/aa28c21684eabccc b/scripts/.hypothesis/examples/972da276db8e758a/aa28c21684eabccc new file mode 100644 index 00000000..449b465b Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/aa28c21684eabccc differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/acdce01de37b3bc0 b/scripts/.hypothesis/examples/972da276db8e758a/acdce01de37b3bc0 new file mode 100644 index 00000000..24e0f5d2 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/acdce01de37b3bc0 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/af07445b8ee364a1 b/scripts/.hypothesis/examples/972da276db8e758a/af07445b8ee364a1 new file mode 100644 index 00000000..3543560b Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/af07445b8ee364a1 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/b00b258dbe043006 b/scripts/.hypothesis/examples/972da276db8e758a/b00b258dbe043006 new file mode 100644 index 00000000..fed22c45 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/b00b258dbe043006 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/b1015f8836881e44 b/scripts/.hypothesis/examples/972da276db8e758a/b1015f8836881e44 new file mode 100644 index 00000000..8409b666 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/b1015f8836881e44 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/b1894fc8b294f2fc b/scripts/.hypothesis/examples/972da276db8e758a/b1894fc8b294f2fc new file mode 100644 index 00000000..5985be46 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/b1894fc8b294f2fc differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/b1db0ee037d0d160 b/scripts/.hypothesis/examples/972da276db8e758a/b1db0ee037d0d160 new file mode 100644 index 00000000..bd60cd53 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/b1db0ee037d0d160 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/b25640052426d5bf b/scripts/.hypothesis/examples/972da276db8e758a/b25640052426d5bf new file mode 100644 index 00000000..339509dc Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/b25640052426d5bf differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/b48bb62fb7b6b944 b/scripts/.hypothesis/examples/972da276db8e758a/b48bb62fb7b6b944 new file mode 100644 index 00000000..4558ff6b Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/b48bb62fb7b6b944 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/b4bc0f30d70c584e b/scripts/.hypothesis/examples/972da276db8e758a/b4bc0f30d70c584e new file mode 100644 index 00000000..59627c1e Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/b4bc0f30d70c584e differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/b686d72ec3131402 b/scripts/.hypothesis/examples/972da276db8e758a/b686d72ec3131402 new file mode 100644 index 00000000..c5b09c60 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/b686d72ec3131402 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/b98009729ee2b3ff b/scripts/.hypothesis/examples/972da276db8e758a/b98009729ee2b3ff new file mode 100644 index 00000000..5b338ebb Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/b98009729ee2b3ff differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/ba6401260165c631 b/scripts/.hypothesis/examples/972da276db8e758a/ba6401260165c631 new file mode 100644 index 00000000..624f3e62 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/ba6401260165c631 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/baed6326ebc5d987 b/scripts/.hypothesis/examples/972da276db8e758a/baed6326ebc5d987 new file mode 100644 index 00000000..6c7b6fdb Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/baed6326ebc5d987 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/bbda293fec635f4d b/scripts/.hypothesis/examples/972da276db8e758a/bbda293fec635f4d new file mode 100644 index 00000000..a8e993fd Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/bbda293fec635f4d differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/bdd4453cecba0e37 b/scripts/.hypothesis/examples/972da276db8e758a/bdd4453cecba0e37 new file mode 100644 index 00000000..9b102469 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/bdd4453cecba0e37 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/be4ead06cf5c2b11 b/scripts/.hypothesis/examples/972da276db8e758a/be4ead06cf5c2b11 new file mode 100644 index 00000000..fe8eebd3 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/be4ead06cf5c2b11 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/bf358ef439773626 b/scripts/.hypothesis/examples/972da276db8e758a/bf358ef439773626 new file mode 100644 index 00000000..cf82e997 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/bf358ef439773626 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/c32dde26c4c34be7 b/scripts/.hypothesis/examples/972da276db8e758a/c32dde26c4c34be7 new file mode 100644 index 00000000..41ca7653 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/c32dde26c4c34be7 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/c6db75fdae76c45a b/scripts/.hypothesis/examples/972da276db8e758a/c6db75fdae76c45a new file mode 100644 index 00000000..6ada3f36 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/c6db75fdae76c45a differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/c79e94cfbc1f2a33 b/scripts/.hypothesis/examples/972da276db8e758a/c79e94cfbc1f2a33 new file mode 100644 index 00000000..0e43afb4 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/c79e94cfbc1f2a33 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/c7dd3fa59bca5bfe b/scripts/.hypothesis/examples/972da276db8e758a/c7dd3fa59bca5bfe new file mode 100644 index 00000000..3b5e8466 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/c7dd3fa59bca5bfe differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/c8446e6581ac4304 b/scripts/.hypothesis/examples/972da276db8e758a/c8446e6581ac4304 new file mode 100644 index 00000000..261b35d7 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/c8446e6581ac4304 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/c8792a081899ea4f b/scripts/.hypothesis/examples/972da276db8e758a/c8792a081899ea4f new file mode 100644 index 00000000..eab41e22 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/c8792a081899ea4f differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/c8bb039bcb5e1d77 b/scripts/.hypothesis/examples/972da276db8e758a/c8bb039bcb5e1d77 new file mode 100644 index 00000000..99859003 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/c8bb039bcb5e1d77 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/c9615f25e6feaea0 b/scripts/.hypothesis/examples/972da276db8e758a/c9615f25e6feaea0 new file mode 100644 index 00000000..f8b2ee9f Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/c9615f25e6feaea0 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/cd2715812b6bfcfe b/scripts/.hypothesis/examples/972da276db8e758a/cd2715812b6bfcfe new file mode 100644 index 00000000..fadbec14 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/cd2715812b6bfcfe differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/ce7c500a330bdb84 b/scripts/.hypothesis/examples/972da276db8e758a/ce7c500a330bdb84 new file mode 100644 index 00000000..f33ac90e Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/ce7c500a330bdb84 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/cf13b60715d4ec84 b/scripts/.hypothesis/examples/972da276db8e758a/cf13b60715d4ec84 new file mode 100644 index 00000000..a711cb1f Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/cf13b60715d4ec84 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/d0ff0d68f27dbca8 b/scripts/.hypothesis/examples/972da276db8e758a/d0ff0d68f27dbca8 new file mode 100644 index 00000000..dac90f01 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/d0ff0d68f27dbca8 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/d1911510a996b2dc b/scripts/.hypothesis/examples/972da276db8e758a/d1911510a996b2dc new file mode 100644 index 00000000..a7991a56 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/d1911510a996b2dc differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/d2290b8bd41dd29c b/scripts/.hypothesis/examples/972da276db8e758a/d2290b8bd41dd29c new file mode 100644 index 00000000..b591f88a Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/d2290b8bd41dd29c differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/d259f24b7fef5386 b/scripts/.hypothesis/examples/972da276db8e758a/d259f24b7fef5386 new file mode 100644 index 00000000..70a31e4d Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/d259f24b7fef5386 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/d2f0a95fcf23f230 b/scripts/.hypothesis/examples/972da276db8e758a/d2f0a95fcf23f230 new file mode 100644 index 00000000..e1fea4f2 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/d2f0a95fcf23f230 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/d30aed40b9030520 b/scripts/.hypothesis/examples/972da276db8e758a/d30aed40b9030520 new file mode 100644 index 00000000..80418afd Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/d30aed40b9030520 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/d3bf11900d341e8f b/scripts/.hypothesis/examples/972da276db8e758a/d3bf11900d341e8f new file mode 100644 index 00000000..4b2828a2 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/d3bf11900d341e8f differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/d430e65dfacfcf78 b/scripts/.hypothesis/examples/972da276db8e758a/d430e65dfacfcf78 new file mode 100644 index 00000000..8061d597 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/d430e65dfacfcf78 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/d5a7ab593ebc9299 b/scripts/.hypothesis/examples/972da276db8e758a/d5a7ab593ebc9299 new file mode 100644 index 00000000..21a2b8b9 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/d5a7ab593ebc9299 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/d6493e97a346138a b/scripts/.hypothesis/examples/972da276db8e758a/d6493e97a346138a new file mode 100644 index 00000000..b813f67d Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/d6493e97a346138a differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/d9e0feea82962058 b/scripts/.hypothesis/examples/972da276db8e758a/d9e0feea82962058 new file mode 100644 index 00000000..49f09b40 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/d9e0feea82962058 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/db82ccfb54ca0f72 b/scripts/.hypothesis/examples/972da276db8e758a/db82ccfb54ca0f72 new file mode 100644 index 00000000..7e8ea750 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/db82ccfb54ca0f72 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/dc25925bbb5c63f5 b/scripts/.hypothesis/examples/972da276db8e758a/dc25925bbb5c63f5 new file mode 100644 index 00000000..d8b2ec78 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/dc25925bbb5c63f5 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/dc470fa4aa25f376 b/scripts/.hypothesis/examples/972da276db8e758a/dc470fa4aa25f376 new file mode 100644 index 00000000..d404c339 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/dc470fa4aa25f376 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/dce8c8837f8291d9 b/scripts/.hypothesis/examples/972da276db8e758a/dce8c8837f8291d9 new file mode 100644 index 00000000..658aa2f2 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/dce8c8837f8291d9 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/dda48081e3b3c427 b/scripts/.hypothesis/examples/972da276db8e758a/dda48081e3b3c427 new file mode 100644 index 00000000..fa289090 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/dda48081e3b3c427 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/df928c770723c1c7 b/scripts/.hypothesis/examples/972da276db8e758a/df928c770723c1c7 new file mode 100644 index 00000000..d501f7bb Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/df928c770723c1c7 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/e04e0b8f3edf05dc b/scripts/.hypothesis/examples/972da276db8e758a/e04e0b8f3edf05dc new file mode 100644 index 00000000..7125381a Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/e04e0b8f3edf05dc differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/e089ff603e5749bb b/scripts/.hypothesis/examples/972da276db8e758a/e089ff603e5749bb new file mode 100644 index 00000000..ea9c1c52 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/e089ff603e5749bb differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/e1e145c86abe97d3 b/scripts/.hypothesis/examples/972da276db8e758a/e1e145c86abe97d3 new file mode 100644 index 00000000..3baffebb Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/e1e145c86abe97d3 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/e4158fa446c0c7a1 b/scripts/.hypothesis/examples/972da276db8e758a/e4158fa446c0c7a1 new file mode 100644 index 00000000..d2962123 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/e4158fa446c0c7a1 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/e444a7d6037fab1f b/scripts/.hypothesis/examples/972da276db8e758a/e444a7d6037fab1f new file mode 100644 index 00000000..b2f0ac99 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/e444a7d6037fab1f differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/e48d68a595f8621d b/scripts/.hypothesis/examples/972da276db8e758a/e48d68a595f8621d new file mode 100644 index 00000000..89843ac6 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/e48d68a595f8621d differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/e4b4637062bc4a66 b/scripts/.hypothesis/examples/972da276db8e758a/e4b4637062bc4a66 new file mode 100644 index 00000000..8c616839 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/e4b4637062bc4a66 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/e5195318a0b1d607 b/scripts/.hypothesis/examples/972da276db8e758a/e5195318a0b1d607 new file mode 100644 index 00000000..197ddd2f Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/e5195318a0b1d607 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/e573a130daba5963 b/scripts/.hypothesis/examples/972da276db8e758a/e573a130daba5963 new file mode 100644 index 00000000..cb939c59 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/e573a130daba5963 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/e65fcba7001a3a92 b/scripts/.hypothesis/examples/972da276db8e758a/e65fcba7001a3a92 new file mode 100644 index 00000000..8058f6fe Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/e65fcba7001a3a92 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/e6b85ff31576e872 b/scripts/.hypothesis/examples/972da276db8e758a/e6b85ff31576e872 new file mode 100644 index 00000000..323d9c79 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/e6b85ff31576e872 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/e6dcd643c4d59894 b/scripts/.hypothesis/examples/972da276db8e758a/e6dcd643c4d59894 new file mode 100644 index 00000000..cebd10ca Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/e6dcd643c4d59894 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/e79acfc5fe8e7e5b b/scripts/.hypothesis/examples/972da276db8e758a/e79acfc5fe8e7e5b new file mode 100644 index 00000000..1bcb7e94 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/e79acfc5fe8e7e5b differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/e7f1eca362155d60 b/scripts/.hypothesis/examples/972da276db8e758a/e7f1eca362155d60 new file mode 100644 index 00000000..b67dc1b5 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/e7f1eca362155d60 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/e95fee051d70607c b/scripts/.hypothesis/examples/972da276db8e758a/e95fee051d70607c new file mode 100644 index 00000000..d5ab0474 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/e95fee051d70607c differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/ea19679a27e4fdc1 b/scripts/.hypothesis/examples/972da276db8e758a/ea19679a27e4fdc1 new file mode 100644 index 00000000..503b2f7f Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/ea19679a27e4fdc1 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/eb40f7a6cfeeed80 b/scripts/.hypothesis/examples/972da276db8e758a/eb40f7a6cfeeed80 new file mode 100644 index 00000000..b845ea08 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/eb40f7a6cfeeed80 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/ed9f39111f533b15 b/scripts/.hypothesis/examples/972da276db8e758a/ed9f39111f533b15 new file mode 100644 index 00000000..7926ac50 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/ed9f39111f533b15 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/ef5ac1803b7cf8e1 b/scripts/.hypothesis/examples/972da276db8e758a/ef5ac1803b7cf8e1 new file mode 100644 index 00000000..9401a36e Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/ef5ac1803b7cf8e1 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/f094d5e957c3f88a b/scripts/.hypothesis/examples/972da276db8e758a/f094d5e957c3f88a new file mode 100644 index 00000000..d6afb273 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/f094d5e957c3f88a differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/f3b6c580b1be5cb9 b/scripts/.hypothesis/examples/972da276db8e758a/f3b6c580b1be5cb9 new file mode 100644 index 00000000..05b47eb4 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/f3b6c580b1be5cb9 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/f3c23fa2bd571954 b/scripts/.hypothesis/examples/972da276db8e758a/f3c23fa2bd571954 new file mode 100644 index 00000000..e3627e88 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/f3c23fa2bd571954 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/f3cf1f81bc1f9166 b/scripts/.hypothesis/examples/972da276db8e758a/f3cf1f81bc1f9166 new file mode 100644 index 00000000..fe5aad3c Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/f3cf1f81bc1f9166 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/f43a83029c4f51d9 b/scripts/.hypothesis/examples/972da276db8e758a/f43a83029c4f51d9 new file mode 100644 index 00000000..dff7c886 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/f43a83029c4f51d9 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/f46379dd712010a5 b/scripts/.hypothesis/examples/972da276db8e758a/f46379dd712010a5 new file mode 100644 index 00000000..48118e08 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/f46379dd712010a5 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/f5210763dcfbe2b3 b/scripts/.hypothesis/examples/972da276db8e758a/f5210763dcfbe2b3 new file mode 100644 index 00000000..2056becf Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/f5210763dcfbe2b3 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/f5b135c751cc7126 b/scripts/.hypothesis/examples/972da276db8e758a/f5b135c751cc7126 new file mode 100644 index 00000000..231a520e Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/f5b135c751cc7126 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/f6dadf7f6144d2a7 b/scripts/.hypothesis/examples/972da276db8e758a/f6dadf7f6144d2a7 new file mode 100644 index 00000000..92f47eb4 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/f6dadf7f6144d2a7 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/f7f2e0b635416af1 b/scripts/.hypothesis/examples/972da276db8e758a/f7f2e0b635416af1 new file mode 100644 index 00000000..de92efe4 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/f7f2e0b635416af1 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/f883f1876eda84bb b/scripts/.hypothesis/examples/972da276db8e758a/f883f1876eda84bb new file mode 100644 index 00000000..71feda1b Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/f883f1876eda84bb differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/f99e7b49d8ac77a3 b/scripts/.hypothesis/examples/972da276db8e758a/f99e7b49d8ac77a3 new file mode 100644 index 00000000..6ae2ccb3 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/f99e7b49d8ac77a3 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/fb2374232294ebe3 b/scripts/.hypothesis/examples/972da276db8e758a/fb2374232294ebe3 new file mode 100644 index 00000000..4c2ba5c9 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/fb2374232294ebe3 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/fe3d34cd98884603 b/scripts/.hypothesis/examples/972da276db8e758a/fe3d34cd98884603 new file mode 100644 index 00000000..031ae9a9 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/fe3d34cd98884603 differ diff --git a/scripts/.hypothesis/examples/972da276db8e758a/ff68979d0ce29eb5 b/scripts/.hypothesis/examples/972da276db8e758a/ff68979d0ce29eb5 new file mode 100644 index 00000000..742eb862 Binary files /dev/null and b/scripts/.hypothesis/examples/972da276db8e758a/ff68979d0ce29eb5 differ diff --git a/scripts/.hypothesis/examples/a7082e79f930272b/38b060a751ac9638 b/scripts/.hypothesis/examples/a7082e79f930272b/38b060a751ac9638 new file mode 100644 index 00000000..e69de29b diff --git a/scripts/.hypothesis/examples/a7765ca8ac786d69/38b060a751ac9638 b/scripts/.hypothesis/examples/a7765ca8ac786d69/38b060a751ac9638 new file mode 100644 index 00000000..e69de29b diff --git a/scripts/.hypothesis/examples/c5cc8ddf7622ef8a/38b060a751ac9638 b/scripts/.hypothesis/examples/c5cc8ddf7622ef8a/38b060a751ac9638 new file mode 100644 index 00000000..e69de29b diff --git a/scripts/.hypothesis/examples/ceb7445d967ce5aa/38b060a751ac9638 b/scripts/.hypothesis/examples/ceb7445d967ce5aa/38b060a751ac9638 new file mode 100644 index 00000000..e69de29b diff --git a/scripts/.hypothesis/examples/f8448706e2bfab64/38b060a751ac9638 b/scripts/.hypothesis/examples/f8448706e2bfab64/38b060a751ac9638 new file mode 100644 index 00000000..e69de29b diff --git a/scripts/.hypothesis/unicode_data/14.0.0/charmap.json.gz b/scripts/.hypothesis/unicode_data/14.0.0/charmap.json.gz new file mode 100644 index 00000000..6dad3fd6 Binary files /dev/null and b/scripts/.hypothesis/unicode_data/14.0.0/charmap.json.gz differ diff --git a/scripts/.hypothesis/unicode_data/14.0.0/codec-utf-8.json.gz b/scripts/.hypothesis/unicode_data/14.0.0/codec-utf-8.json.gz new file mode 100644 index 00000000..4f8d0a0f Binary files /dev/null and b/scripts/.hypothesis/unicode_data/14.0.0/codec-utf-8.json.gz differ diff --git a/src/Ghosts.Animator/Education.cs b/src/Ghosts.Animator/Education.cs index a440546b..e1cabdf0 100644 --- a/src/Ghosts.Animator/Education.cs +++ b/src/Ghosts.Animator/Education.cs @@ -378,7 +378,7 @@ private static EducationProfile DegreeMOSRequirements(EducationProfile o, Milita } } } - else if (rank.Branch == MilitaryBranch.USAF && rank.Pay[0] == 'O') + else if (rank.Branch == MilitaryBranch.USAF && rank.Pay != null && rank.Pay[0] == 'O') { //check MOS //Engineer: 32EX, 61DX, 62EX, 62EX* diff --git a/src/Ghosts.Animator/Extensions/StringExtensions.cs b/src/Ghosts.Animator/Extensions/StringExtensions.cs index 471e0119..7527a42e 100644 --- a/src/Ghosts.Animator/Extensions/StringExtensions.cs +++ b/src/Ghosts.Animator/Extensions/StringExtensions.cs @@ -39,12 +39,15 @@ private static string Replace(this string str, char item, Func character) public static string ToAccountSafeString(this string o) { + if (string.IsNullOrEmpty(o)) return string.Empty; o = Regex.Replace(o, @"[^0-9a-zA-Z\._]", ""); return o; } public static string After(this string value, string a) { + if (string.IsNullOrEmpty(value) || string.IsNullOrEmpty(a)) return string.Empty; + var posA = value.LastIndexOf(a, StringComparison.InvariantCultureIgnoreCase); if (posA == -1) { diff --git a/src/Ghosts.Animator/Internet.cs b/src/Ghosts.Animator/Internet.cs index c7d403f8..1b1353fd 100644 --- a/src/Ghosts.Animator/Internet.cs +++ b/src/Ghosts.Animator/Internet.cs @@ -47,13 +47,14 @@ public static string GetFreeEmail(string name = null) public static string GetUserName(string name = null) { - //% have a random username not associated with their own name + // 67% chance to get a random username from the file if (AnimatorRandom.Rand.Next(0, 3) > 1) { - var file = $"config/usernames.txt"; + var file = "config/usernames.txt"; return file.GetRandomFromFile(); } - + + // If name is null, generate a new name if (name == null) { switch (AnimatorRandom.Rand.Next(2)) @@ -62,20 +63,35 @@ public static string GetUserName(string name = null) name = new Regex(@"\W").Replace(Name.GetFirstName(), "").ToLower(); break; default: - name = new[] {Name.GetFirstName(), Name.GetLastName()}.Select(n => new Regex(@"\W").Replace(n, "")) - .Join(new[] {".", "_"}.RandomElement()).ToLower(); + name = new[] + { + new Regex(@"\W").Replace(Name.GetFirstName(), ""), + new Regex(@"\W").Replace(Name.GetLastName(), "") + }.Join(new[] { ".", "_" }.RandomElement()).ToLower(); break; } } + // Convert the name to an account-safe string name = name.ToAccountSafeString(); - name = name.Split(' ').Join(new[] {".", "_"}.RandomElement()).ToLower(); + + // Split and join the name using a random delimiter if there are spaces + name = name.Split(' ').Join(new[] { ".", "_" }.RandomElement()).ToLower(); - if (AnimatorRandom.Rand.Next(0,4) > 0) - name = name.Substring(0, AnimatorRandom.Rand.Next(1, name.Length - 1)); + // Randomly shorten the name + if (AnimatorRandom.Rand.Next(0, 4) > 0) + { + if (name.Length > 1) + { + name = name.Substring(0, AnimatorRandom.Rand.Next(1, name.Length)); + } + } - if (AnimatorRandom.Rand.Next(0,4) > 0) - name += AnimatorRandom.Rand.Next(0, 9999); + // Randomly append a number + if (AnimatorRandom.Rand.Next(0, 4) > 0) + { + name += AnimatorRandom.Rand.Next(0, 10000); // Range 0 to 9999 + } return name; } @@ -157,8 +173,11 @@ public static AccountsProfile GetAccountProfile(string name = null) return o; } - public static IEnumerable GetAccounts(string name = null) + public static IEnumerable GetAccounts(string name) { + if (string.IsNullOrEmpty(name)) + return new List(); + var o = new List(); var numberOfAccounts = AnimatorRandom.Rand.Next(0, 15); diff --git a/src/Ghosts.Animator/MilitaryUnits.cs b/src/Ghosts.Animator/MilitaryUnits.cs index fb1b61f7..daae88a7 100644 --- a/src/Ghosts.Animator/MilitaryUnits.cs +++ b/src/Ghosts.Animator/MilitaryUnits.cs @@ -41,6 +41,10 @@ public static MilitaryUnit GetOneByServiceBranch(MilitaryBranch branch) { choice.Unit.Address = GetBaseAddress(branch, hq.MilUnit.Address.Name); } + else + { + choice.Unit.Address = new AddressProfiles.AddressProfile(); + } return choice.Unit; } diff --git a/src/Ghosts.Animator/ghosts.animator.csproj b/src/Ghosts.Animator/ghosts.animator.csproj index 72cf8922..2db9895f 100644 --- a/src/Ghosts.Animator/ghosts.animator.csproj +++ b/src/Ghosts.Animator/ghosts.animator.csproj @@ -4,8 +4,8 @@ Ghosts.Animator ghosts.animator - 1.1.1.0 - 1.1.1.0 + 1.2.1.0 + 1.2.1.0 GHOSTS Development Team @ Carnegie Mellon University Carnegie Mellon University @@ -17,7 +17,7 @@ - + diff --git a/src/Ghosts.Api/Areas/Animator/Controllers/Api/AnimationJobsController.cs b/src/Ghosts.Api/Areas/Animator/Controllers/Api/AnimationJobsController.cs deleted file mode 100644 index 2c2204a1..00000000 --- a/src/Ghosts.Api/Areas/Animator/Controllers/Api/AnimationJobsController.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. - -using System; -using System.Threading; -using System.Threading.Tasks; -using ghosts.api.Areas.Animator.Infrastructure.Animations; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; -using Swashbuckle.AspNetCore.Annotations; - -namespace ghosts.api.Areas.Animator.Controllers.Api; - -[Area("Animator")] -[Route("animations")] -[Controller] -[Produces("application/json")] -public class AnimationJobsController : Controller -{ - private readonly AnimationsManager _animationsManager; - - public AnimationJobsController(IServiceProvider serviceProvider) - { - _animationsManager = serviceProvider.GetRequiredService() as AnimationsManager; - } - - [SwaggerOperation("animationsStart")] - [HttpGet("start")] - public async Task Start(CancellationToken cancellationToken) - { - await _animationsManager.StartAsync(cancellationToken); - return Ok(); - } - - [SwaggerOperation("animationsStop")] - [HttpGet("stop")] - public async Task Stop(CancellationToken cancellationToken) - { - await _animationsManager.StopAsync(cancellationToken); - return Ok(); - } - - [SwaggerOperation("animationsStatus")] - [HttpGet("status")] - public IActionResult Status(CancellationToken cancellationToken) - { - return Ok(_animationsManager.GetRunningJobs()); - } - - [SwaggerOperation("animationsOutput")] - [HttpGet("output")] - public IActionResult Output(AnimationJobTypes job, CancellationToken cancellationToken) - { - var zipFilePath = _animationsManager.GetOutput(job); - - var bytes = System.IO.File.ReadAllBytes(zipFilePath); - return File(bytes, "application/zip", $"{job.ToString().ToLower()}-{DateTime.Now.ToString("yyyyMMddHHmmss")}.zip"); - } -} \ No newline at end of file diff --git a/src/Ghosts.Api/Areas/Animator/Controllers/Api/NpcsGenerateController.cs b/src/Ghosts.Api/Areas/Animator/Controllers/Api/NpcsGenerateController.cs deleted file mode 100644 index ec91c3bd..00000000 --- a/src/Ghosts.Api/Areas/Animator/Controllers/Api/NpcsGenerateController.cs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Ghosts.Animator; -using Ghosts.Animator.Models; -using ghosts.api.Areas.Animator.Infrastructure.Models; -using Ghosts.Api.Infrastructure.Data; -using Microsoft.AspNetCore.Mvc; -using NLog; -using Swashbuckle.AspNetCore.Annotations; -using Npc = Ghosts.Animator.Npc; - -namespace ghosts.api.Areas.Animator.Controllers.Api; - -/// -/// Build entire team of NPCs for a campaign and enclave -/// -[ApiController] -[Produces("application/json")] -[Route("api/[controller]")] -public class NpcsGenerateController : ControllerBase -{ - private static readonly Logger _log = LogManager.GetCurrentClassLogger(); - private readonly ApplicationDbContext _context; - - public NpcsGenerateController(ApplicationDbContext context) - { - _context = context; - } - - /// - /// Returns all NPCs at the specified level - Campaign, Enclave, or Team - /// - /// campaign, enclave, team - /// - [ProducesResponseType(typeof(IEnumerable), (int) HttpStatusCode.OK)] - [SwaggerResponse((int) HttpStatusCode.OK, Type = typeof(IEnumerable))] - [SwaggerOperation("getKeys")] - [HttpGet] - public IEnumerable GetKeys(string key) - { - return key.ToLower() switch - { - "campaign" => _context.Npcs.Where(x => x.Campaign != null).Distinct().Select(x=>x.Campaign).ToList(), - "enclave" => _context.Npcs.Where(x => x.Enclave != null).Distinct().Select(x=>x.Enclave).ToList(), - "team" => _context.Npcs.Where(x => x.Team != null).Distinct().Select(x=>x.Team).ToList(), - _ => throw new KeyNotFoundException("Invalid key! Key must be campaign, enclave or team") - }; - } - - /// - /// Create NPCs belonging to a campaign, enclave and team based on configuration - /// - /// - /// - /// - [ProducesResponseType(typeof(IEnumerable), (int) HttpStatusCode.OK)] - [SwaggerResponse((int) HttpStatusCode.OK, Type = typeof(IEnumerable))] - [SwaggerOperation("createBuild")] - [HttpPost] - public async Task> Create(GenerationConfiguration config, CancellationToken ct) - { - var t = new Stopwatch(); - t.Start(); - - var createdNpcs = new List(); - foreach (var enclave in config.Enclaves) - { - foreach (var team in enclave.Teams) - { - for (var i = 0; i < team.Npcs.Number; i++) - { - var last = t.ElapsedMilliseconds; - var branch = team.Npcs.Configuration?.Branch ?? MilitaryUnits.GetServiceBranch(); - var npc = NpcRecord.TransformToNpc(Npc.Generate(new NpcGenerationConfiguration { Branch = branch, PreferenceSettings = team.PreferenceSettings })); - npc.Id = npc.NpcProfile.Id; - npc.Team = team.Name; - npc.Campaign = config.Campaign; - npc.Enclave = enclave.Name; - - this._context.Npcs.Add(npc); - createdNpcs.Add(npc); - _log.Trace($"{i} generated in {t.ElapsedMilliseconds - last} ms"); - } - } - } - await this._context.SaveChangesAsync(ct); - - t.Stop(); - _log.Trace($"{createdNpcs.Count} NPCs generated in {t.ElapsedMilliseconds} ms"); - - return createdNpcs; - } - - /// - /// Generate random NPC by random service branch - /// - /// NPC Profile - [ProducesResponseType(typeof(NpcProfile), (int) HttpStatusCode.OK)] - [SwaggerResponse((int) HttpStatusCode.OK, Type = typeof(NpcProfile))] - [SwaggerOperation("generateNpc")] - [HttpPost("one")] - public async Task GenerateOne() - { - var npc = NpcRecord.TransformToNpc(Npc.Generate(MilitaryUnits.GetServiceBranch())); - npc.Id = npc.NpcProfile.Id; - this._context.Npcs.Add(npc); - await this._context.SaveChangesAsync(); - return npc; - } - - /// - /// Ensures an NPC is created for each and every machine currentusername that exists - /// - /// NPC Profile - [ProducesResponseType(typeof(NpcProfile), (int) HttpStatusCode.OK)] - [SwaggerResponse((int) HttpStatusCode.OK, Type = typeof(NpcProfile))] - [SwaggerOperation("syncNpcsWithMachines")] - [HttpPost("syncWithMachineUsernames")] - public async Task SyncWithMachineUsernames() - { - var machines = this._context.Machines.ToList(); - var npcs = this._context.Npcs.ToArray(); - - foreach (var machine in machines) - { - if (npcs.Any(x => x.MachineId == machine.Id)) - continue; - if (npcs.Any(x => string.Equals(x.NpcProfile.Name.ToString()?.Replace(" ", "."), - machine.CurrentUsername, StringComparison.InvariantCultureIgnoreCase))) - continue; - - var npc = NpcRecord.TransformToNpc(Npc.Generate(MilitaryUnits.GetServiceBranch(), machine.CurrentUsername)); - - //todo: need to be sure user is aligned with the machine currentusername - - npc.Id = npc.NpcProfile.Id; - npc.MachineId = machine.Id; - this._context.Npcs.Add(npc); - _log.Trace($"NPC created for {machine.CurrentUsername}..."); - } - await this._context.SaveChangesAsync(); - _log.Trace($"NPCs created for each username in machines"); - } -} \ No newline at end of file diff --git a/src/Ghosts.Api/Areas/Animator/Controllers/HomeController.cs b/src/Ghosts.Api/Areas/Animator/Controllers/HomeController.cs deleted file mode 100644 index ed80397e..00000000 --- a/src/Ghosts.Api/Areas/Animator/Controllers/HomeController.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. - -using System.Threading.Tasks; -using Ghosts.Api; -using ghosts.api.Areas.Animator.Infrastructure.ContentServices.Ollama; -using ghosts.api.Areas.Animator.Infrastructure.Models; -using Microsoft.AspNetCore.Mvc; - -namespace ghosts.api.Areas.Animator.Controllers; - -[Area("Animator")] -[Route("animator")] -public class HomeController : Controller -{ - [ApiExplorerSettings(IgnoreApi = true)] - [HttpGet] - public IActionResult Index() - { - return View(); - } - - [ApiExplorerSettings(IgnoreApi = true)] - [HttpGet("test")] - public async Task Test() - { - var x = new OllamaFormatterService(Program.ApplicationSettings.AnimatorSettings.Animations.SocialSharing.ContentEngine); - var o = await x.GenerateNextAction(new NpcRecord(),"why is the sky blue?"); - return Ok(o); - - } -} \ No newline at end of file diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/SocialSharingJob.cs b/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/SocialSharingJob.cs deleted file mode 100644 index ea3703a5..00000000 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/SocialSharingJob.cs +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Threading; -using Ghosts.Animator.Extensions; -using ghosts.api.Areas.Animator.Hubs; -using ghosts.api.Areas.Animator.Infrastructure.ContentServices; -using ghosts.api.Areas.Animator.Infrastructure.Models; -using Ghosts.Api.Infrastructure; -using Ghosts.Api.Infrastructure.Data; -using ghosts.api.Infrastructure.Models; -using ghosts.api.Infrastructure.Services; -using Ghosts.Domain; -using Microsoft.AspNetCore.SignalR; -using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; -using NLog; -using RestSharp; - -namespace ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationDefinitions; - -public class SocialSharingJob -{ - private static readonly Logger _log = LogManager.GetCurrentClassLogger(); - private readonly ApplicationSettings _configuration; - private readonly Random _random; - private readonly int _currentStep; - private readonly IHubContext _activityHubContext; - private readonly CancellationToken _cancellationToken; - private readonly ApplicationDbContext _context; - private readonly IMachineUpdateService _updateService; - private readonly IFormatterService _formatterService; - - public SocialSharingJob(ApplicationSettings configuration, IServiceScopeFactory scopeFactory, Random random, - IHubContext activityHubContext, CancellationToken cancellationToken) - { - try - { - this._activityHubContext = activityHubContext; - this._configuration = configuration; - this._random = random; - - using var innerScope = scopeFactory.CreateScope(); - this._context = innerScope.ServiceProvider.GetRequiredService(); - - this._cancellationToken = cancellationToken; - this._updateService = innerScope.ServiceProvider.GetRequiredService(); - - _formatterService = - new ContentCreationService(_configuration.AnimatorSettings.Animations.Chat.ContentEngine).FormatterService; - - if (!_configuration.AnimatorSettings.Animations.SocialSharing.IsInteracting) - { - _log.Trace($"Social sharing is not interacting. Exiting..."); - return; - } - - while (!this._cancellationToken.IsCancellationRequested) - { - if (this._currentStep > _configuration.AnimatorSettings.Animations.SocialSharing.MaximumSteps) - { - _log.Trace($"Maximum steps met: {this._currentStep - 1}. Social sharing is exiting..."); - return; - } - - this.Step(); - Thread.Sleep(this._configuration.AnimatorSettings.Animations.SocialSharing.TurnLength); - this._currentStep++; - } - } - catch (ThreadInterruptedException e) - { - _log.Info("Social sharing thread interrupted!"); - _log.Error(e); - } - catch (Exception e) - { - _log.Error(e); - } - _log.Info("Social sharing job complete. Exiting..."); - } - - private async void Step() - { - _log.Trace("Social sharing step proceeding..."); - - //take some random NPCs - var activities = new List(); - var rawAgents = this._context.Npcs.ToList(); - if (!rawAgents.Any()) - { - _log.Warn("No NPCs found. Is this correct?"); - return; - } - _log.Trace($"Found {rawAgents.Count()} raw agents..."); - - var agents = rawAgents.Shuffle(_random).Take(_random.Next(5, 20)).ToList(); - _log.Trace($"Processing {agents.Count()} agents..."); - foreach (var agent in agents) - { - _log.Trace($"Processing agent {agent.NpcProfile.Email}..."); - var tweetText = await this._formatterService.GenerateTweet(agent); - if (string.IsNullOrEmpty(tweetText)) - { - _log.Trace($"Content service generated no payload..."); - return; - } - - activities.Add(new NpcActivity {ActivityType = NpcActivity.ActivityTypes.SocialMediaPost, NpcId = agent.Id, CreatedUtc = DateTime.UtcNow, Detail = tweetText}); - - // the payloads to socializer are a bit randomized - var userFormValue = new[] { "user", "usr", "u", "uid", "user_id", "u_id" }.RandomFromStringArray(); - var messageFormValue = - new[] { "message", "msg", "m", "message_id", "msg_id", "msg_text", "text", "payload" } - .RandomFromStringArray(); - - if (_configuration.AnimatorSettings.Animations.SocialSharing.IsSendingTimelinesDirectToSocializer) - { - var client = new RestClient(_configuration.AnimatorSettings.Animations.SocialSharing.PostUrl); - var request = new RestRequest("/", Method.Post) - { - RequestFormat = DataFormat.Json - }; - request.AddParameter(userFormValue, agent.NpcProfile.Name.ToString()); - request.AddParameter(messageFormValue, tweetText); - - try - { - var response = client.Execute(request); - if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.NoContent) - { - throw (new Exception( - $"Socializer responded with {response.StatusCode} to the request agent: {agent.NpcProfile.Name} text: {tweetText}")); - } - } - catch (Exception e) - { - _log.Error( - $"Could not post timeline command to Socializer {_configuration.AnimatorSettings.Animations.SocialSharing.PostUrl}: {e}"); - } - } - - if (_configuration.AnimatorSettings.Animations.SocialSharing.IsSendingTimelinesToGhostsApi) - { - var payload = new - { - Uri = _configuration.AnimatorSettings.Animations.SocialSharing.PostUrl, - Category = "social", - Method = "POST", - Headers = new Dictionary - { - { "u", agent.NpcProfile.Email } - }, - FormValues = new Dictionary - { - { userFormValue, agent.NpcProfile.Email }, - { messageFormValue, tweetText } - } - }; - - var t = new Timeline(); - t.Id = Guid.NewGuid(); - t.Status = Timeline.TimelineStatus.Run; - var th = new TimelineHandler(); - th.HandlerType = HandlerType.BrowserFirefox; - th.Initial = "about:blank"; - th.UtcTimeOn = new TimeSpan(0, 0, 0); - th.UtcTimeOff = new TimeSpan(23, 59, 59); - th.HandlerArgs = new Dictionary(); - th.HandlerArgs.Add("isheadless", "false"); - th.Loop = false; - var te = new TimelineEvent(); - te.Command = "browse"; - te.CommandArgs = new List(); - te.CommandArgs.Add(JsonConvert.SerializeObject(payload)); - te.DelayAfter = 0; - te.DelayBefore = 0; - th.TimeLineEvents.Add(te); - t.TimeLineHandlers.Add(th); - - var machineUpdate = new MachineUpdate(); - if (agent.MachineId.HasValue) - { - machineUpdate.MachineId = agent.MachineId.Value; - } - - machineUpdate.Update = JsonConvert.SerializeObject(t); - machineUpdate.Username = agent.NpcProfile.Email; - machineUpdate.Status = StatusType.Active; - machineUpdate.Type = UpdateClientConfig.UpdateType.TimelinePartial; - - _ = await _updateService.CreateAsync(machineUpdate, _cancellationToken); - } - - //post to hub - await this._activityHubContext.Clients.All.SendAsync("show", - "1", - agent.Id.ToString(), - "social", - tweetText, - DateTime.Now.ToString(CultureInfo.InvariantCulture), - cancellationToken: _cancellationToken); - } - - await this._context.NpcActivities.AddRangeAsync(activities, _cancellationToken); - await this._context.SaveChangesAsync(this._cancellationToken); - } - -} \ No newline at end of file diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/IContentService.cs b/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/IContentService.cs deleted file mode 100644 index 1050ba16..00000000 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/IContentService.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Threading.Tasks; - -namespace ghosts.api.Areas.Animator.Infrastructure.ContentServices; - -public interface IContentService -{ - Task ExecuteQuery(string prompt); -} \ No newline at end of file diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPCToInsiderThreatCsv.cs b/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPCToInsiderThreatCsv.cs deleted file mode 100644 index 699204f7..00000000 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPCToInsiderThreatCsv.cs +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using FileHelpers; -using Ghosts.Animator; -using Ghosts.Animator.Extensions; -using Ghosts.Animator.Models.InsiderThreat; - -namespace ghosts.api.Areas.Animator.Infrastructure.Models; - -[DelimitedRecord(",")] -public class NPCToInsiderThreatCsv -{ - [FieldQuoted] public Guid Id { get; set; } - [FieldQuoted] public string Hostname { get; set; } - [FieldQuoted] public string DNS { get; set; } - [FieldQuoted] public string OpenVPNUsername { get; set; } - [FieldQuoted] public string OpenVPNPassword { get; set; } - [FieldQuoted] public string GmailUsername { get; set; } - [FieldQuoted] public string GmailPassword { get; set; } - [FieldQuoted] public string NmailUser { get; set; } - [FieldQuoted] public string NmailPassword { get; set; } - [FieldQuoted] public string IPAddress { get; set; } - [FieldQuoted] public string DomainUser { get; set; } - [FieldQuoted] public string FirstName { get; set; } - [FieldQuoted] public string LastName { get; set; } - [FieldQuoted] public string Password { get; set; } - [FieldQuoted] public string Company { get; set; } - [FieldQuoted] public string StartDate { get; set; } - [FieldQuoted] public string Department { get; set; } - [FieldQuoted] public string Organization { get; set; } - [FieldQuoted] public string JobTitle { get; set; } - [FieldQuoted] public int JobLevel { get; set; } - [FieldQuoted] public string Salary { get; set; } - [FieldConverter(typeof(EmptyGuidConverter))] [FieldQuoted] public Guid Manager { get; set; } - [FieldQuoted] public string EmailSuffix { get; set; } - [FieldQuoted] public string Email { get; set; } - [FieldQuoted] public string Type { get; set; } - [FieldQuoted] public string Address { get; set; } - [FieldQuoted] public string City { get; set; } - [FieldQuoted] public string Phone { get; set; } - [FieldQuoted] public string State { get; set; } - [FieldQuoted] public string Zip { get; set; } - [FieldQuoted] public string Country { get; set; } - [FieldQuoted] public string EmploymentStatus { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool Disgruntled { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool Demoted { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool MissedRaises { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool TeamLayoffs { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool NotifiedOfTermination { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool AnnouncesTermination { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool AnnouncesResignation { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool Threats { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool MissedPromotion { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool Insubordination { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool Absenteeism { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool HRComplaints { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool ITPolicyViolations { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool IPPolicyViolations { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool DrugAlcoholAbuse { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool CoworkerConflict { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool EAPReferral { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool AccessRevoked { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool UnauthorizedCodingChange { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool UnauthorizedAccessChange { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool FinancialProblems { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool ArrestRecord { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool GamblingHistory { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool ServiceAccountUse { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool RemoteAccess { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool BackdoorAccountUse { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool SolicitedByCompetitor { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool AfterHoursLogin { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool PrivilegeCreep { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool FileExtensionModification { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool FileHeaderModification { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool FileContentModification { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool FileTypeModification { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool SensitiveInformationCopied { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool ScreenShots { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool ZipFile { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool Encryption { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool DocumentMarkingTampering { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool Steganography { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool LogDeletion { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool ConcealmentInformation { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool SaleAttempt { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool Scanner { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool CloudStorage { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool RemovableMedia { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool Print { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool NetworkShare { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool PersonalEmailAccount { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool EmailToConspirator { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool DNSExfiltrationTool { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool FileDeletion { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool RecentHRTicket { get; set; } - [FieldConverter(typeof(TrueFalseToXConverter))] [FieldQuoted] public bool BackgroundCkResult { get; set; } - [FieldQuoted] public int InterpersonalSkills { get; set; } - [FieldQuoted] public int AdherenceToPolicy { get; set; } - [FieldQuoted] public int EnthusiasmAndAttitude { get; set; } - [FieldQuoted] public int OpenToFeedback { get; set; } - [FieldQuoted] public int GeneralPerformance { get; set; } - [FieldQuoted] public int OverallPerformance { get; set; } - - public static IEnumerable ConvertToCsv(IEnumerable npcs) - { - var finalList = new List(); - foreach (var n in npcs) - { - var events = n.NpcProfile.InsiderThreat.GetAllEvents(); - - var i = AnimatorRandom.Rand.Next(0, 99); - var o = new NPCToInsiderThreatCsv(); - o.Id = n.Id; - o.Hostname = $"user{i}"; - o.DNS = $"FIN-USR-{i}"; - - foreach (var account in n.NpcProfile.Accounts.Where(x => x.Url.ToLower().Contains("openvpn"))) - { - o.OpenVPNUsername = account.Username; - o.OpenVPNPassword = account.Password; - } - - // o.HRMUserID - // o.HRMManagerID - // o.TestCase - // o.ExampleCase - - foreach (var account in n.NpcProfile.Accounts.Where(x => x.Url.ToLower().Contains("gmail"))) - { - o.GmailUsername = account.Username; - o.GmailPassword = account.Password; - } - - foreach (var account in n.NpcProfile.Accounts.Where(x => x.Url.ToLower().Contains("nmail"))) - { - o.NmailUser = account.Username; - o.NmailPassword = account.Password; - } - - o.IPAddress = n.NpcProfile.Workstation.IPAddress; - - o.DomainUser = n.NpcProfile.Workstation.Username; - o.FirstName = n.NpcProfile.Name.First; - o.LastName = n.NpcProfile.Name.Last; - o.Password = n.NpcProfile.Workstation.Password; - o.Type = "User"; - if (n.NpcProfile.Employment.EmploymentRecords != null && n.NpcProfile.Employment.EmploymentRecords.Any()) - { - var currentEmployer = n.NpcProfile.Employment.EmploymentRecords.LastOrDefault(); - o.EmploymentStatus = "Employed"; - o.Company = currentEmployer?.Company; - o.StartDate = currentEmployer?.StartDate.ToString(CultureInfo.InvariantCulture); - o.Department = currentEmployer?.Department; - o.Organization = currentEmployer?.Organization; - o.JobTitle = currentEmployer?.JobTitle; - o.JobLevel = currentEmployer.Level; - o.Salary = currentEmployer?.Salary.ToString(CultureInfo.InvariantCulture); - o.Manager = currentEmployer.Manager; - if(o.JobLevel > 2) - { - if (AnimatorRandom.Rand.Next(0, 100) > 50) - { - o.Type = "Administrator"; - } - } - } - else - { - o.EmploymentStatus = "Unemployed"; - } - o.EmailSuffix = n.NpcProfile.Email.After("@"); - o.Email = n.NpcProfile.Email; - - o.Address = n.NpcProfile.Address.FirstOrDefault()?.Address1; - o.City = n.NpcProfile.Address.FirstOrDefault()?.City; - o.Phone = n.NpcProfile.CellPhone; - o.State = n.NpcProfile.Address.FirstOrDefault()?.State; - o.Zip = n.NpcProfile.Address.FirstOrDefault()?.PostalCode; - o.Country = "US"; - var relatedEvents = events as RelatedEvent[] ?? events.ToArray(); - o.Demoted = relatedEvents.Any(x => x.Description.Contains("Demoted", StringComparison.CurrentCultureIgnoreCase)); - o.Disgruntled = relatedEvents.Length > 3; - o.MissedRaises = relatedEvents.Any(x => x.Description.Contains("Missed raise", StringComparison.CurrentCultureIgnoreCase)); - o.TeamLayoffs = relatedEvents.Any(x => x.Description.Contains("Team layoffs", StringComparison.CurrentCultureIgnoreCase)); - o.NotifiedOfTermination = relatedEvents.Any(x => x.Description.Contains("Notified of termination", StringComparison.CurrentCultureIgnoreCase)); - o.AnnouncesTermination = relatedEvents.Any(x => x.Description.Contains("Announces Termination", StringComparison.CurrentCultureIgnoreCase)); - o.AnnouncesResignation = relatedEvents.Any(x => x.Description.Contains("Announces Resignation", StringComparison.CurrentCultureIgnoreCase)); - o.Threats = relatedEvents.Any(x => x.Description.Contains("Threatening", StringComparison.CurrentCultureIgnoreCase)) || - relatedEvents.Any(x => x.Description.Contains("threatened", StringComparison.CurrentCultureIgnoreCase)); - o.MissedPromotion = relatedEvents.Any(x => x.Description.Contains("Missed Promotion", StringComparison.CurrentCultureIgnoreCase)); - o.Insubordination = relatedEvents.Any(x => x.Description.Contains("insubordinate", StringComparison.CurrentCultureIgnoreCase)); - o.Absenteeism = relatedEvents.Any(x => x.Description.Contains("missed work", StringComparison.CurrentCultureIgnoreCase)); - o.HRComplaints = relatedEvents.Any(x => x.Description.Contains("Human Resource Complaint", StringComparison.CurrentCultureIgnoreCase)); - o.ITPolicyViolations = relatedEvents.Any(x => x.Description.Contains("Employee violated company IT policy", StringComparison.CurrentCultureIgnoreCase)); - o.IPPolicyViolations = relatedEvents.Any(x => x.Description.Contains("Compliance Violation", StringComparison.CurrentCultureIgnoreCase)); - o.DrugAlcoholAbuse = n.NpcProfile.InsiderThreat.SubstanceAbuseAndAddictiveBehaviors.RelatedEvents.Any(); - o.CoworkerConflict = relatedEvents.Any(x => x.Description.Contains("coworker", StringComparison.CurrentCultureIgnoreCase)); - o.EAPReferral = relatedEvents.Any(x => x.Description.Contains("EAP Referral", StringComparison.CurrentCultureIgnoreCase)); - o.AccessRevoked = relatedEvents.Any(x => x.Description.Contains("Access Revoked", StringComparison.CurrentCultureIgnoreCase)); - o.UnauthorizedCodingChange = relatedEvents.Any(x => x.Description.Contains("unauthorized changes to a code base", StringComparison.CurrentCultureIgnoreCase)); - o.UnauthorizedAccessChange = relatedEvents.Any(x => x.Description.Contains("unauthorized changes to access", StringComparison.CurrentCultureIgnoreCase)); - o.FinancialProblems = relatedEvents.Any(x => x.Description.Contains("Financial Problems", StringComparison.CurrentCultureIgnoreCase)) || - n.NpcProfile.InsiderThreat.FinancialConsiderations.RelatedEvents.Any(); - o.ArrestRecord = relatedEvents.Any(x => x.Description.Contains("arrest", StringComparison.CurrentCultureIgnoreCase)); - o.GamblingHistory = relatedEvents.Any(x => x.Description.Contains("gambling", StringComparison.CurrentCultureIgnoreCase)); - o.ServiceAccountUse = relatedEvents.Any(x => x.Description.Contains("service account", StringComparison.CurrentCultureIgnoreCase)); - o.RemoteAccess = relatedEvents.Any(x => x.Description.Contains("Virtual Access Anomaly", StringComparison.CurrentCultureIgnoreCase)); - o.BackdoorAccountUse = relatedEvents.Any(x => x.Description.Contains("backdoor account", StringComparison.CurrentCultureIgnoreCase)); - o.SolicitedByCompetitor = relatedEvents.Any(x => x.Description.Contains("Solicited by Competitor", StringComparison.CurrentCultureIgnoreCase)); - o.AfterHoursLogin = relatedEvents.Any(x => x.Description.Contains("After Hours Login", StringComparison.CurrentCultureIgnoreCase)); - o.PrivilegeCreep = relatedEvents.Any(x => x.Description.Contains("Misusing Privileged Function", StringComparison.CurrentCultureIgnoreCase)); - o.FileExtensionModification = relatedEvents.Any(x => x.Description.Contains("modified file extension", StringComparison.CurrentCultureIgnoreCase)); - o.FileHeaderModification = relatedEvents.Any(x => x.Description.Contains("modified file header", StringComparison.CurrentCultureIgnoreCase)); - o.FileContentModification = relatedEvents.Any(x => x.Description.Contains("altered document", StringComparison.CurrentCultureIgnoreCase)); - o.FileTypeModification = relatedEvents.Any(x => x.Description.Contains("modified file extension", StringComparison.CurrentCultureIgnoreCase)); - o.SensitiveInformationCopied = relatedEvents.Any(x => x.Description.Contains("copied sensitive information", StringComparison.CurrentCultureIgnoreCase)); - o.ScreenShots = relatedEvents.Any(x => x.Description.Contains("took screenshots", StringComparison.CurrentCultureIgnoreCase)); - o.ZipFile = relatedEvents.Any(x => x.Description.Contains("compressed files", StringComparison.CurrentCultureIgnoreCase)); - o.Encryption = relatedEvents.Any(x => x.Description.Contains("encrypted files", StringComparison.CurrentCultureIgnoreCase)); - o.DocumentMarkingTampering = relatedEvents.Any(x => x.Description.Contains("altered document markings", StringComparison.CurrentCultureIgnoreCase)); - o.Steganography = relatedEvents.Any(x => x.Description.Contains("used steganography", StringComparison.CurrentCultureIgnoreCase)); - o.LogDeletion = relatedEvents.Any(x => x.Description.Contains("deleted logs", StringComparison.CurrentCultureIgnoreCase)); - o.ConcealmentInformation = relatedEvents.Any(x => x.Description.Contains("concealed actions", StringComparison.CurrentCultureIgnoreCase)); - o.SaleAttempt = relatedEvents.Any(x => x.Description.Contains("Information Sale Attempt", StringComparison.CurrentCultureIgnoreCase)); - o.Scanner = relatedEvents.Any(x => x.Description.Contains("scanned files", StringComparison.CurrentCultureIgnoreCase)); - o.CloudStorage = relatedEvents.Any(x => x.Description.Contains("cloud storage", StringComparison.CurrentCultureIgnoreCase)); - o.RemovableMedia = relatedEvents.Any(x => x.Description.Contains("removable media device", StringComparison.CurrentCultureIgnoreCase)); - o.Print = relatedEvents.Any(x => x.Description.Contains("printed sensitive files", StringComparison.CurrentCultureIgnoreCase)); - o.NetworkShare = relatedEvents.Any(x => x.Description.Contains("unauthorized changes", StringComparison.CurrentCultureIgnoreCase)); - o.PersonalEmailAccount = relatedEvents.Any(x => x.Description.Contains("personal email account", StringComparison.CurrentCultureIgnoreCase)); - o.EmailToConspirator = relatedEvents.Any(x => x.Description.Contains("Email to Conspirator", StringComparison.CurrentCultureIgnoreCase)); - o.DNSExfiltrationTool = relatedEvents.Any(x => x.Description.Contains("dns exfiltration", StringComparison.CurrentCultureIgnoreCase)); - o.FileDeletion = relatedEvents.Any(x => x.Description.Contains("deleted files", StringComparison.CurrentCultureIgnoreCase)); - o.RecentHRTicket = relatedEvents.Any(x => x.Reported > DateTime.Now.AddYears(-1)); //reported in the last year - o.BackgroundCkResult = n.NpcProfile.InsiderThreat.IsBackgroundCheckStatusClear; - o.InterpersonalSkills = n.NpcProfile.MentalHealth.InterpersonalSkills; - o.AdherenceToPolicy = n.NpcProfile.MentalHealth.AdherenceToPolicy; - o.EnthusiasmAndAttitude = n.NpcProfile.MentalHealth.EnthusiasmAndAttitude; - o.OpenToFeedback = n.NpcProfile.MentalHealth.OpenToFeedback; - o.GeneralPerformance = n.NpcProfile.MentalHealth.GeneralPerformance; - o.OverallPerformance = n.NpcProfile.MentalHealth.OverallPerformance; - finalList.Add(o); - } - - foreach (var npc in finalList.Where(x => x.Manager != Guid.Empty)) - { - var manager = finalList.FirstOrDefault(x => x.Id == npc.Manager); - if (manager == null) continue; - if (npc.Company != manager.Company || npc.Department != manager.Department) - { - npc.Manager = Guid.Empty; - } - } - - return finalList; - } -} - -public class TrueFalseToXConverter : ConverterBase -{ - public override object StringToField(string o) - { - return o.Equals("X", StringComparison.InvariantCultureIgnoreCase); - } - - public override string FieldToString(object o) - { - return Convert.ToBoolean(o) ? "X" : string.Empty; - } - -} - -public class EmptyGuidConverter : ConverterBase -{ - public override object StringToField(string o) - { - return o.Equals(Guid.Empty.ToString(), StringComparison.InvariantCultureIgnoreCase); - } - - public override string FieldToString(object o) - { - var x = Guid.Parse(o.ToString() ?? ""); - return x == Guid.Empty ? "" : x.ToString(); - } -} \ No newline at end of file diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Services/NpcService.cs b/src/Ghosts.Api/Areas/Animator/Infrastructure/Services/NpcService.cs deleted file mode 100644 index 7141118d..00000000 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Services/NpcService.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Ghosts.Animator; -using Ghosts.Animator.Models; -using ghosts.api.Areas.Animator.Infrastructure.Models; -using Ghosts.Api.Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using NLog; - -namespace ghosts.api.Areas.Animator.Infrastructure.Services; - -public interface INpcService -{ - public Task> GetAll(); - public Task> GetEnclave(string campaign, string enclave); - public Task> GetListAsync(); - public Task> GetTeam(string campaign, string enclave, string team); - public Task GetById(Guid id); - public Task Create(NpcProfile npcProfile, bool generate); - public Task DeleteById(Guid id); -} - -public class NpcService : INpcService -{ - private static readonly Logger _log = LogManager.GetCurrentClassLogger(); - private readonly ApplicationDbContext _context; - - public NpcService(ApplicationDbContext context) - { - _context = context; - } - - public async Task> GetAll() - { - return await this._context.Npcs.ToListAsync(); - } - - public async Task> GetEnclave(string campaign, string enclave) - { - return await _context.Npcs.Where(x => x.Campaign == campaign && x.Enclave == enclave).ToListAsync(); - } - - public async Task> GetListAsync() - { - return await this._context.Npcs - .Select(item => new NpcNameId - { - Id = item.Id, - Name = $"{item.NpcProfile.Name.First} {item.NpcProfile.Name.Last}" - }) - .ToListAsync(); - } - - public async Task> GetListAsync(string campaign) - { - return await this._context.Npcs - .Where(x=>x.Campaign == campaign) - .Select(item => new NpcNameId - { - Id = item.Id, - Name = $"{item.NpcProfile.Name.First} {item.NpcProfile.Name.Last}" - }) - .ToListAsync(); - } - - public async Task> GetTeam(string campaign, string enclave, string team) - { - return await this._context.Npcs.Where(x => x.Campaign == campaign && x.Enclave == enclave && x.Team == team).ToListAsync(); - } - - public async Task GetById(Guid id) - { - return await this._context.Npcs.FirstOrDefaultAsync(x => x.Id == id); - } - - public async Task Create(NpcProfile npcProfile, bool generate) - { - NpcRecord npc; - if (generate) - { - npc = NpcRecord.TransformToNpc(Npc.Generate(MilitaryUnits.GetServiceBranch())); - npc.NpcProfile.Name = npcProfile.Name; - npc.NpcProfile.Email = npcProfile.Email; - } - else - { - npc = NpcRecord.TransformToNpc(npcProfile); - } - - npc.NpcProfile.Id = Guid.NewGuid(); - npc.NpcProfile.Created = DateTime.UtcNow; - npc.Id = npc.NpcProfile.Id; - - this._context.Npcs.Add(npc); - await this._context.SaveChangesAsync(); - return npc; - } - - public async Task DeleteById(Guid id) - { - var o = await this._context.Npcs.FindAsync(id); - if (o != null) - { - this._context.Npcs.Remove(o); - await this._context.SaveChangesAsync(); - } - } -} \ No newline at end of file diff --git a/src/Ghosts.Api/Areas/Animator/Views/Home/Index.cshtml b/src/Ghosts.Api/Areas/Animator/Views/Home/Index.cshtml deleted file mode 100644 index 55fbe913..00000000 --- a/src/Ghosts.Api/Areas/Animator/Views/Home/Index.cshtml +++ /dev/null @@ -1,5 +0,0 @@ -
-
-

Welcome to GHOSTS ANIMATOR.

-
-
diff --git a/src/Ghosts.Api/Areas/Animator/Views/_ViewStart.cshtml b/src/Ghosts.Api/Areas/Animator/Views/_ViewStart.cshtml deleted file mode 100644 index c34f4d58..00000000 --- a/src/Ghosts.Api/Areas/Animator/Views/_ViewStart.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@{ - Layout = "~/Views/Shared/_Layout.cshtml"; -} \ No newline at end of file diff --git a/src/Ghosts.Api/Areas/Animator/Controllers/AnimationsController.cs b/src/Ghosts.Api/Controllers/AnimationsController.cs similarity index 90% rename from src/Ghosts.Api/Areas/Animator/Controllers/AnimationsController.cs rename to src/Ghosts.Api/Controllers/AnimationsController.cs index 2c831323..047e61cf 100644 --- a/src/Ghosts.Api/Areas/Animator/Controllers/AnimationsController.cs +++ b/src/Ghosts.Api/Controllers/AnimationsController.cs @@ -1,15 +1,16 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + using System.Threading; using Ghosts.Api; -using ghosts.api.Areas.Animator.Infrastructure.Animations; using Ghosts.Api.Infrastructure; +using ghosts.api.Infrastructure.Animations; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using NLog; -namespace ghosts.api.Areas.Animator.Controllers; +namespace ghosts.api.Controllers; -[Area("Animator")] -[Route("animator/[controller]")] +[Route("[controller]")] [ApiExplorerSettings(IgnoreApi = true)] public class AnimationsController : Controller { diff --git a/src/Ghosts.Api/Controllers/Api/AnimationJobsController.cs b/src/Ghosts.Api/Controllers/Api/AnimationJobsController.cs new file mode 100644 index 00000000..03eee64f --- /dev/null +++ b/src/Ghosts.Api/Controllers/Api/AnimationJobsController.cs @@ -0,0 +1,44 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + +using System; +using ghosts.api.Infrastructure.Animations; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; + +namespace ghosts.api.Controllers.Api; + +[Route("animations")] +[Route("api/[controller]")] +[Produces("application/json")] +public class AnimationJobsController : Controller +{ + private readonly AnimationsManager _animationsManager; + + public AnimationJobsController(IServiceProvider serviceProvider) + { + _animationsManager = serviceProvider.GetRequiredService() as AnimationsManager; + } + // + // [SwaggerOperation("AnimationStart")] + // [HttpGet("start")] + // public async Task Start(CancellationToken cancellationToken) + // { + // await _animationsManager.StartAsync(cancellationToken); + // return Ok(); + // } + // + // [SwaggerOperation("AnimationStop")] + // [HttpGet("stop")] + // public async Task Stop(CancellationToken cancellationToken) + // { + // await _animationsManager.StopAsync(cancellationToken); + // return Ok(); + // } + // + // [SwaggerOperation("AnimationGetStatus")] + // [HttpGet("status")] + // public IActionResult Status(CancellationToken cancellationToken) + // { + // return Ok(_animationsManager.GetRunningJobs()); + // } +} \ No newline at end of file diff --git a/src/Ghosts.Api/Controllers/ClientIdController.cs b/src/Ghosts.Api/Controllers/Api/ClientIdController.cs similarity index 67% rename from src/Ghosts.Api/Controllers/ClientIdController.cs rename to src/Ghosts.Api/Controllers/Api/ClientIdController.cs index df2b0187..ed0dde97 100755 --- a/src/Ghosts.Api/Controllers/ClientIdController.cs +++ b/src/Ghosts.Api/Controllers/Api/ClientIdController.cs @@ -6,8 +6,9 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using NLog; +using Swashbuckle.AspNetCore.Annotations; -namespace Ghosts.Api.Controllers +namespace ghosts.api.Controllers.Api { /// /// GHOSTS CLIENT CONTROLLER @@ -31,22 +32,29 @@ public ClientIdController(IMachineService service) /// /// Cancellation Token /// A client's particular unique GHOSTS system ID (GUID) + [SwaggerOperation("ClientIdGet")] [HttpGet] public async Task Index(CancellationToken ct) { - var id = Request.Headers["ghosts-id"]; - _log.Trace($"Request by {id}"); + if (!Request.Headers.TryGetValue("ghosts-id", out var id)) + { + _log.Warn("Request missing ghosts-id header"); + return BadRequest("Missing ghosts-id header"); + } + + _log.Info($"Request by {id}"); - var findMachineResponse = await this._service.FindOrCreate(HttpContext, ct); + var findMachineResponse = await _service.FindOrCreate(HttpContext, ct); if (!findMachineResponse.IsValid()) { + _log.Error($"FindOrCreate failed for {id}: {findMachineResponse.Error}"); return StatusCode(StatusCodes.Status401Unauthorized, findMachineResponse.Error); } - var m = findMachineResponse.Machine; + var machineId = findMachineResponse.Machine.Id; //client saves this for future calls - return Json(m.Id); + return Ok(machineId); } } -} \ No newline at end of file +} diff --git a/src/Ghosts.Api/Controllers/ClientResultsController.cs b/src/Ghosts.Api/Controllers/Api/ClientResultsController.cs similarity index 60% rename from src/Ghosts.Api/Controllers/ClientResultsController.cs rename to src/Ghosts.Api/Controllers/Api/ClientResultsController.cs index ea678fca..95849b00 100755 --- a/src/Ghosts.Api/Controllers/ClientResultsController.cs +++ b/src/Ghosts.Api/Controllers/Api/ClientResultsController.cs @@ -12,8 +12,9 @@ using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using NLog; +using Swashbuckle.AspNetCore.Annotations; -namespace Ghosts.Api.Controllers +namespace ghosts.api.Controllers.Api { /// /// GHOSTS CLIENT CONTROLLER @@ -38,28 +39,32 @@ public ClientResultsController(IBackgroundQueue service) /// The encrypted timeline or health log payload /// Cancellation Token /// 204 No Content on success + [SwaggerOperation("ClientResultsCreateSecure")] [HttpPost("secure")] public IActionResult Secure([FromBody] EncryptedPayload transmission, CancellationToken ct) { - string raw; - try { - var key = Request.Headers["ghosts-name"].ToString(); - //decrypt + if (!Request.Headers.TryGetValue("ghosts-name", out var key)) + { + _log.Warn("Request missing ghosts-name header"); + return BadRequest("Missing ghosts-name header"); + } + + // Decrypt transmission.Payload = Base64Encoder.Base64Decode(transmission.Payload); - raw = Crypto.DecryptStringAes(transmission.Payload, key); + var raw = Crypto.DecryptStringAes(transmission.Payload, key); + + // Deserialize + var value = JsonConvert.DeserializeObject(raw); + + return Process(HttpContext, Request, value, ct); } catch (Exception exc) { - _log.Trace(exc); - throw new Exception("Malformed data"); + _log.Error(exc, "Malformed data"); + return BadRequest("Malformed data"); } - - //deserialize - var value = JsonConvert.DeserializeObject(raw); - - return Process(HttpContext, Request, value, ct); } /// @@ -68,48 +73,49 @@ public IActionResult Secure([FromBody] EncryptedPayload transmission, Cancellati /// Timeline or health log payload /// Cancellation Token /// 204 No Content on success + [SwaggerOperation("ClientResultsCreate")] [HttpPost] public IActionResult Index([FromBody] TransferLogDump value, CancellationToken ct) { return Process(HttpContext, Request, value, ct); } - // ReSharper disable once UnusedParameter.Local private IActionResult Process(HttpContext context, HttpRequest request, TransferLogDump value, CancellationToken ct) { - var id = request.Headers["ghosts-id"]; + if (!Request.Headers.TryGetValue("ghosts-id", out var id)) + { + _log.Warn("Request missing ghosts-id header"); + return Unauthorized("Missing ghosts-id header"); + } - var m = WebRequestReader.GetMachine(HttpContext); + var m = WebRequestReader.GetMachine(context); if (!string.IsNullOrEmpty(id)) { m.Id = new Guid(id); } - else + else if (!m.IsValid()) { - if (!m.IsValid()) - return StatusCode(StatusCodes.Status401Unauthorized, "Invalid machine request"); + return Unauthorized("Invalid machine request"); } if (value is not null && !string.IsNullOrEmpty(value.Log)) { - _log.Trace($"payload received: {value.Log}"); + _log.Info($"Payload received: {value.Log}"); - _service.Enqueue( - new QueueEntry + _service.Enqueue(new QueueEntry + { + Payload = new MachineQueueEntry { - Payload = - new MachineQueueEntry - { - Machine = m, - LogDump = value, - HistoryType = Machine.MachineHistoryItem.HistoryType.PostedResults - }, - Type = QueueEntry.Types.Machine - }); + Machine = m, + LogDump = value, + HistoryType = Machine.MachineHistoryItem.HistoryType.PostedResults + }, + Type = QueueEntry.Types.Machine + }); } return NoContent(); } } -} \ No newline at end of file +} diff --git a/src/Ghosts.Api/Controllers/ClientSurveyController.cs b/src/Ghosts.Api/Controllers/Api/ClientSurveyController.cs similarity index 64% rename from src/Ghosts.Api/Controllers/ClientSurveyController.cs rename to src/Ghosts.Api/Controllers/Api/ClientSurveyController.cs index 0b23b57d..def3e306 100755 --- a/src/Ghosts.Api/Controllers/ClientSurveyController.cs +++ b/src/Ghosts.Api/Controllers/Api/ClientSurveyController.cs @@ -11,8 +11,9 @@ using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using NLog; +using Swashbuckle.AspNetCore.Annotations; -namespace ghosts.api.Controllers +namespace ghosts.api.Controllers.Api { /// /// GHOSTS CLIENT CONTROLLER @@ -37,28 +38,32 @@ public ClientSurveyController(IBackgroundQueue service) /// The encrypted survey result /// Cancellation Token /// 204 No Content on success + [SwaggerOperation("ClientSurveyCreateSecure")] [HttpPost("secure")] public IActionResult Secure([FromBody] EncryptedPayload transmission, CancellationToken ct) { - string raw; - try { - var key = Request.Headers["ghosts-name"].ToString(); - //decrypt - the headers must be the same as encrypted with the client + if (!Request.Headers.TryGetValue("ghosts-name", out var key)) + { + _log.Warn("Request missing ghosts-name header"); + return BadRequest("Missing ghosts-name header"); + } + + // Decrypt transmission.Payload = Base64Encoder.Base64Decode(transmission.Payload); - raw = Crypto.DecryptStringAes(transmission.Payload, key); + var raw = Crypto.DecryptStringAes(transmission.Payload, key); + + // Deserialize + var value = JsonConvert.DeserializeObject(raw); + + return Process(HttpContext, Request, value, ct); } catch (Exception exc) { - _log.Trace(exc); - throw new Exception("Malformed data"); + _log.Error(exc, "Malformed data"); + return BadRequest("Malformed data"); } - - //deserialize - var value = JsonConvert.DeserializeObject(raw); - - return Process(HttpContext, Request, value, ct); } /// @@ -67,6 +72,7 @@ public IActionResult Secure([FromBody] EncryptedPayload transmission, Cancellati /// The client survey result /// Cancellation Token /// 204 No Content on success + [SwaggerOperation("ClientSurveyCreate")] [HttpPost] public IActionResult Index([FromBody] Survey value, CancellationToken ct) { @@ -75,29 +81,32 @@ public IActionResult Index([FromBody] Survey value, CancellationToken ct) private IActionResult Process(HttpContext context, HttpRequest request, Survey value, CancellationToken ct) { - var id = request.Headers["ghosts-id"]; + if (!Request.Headers.TryGetValue("ghosts-id", out var id)) + { + _log.Warn("Request missing ghosts-id header"); + return Unauthorized("Missing ghosts-id header"); + } - _log.Trace($"Request by {id}"); + _log.Info($"Request by {id}"); - var m = WebRequestReader.GetMachine(HttpContext); + var m = WebRequestReader.GetMachine(context); if (!string.IsNullOrEmpty(id)) { m.Id = new Guid(id); } - else + else if (!m.IsValid()) { - if (!m.IsValid()) - return StatusCode(StatusCodes.Status401Unauthorized, "Invalid machine request"); + return Unauthorized("Invalid machine request"); } value.MachineId = m.Id; if (value.Created == DateTime.MinValue) value.Created = DateTime.UtcNow; - _service.Enqueue(new QueueEntry {Type = QueueEntry.Types.Survey, Payload = value}); + _service.Enqueue(new QueueEntry { Type = QueueEntry.Types.Survey, Payload = value }); return NoContent(); } } -} \ No newline at end of file +} diff --git a/src/Ghosts.Api/Controllers/ClientTimeline.cs b/src/Ghosts.Api/Controllers/Api/ClientTimeline.cs similarity index 73% rename from src/Ghosts.Api/Controllers/ClientTimeline.cs rename to src/Ghosts.Api/Controllers/Api/ClientTimeline.cs index f3d3e654..324cd309 100644 --- a/src/Ghosts.Api/Controllers/ClientTimeline.cs +++ b/src/Ghosts.Api/Controllers/Api/ClientTimeline.cs @@ -6,12 +6,12 @@ using Ghosts.Api.Infrastructure; using ghosts.api.Infrastructure.Services; using Ghosts.Domain; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using NLog; +using Swashbuckle.AspNetCore.Annotations; -namespace ghosts.api.Controllers +namespace ghosts.api.Controllers.Api { /// /// GHOSTS CLIENT CONTROLLER @@ -38,12 +38,17 @@ public ClientTimelineController(IMachineTimelinesService service, IMachineServic /// The client's current timeline /// Cancellation Token /// The saved timeline record + [SwaggerOperation("ClientTimelineCreate")] [HttpPost] public async Task Index([FromBody] string timeline, CancellationToken ct) { - var id = Request.Headers["ghosts-id"]; + if (!Request.Headers.TryGetValue("ghosts-id", out var id)) + { + _log.Warn("Request missing ghosts-id header"); + return Unauthorized("Missing ghosts-id header"); + } - _log.Trace($"Request by {id}"); + _log.Info($"Request by {id}"); var m = WebRequestReader.GetMachine(HttpContext); @@ -52,12 +57,9 @@ public async Task Index([FromBody] string timeline, CancellationT m.Id = new Guid(id); await _machineService.CreateAsync(m, ct); } - else + else if (!m.IsValid()) { - if (!m.IsValid()) - { - return StatusCode(StatusCodes.Status401Unauthorized, "Invalid machine request"); - } + return Unauthorized("Invalid machine request"); } Timeline tl; @@ -68,11 +70,12 @@ public async Task Index([FromBody] string timeline, CancellationT } catch (Exception e) { - _log.Error(e); - return StatusCode(StatusCodes.Status400BadRequest, "Invalid timeline file"); + _log.Error(e, "Invalid timeline file"); + return BadRequest("Invalid timeline file"); } - return Ok(await _service.CreateAsync(m, tl, ct)); + var createdTimeline = await _service.CreateAsync(m, tl, ct); + return Ok(createdTimeline); } } -} \ No newline at end of file +} diff --git a/src/Ghosts.Api/Controllers/ClientUpdatesController.cs b/src/Ghosts.Api/Controllers/Api/ClientUpdatesController.cs similarity index 94% rename from src/Ghosts.Api/Controllers/ClientUpdatesController.cs rename to src/Ghosts.Api/Controllers/Api/ClientUpdatesController.cs index e0b15f38..498d59b7 100755 --- a/src/Ghosts.Api/Controllers/ClientUpdatesController.cs +++ b/src/Ghosts.Api/Controllers/Api/ClientUpdatesController.cs @@ -9,8 +9,9 @@ using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json.Linq; using NLog; +using Swashbuckle.AspNetCore.Annotations; -namespace Ghosts.Api.Controllers +namespace ghosts.api.Controllers.Api { /// /// GHOSTS CLIENT CONTROLLER @@ -41,10 +42,11 @@ public ClientUpdatesController(IMachineService machineService, IMachineUpdateSer /// Returns json payload of a particular update /// Unauthorized or incorrectly formatted machine request /// No Update - [HttpGet] [ProducesResponseType(200)] [ProducesResponseType(401)] [ProducesResponseType(404)] + [SwaggerOperation("ClientUpdatesCreate")] + [HttpGet] public async Task Index(CancellationToken ct) { var id = Request.Headers["ghosts-id"]; diff --git a/src/Ghosts.Api/Controllers/InstallController.cs b/src/Ghosts.Api/Controllers/Api/InstallController.cs similarity index 83% rename from src/Ghosts.Api/Controllers/InstallController.cs rename to src/Ghosts.Api/Controllers/Api/InstallController.cs index 68667829..d5c7f504 100644 --- a/src/Ghosts.Api/Controllers/InstallController.cs +++ b/src/Ghosts.Api/Controllers/Api/InstallController.cs @@ -1,8 +1,9 @@ using System.IO; using System.Linq; using Microsoft.AspNetCore.Mvc; +using Swashbuckle.AspNetCore.Annotations; -namespace ghosts.api.Controllers; +namespace ghosts.api.Controllers.Api; /// /// For installing client binaries from the api @@ -10,6 +11,7 @@ namespace ghosts.api.Controllers; [Route("/install")] public class InstallController : Controller { + [SwaggerOperation("InstallersGetWindows64")] [HttpGet("client/windows")] [HttpGet("client/windows/x64")] public IActionResult Windows64Client() @@ -17,12 +19,14 @@ public IActionResult Windows64Client() return GetFile("config/binaries/windows/x64"); } + [SwaggerOperation("InstallersGetWindows32")] [HttpGet("client/windows/x32")] public IActionResult Windows32Client() { return GetFile("config/binaries/windows/x32"); } + [SwaggerOperation("InstallersGetLinux")] [HttpGet("client/linux")] public IActionResult LinuxClient() { diff --git a/src/Ghosts.Api/Controllers/MachineGroupsController.cs b/src/Ghosts.Api/Controllers/Api/MachineGroupsController.cs similarity index 60% rename from src/Ghosts.Api/Controllers/MachineGroupsController.cs rename to src/Ghosts.Api/Controllers/Api/MachineGroupsController.cs index 79b0add1..713309f2 100644 --- a/src/Ghosts.Api/Controllers/MachineGroupsController.cs +++ b/src/Ghosts.Api/Controllers/Api/MachineGroupsController.cs @@ -3,17 +3,20 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Threading; using System.Threading.Tasks; +using Ghosts.Api.Infrastructure.Extensions; using ghosts.api.Infrastructure.Models; using ghosts.api.Infrastructure.Services; using Microsoft.AspNetCore.Mvc; using NLog; +using Swashbuckle.AspNetCore.Annotations; -namespace Ghosts.Api.Controllers +namespace ghosts.api.Controllers.Api { [Produces("application/json")] - [Route("api/machinegroups")] + [Route("api/[controller]")] [ResponseCache(Duration = 5)] public class MachineGroupsController : Controller { @@ -33,6 +36,7 @@ public MachineGroupsController(IMachineGroupService service, IMachineService mac /// Query /// Cancellation Token /// Group information + [SwaggerOperation("MachineGroupsGetByQuery")] [HttpGet] public async Task> GetMachineGroup(string q, CancellationToken ct) { @@ -45,14 +49,23 @@ public async Task> GetMachineGroup(string q, CancellationToke /// Group Id /// Cancellation Token /// Group information + [SwaggerOperation("MachineGroupsGetById")] [HttpGet("{id}")] public async Task GetMachineGroup([FromRoute] int id, CancellationToken ct) { - if (!ModelState.IsValid) return BadRequest(ModelState); + if (!ModelState.IsValid) + { + _log.Warn("Invalid model state"); + return BadRequest(ModelState); + } var machineGroup = await _service.GetAsync(id, ct); + if (machineGroup == null) + { + _log.Info($"Group with id {id} not found"); + return NotFound(); + } - if (machineGroup == null) return NotFound(); return Ok(machineGroup); } @@ -62,12 +75,25 @@ public async Task GetMachineGroup([FromRoute] int id, Cancellatio /// The group to update /// Cancellation Token /// The updated group + [SwaggerOperation("MachineGroupsUpdate")] [HttpPut("{id}")] public async Task PutMachineGroup([FromBody] Group model, CancellationToken ct) { - if (!ModelState.IsValid) return BadRequest(ModelState); + if (!ModelState.IsValid || model.ContainsInvalidUnicode()) + { + _log.Warn("Invalid model state"); + return BadRequest(ModelState); + } + + // if trying to update something that doesn't exist, create it instead + if (await _service.GetAsync(model.Id, ct) == null) + { + var id = await _service.CreateAsync(model, ct); + return CreatedAtAction(nameof(GetMachineGroup), new { id }, model); + } await _service.UpdateAsync(model, ct); + _log.Info($"Group with id {model.Id} updated"); return Ok(model); } @@ -78,14 +104,29 @@ public async Task PutMachineGroup([FromBody] Group model, Cancell /// The new group to add /// Cancellation Token /// The saved group + [ProducesResponseType((int)HttpStatusCode.OK)] + [SwaggerResponse((int)HttpStatusCode.OK)] + [SwaggerOperation("MachineGroupsCreate")] [HttpPost] public async Task PostMachineGroup([FromBody] Group model, CancellationToken ct) { - if (!ModelState.IsValid) return BadRequest(ModelState); + if (!ModelState.IsValid + || model.ContainsInvalidUnicode() + || model.Status == StatusType.Deleted + || model.GroupMachines == null) + { + _log.Warn("Invalid model state"); + return BadRequest(ModelState); + } + // does group exist? + if(await _service.GetAsync(model.Id, ct) != null) + return CreatedAtAction(nameof(GetMachineGroup), new { model.Id }, model); + var id = await _service.CreateAsync(model, ct); + _log.Info($"Group with id {id} created"); - return CreatedAtAction("GetMachineGroup", new {id}, model); + return CreatedAtAction(nameof(GetMachineGroup), new { id }, model); } /// @@ -94,13 +135,19 @@ public async Task PostMachineGroup([FromBody] Group model, Cancel /// The group to delete /// Cancellation Token /// 204 No Content + [SwaggerOperation("MachineGroupsDeleteById")] [HttpDelete("{id}")] [ResponseCache(Duration = 0)] public async Task DeleteMachineGroup([FromRoute] int id, CancellationToken ct) { - if (!ModelState.IsValid) return BadRequest(ModelState); + if (!ModelState.IsValid) + { + _log.Warn("Invalid model state"); + return BadRequest(ModelState); + } await _service.DeleteAsync(id, ct); + _log.Info($"Group with id {id} deleted"); return NoContent(); } @@ -113,6 +160,7 @@ public async Task DeleteMachineGroup([FromRoute] int id, Cancella /// How many records to return /// Cancellation token /// The activity for the group + [SwaggerOperation("MachineGroupsGetActivityById")] [HttpGet("{id}/activity")] public async Task Activity([FromRoute] int id, int skip, int take, CancellationToken ct) { @@ -123,7 +171,8 @@ public async Task Activity([FromRoute] int id, int skip, int take } catch (Exception exc) { - return Json(exc); + _log.Error(exc, $"Error getting activity for group {id}"); + return StatusCode(500, "Internal server error"); } } @@ -133,16 +182,30 @@ public async Task Activity([FromRoute] int id, int skip, int take /// Group Id /// Cancellation Token /// Health records for machines in the group + [SwaggerOperation("MachineGroupsGetHealthById")] [HttpGet("{id}/health")] public async Task GetGroup([FromRoute] int id, CancellationToken ct) { - if (!ModelState.IsValid) return BadRequest(ModelState); - - var list = new List(); + if (!ModelState.IsValid) + { + _log.Warn("Invalid model state"); + return BadRequest(ModelState); + } var group = await _service.GetAsync(id, ct); + if (group == null) + { + _log.Info($"Group with id {id} not found"); + return NotFound(); + } + + var list = new List(); + foreach (var machine in group.GroupMachines) + { + list.AddRange(await _serviceMachine.GetMachineHistory(machine.MachineId, ct)); + } - foreach (var machine in group.GroupMachines) list.AddRange(await _serviceMachine.GetMachineHistory(machine.MachineId, ct)); + _log.Info($"Health records retrieved for group {id}"); return Ok(list.OrderByDescending(o => o.CreatedUtc)); } diff --git a/src/Ghosts.Api/Controllers/Api/MachineUpdatesController.cs b/src/Ghosts.Api/Controllers/Api/MachineUpdatesController.cs new file mode 100755 index 00000000..ea572f0c --- /dev/null +++ b/src/Ghosts.Api/Controllers/Api/MachineUpdatesController.cs @@ -0,0 +1,182 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + +using System; +using System.Threading; +using System.Threading.Tasks; +using ghosts.api.Infrastructure; +using ghosts.api.Infrastructure.Models; +using ghosts.api.Infrastructure.Services; +using Ghosts.Api.ViewModels; +using Ghosts.Domain; +using Microsoft.AspNetCore.Mvc; +using NLog; +using Swashbuckle.AspNetCore.Annotations; +using Swashbuckle.AspNetCore.Filters; + +namespace ghosts.api.Controllers.Api +{ + /// + /// Enter a machine command, so that the next time a machine checks in, + /// it executes the indicated MachineUpdate.Type command + /// + [Produces("application/json")] + [Route("api/[controller]")] + public class MachineUpdatesController : Controller + { + private static readonly Logger _log = LogManager.GetCurrentClassLogger(); + private readonly IMachineUpdateService _updateService; + + public MachineUpdatesController(IMachineUpdateService updateService) + { + _updateService = updateService; + } + + /// + /// Sends a command for machine to perform, + /// e.g. health or timeline updates, or post back current timeline + /// + /// The saved MachineUpdate record + [ProducesResponseType(200)] + [ProducesResponseType(401)] + [ProducesResponseType(404)] + [SwaggerRequestExample(typeof(MachineUpdate), typeof(MachineUpdateExample))] + [SwaggerOperation("MachineUpdatesCreate")] + [HttpPost] + public async Task Create([FromBody] MachineUpdate machineUpdate, CancellationToken ct) + { + if (!ModelState.IsValid) + { + _log.Warn("Invalid model state"); + return BadRequest(ModelState); + } + + try + { + var result = await _updateService.CreateAsync(machineUpdate, ct); + _log.Info($"Machine update created with ID {result.Id}"); + return Ok(result); + } + catch (Exception e) + { + _log.Error(e, "Error creating machine update"); + return StatusCode(500, "Internal server error"); + } + } + + /// + /// Send a new timeline to an entire group of machines + /// + /// Group Id + /// The update to send + /// Cancellation token + /// 204 No content + [SwaggerOperation("MachineUpdateGroupUpdate")] + [HttpPost("group/{groupId}")] + public async Task GroupUpdate([FromRoute] int groupId, [FromBody] MachineUpdateViewModel machineUpdate, CancellationToken ct) + { + if (!ModelState.IsValid) + { + _log.Warn("Invalid model state"); + return BadRequest(ModelState); + } + + try + { + await _updateService.UpdateGroupAsync(groupId, machineUpdate, ct); + _log.Info($"Group update sent to group ID {groupId}"); + return NoContent(); + } + catch (Exception e) + { + _log.Error(e, $"Error updating group ID {groupId}"); + return StatusCode(500, "Internal server error"); + } + } + + /// + /// Gets a machine update by its ID + /// + /// Update ID + /// Cancellation token + /// The machine update + [SwaggerOperation("MachineUpdatesGetById")] + [HttpGet("{updateId}")] + public async Task GetById([FromRoute] int updateId, CancellationToken ct) + { + if (!ModelState.IsValid) + { + _log.Warn("Invalid model state"); + return BadRequest(ModelState); + } + + var update = await _updateService.GetById(updateId, ct); + if (update == null) + { + _log.Info($"Machine update with ID {updateId} not found"); + return NotFound(); + } + + return Ok(update); + } + + /// + /// Gets machine updates by machine ID + /// + /// Machine ID + /// Cancellation token + /// List of machine updates + [SwaggerOperation("MachineUpdatesGetByMachineId")] + [HttpGet("machine/{machineId}")] + public async Task GetByMachineId([FromRoute] Guid machineId, CancellationToken ct) + { + if (!ModelState.IsValid) + { + _log.Warn("Invalid model state"); + return BadRequest(ModelState); + } + + var updates = await _updateService.GetByMachineId(machineId, ct); + return Ok(updates); + } + + /// + /// Gets machine updates by type + /// + /// Update type + /// Cancellation token + /// List of machine updates + [SwaggerOperation("MachineUpdatesGetByType")] + [HttpGet("type/{type}")] + public async Task GetByType([FromRoute] UpdateClientConfig.UpdateType type, CancellationToken ct) + { + if (!ModelState.IsValid) + { + _log.Warn("Invalid model state"); + return BadRequest(ModelState); + } + + var updates = await _updateService.GetByType(type, ct); + return Ok(updates); + } + + /// + /// Gets machine updates by status + /// + /// Update status + /// Cancellation token + /// List of machine updates + [SwaggerOperation("MachineUpdatesGetByStatus")] + [HttpGet("status/{status}")] + public async Task GetByStatus([FromRoute] StatusType status, CancellationToken ct) + { + if (!ModelState.IsValid) + { + _log.Warn("Invalid model state"); + return BadRequest(ModelState); + } + + var updates = await _updateService.GetByStatus(status, ct); + return Ok(updates); + } + } +} diff --git a/src/Ghosts.Api/Controllers/MachinesController.cs b/src/Ghosts.Api/Controllers/Api/MachinesController.cs similarity index 85% rename from src/Ghosts.Api/Controllers/MachinesController.cs rename to src/Ghosts.Api/Controllers/Api/MachinesController.cs index 870a7f40..d50d420f 100644 --- a/src/Ghosts.Api/Controllers/MachinesController.cs +++ b/src/Ghosts.Api/Controllers/Api/MachinesController.cs @@ -6,8 +6,10 @@ using ghosts.api.Infrastructure.Models; using ghosts.api.Infrastructure.Services; using Microsoft.AspNetCore.Mvc; +using NLog; +using Swashbuckle.AspNetCore.Annotations; -namespace Ghosts.Api.Controllers +namespace ghosts.api.Controllers.Api { [Produces("application/json")] [Route("api/[controller]")] @@ -15,6 +17,7 @@ namespace Ghosts.Api.Controllers public class MachinesController : Controller { private readonly IMachineService _service; + private static readonly Logger _log = LogManager.GetCurrentClassLogger(); public MachinesController(IMachineService service) { @@ -27,6 +30,7 @@ public MachinesController(IMachineService service) /// Query /// Cancellation Token /// Machine information + [SwaggerOperation("MachinesGetByQuery")] [HttpGet] public async Task GetMachines(string q, CancellationToken ct) { @@ -41,8 +45,8 @@ public async Task GetMachines(string q, CancellationToken ct) /// /// Cancellation Token /// All machine records - [HttpGet] - [Route("list")] + [SwaggerOperation("MachinesGet")] + [HttpGet("list")] public IActionResult GetList(CancellationToken ct) { return Ok(_service.GetList()); @@ -54,6 +58,7 @@ public IActionResult GetList(CancellationToken ct) /// Machine Guid /// Cancellation Token /// Machine record + [SwaggerOperation("MachinesGetById")] [HttpGet("{id}")] public async Task GetMachine([FromRoute] Guid id, CancellationToken ct) { @@ -72,6 +77,7 @@ public async Task GetMachine([FromRoute] Guid id, CancellationTok /// The machine record to update /// Cancellation Token /// The updated machine record + [SwaggerOperation("MachinesUpdate")] [HttpPut("{id}")] public async Task PutMachine([FromBody] Machine machine, CancellationToken ct) { @@ -89,14 +95,15 @@ public async Task PutMachine([FromBody] Machine machine, Cancella /// The machine to create /// Cancellation Token /// + [SwaggerOperation("MachinesCreate")] [HttpPost] public async Task PostMachine([FromBody] Machine machine, CancellationToken ct) { - if (!ModelState.IsValid) return BadRequest(ModelState); + if (!ModelState.IsValid || !machine.IsValid()) return BadRequest(ModelState); var id = await _service.CreateAsync(machine, ct); - return CreatedAtAction("GetMachine", new {id}, machine); + return CreatedAtAction(nameof(GetMachine), new { id }, machine); } /// @@ -105,8 +112,9 @@ public async Task PostMachine([FromBody] Machine machine, Cancell /// The Id of the machine to delete /// Cancellation Token /// 204 No Content - [HttpDelete("{id}")] [ResponseCache(Duration = 0)] + [SwaggerOperation("MachinesDeleteById")] + [HttpDelete("{id}")] public async Task DeleteMachine([FromRoute] Guid id, CancellationToken ct) { if (!ModelState.IsValid || id == Guid.Empty) return BadRequest(ModelState); @@ -123,6 +131,7 @@ public async Task DeleteMachine([FromRoute] Guid id, Cancellation /// How many records to return /// Cancellation Token /// The activity history for the requested machine + [SwaggerOperation("MachinesGetActivityById")] [HttpGet("{id}/activity")] public async Task Activity([FromRoute] Guid id, int skip, int take, CancellationToken ct) { @@ -135,7 +144,9 @@ public async Task Activity([FromRoute] Guid id, int skip, int tak } catch (Exception exc) { - return Json(exc); + // Log the exception and return a 500 status code + _log.Error(exc, $"An error occurred while fetching activity for machine {id}"); + return StatusCode(500, "Internal server error"); } } @@ -145,6 +156,7 @@ public async Task Activity([FromRoute] Guid id, int skip, int tak /// Machine Guid /// Cancellation Token /// Health records for the machine + [SwaggerOperation("MachinesGetHealthById")] [HttpGet("{id}/health")] public async Task Health([FromRoute] Guid id, CancellationToken ct) { @@ -155,4 +167,4 @@ public async Task Health([FromRoute] Guid id, CancellationToken c return Ok(list); } } -} \ No newline at end of file +} diff --git a/src/Ghosts.Api/Areas/Animator/Controllers/Api/NpcsController.cs b/src/Ghosts.Api/Controllers/Api/NpcsController.cs similarity index 74% rename from src/Ghosts.Api/Areas/Animator/Controllers/Api/NpcsController.cs rename to src/Ghosts.Api/Controllers/Api/NpcsController.cs index 86ebb1e0..3616b5e4 100644 --- a/src/Ghosts.Api/Areas/Animator/Controllers/Api/NpcsController.cs +++ b/src/Ghosts.Api/Controllers/Api/NpcsController.cs @@ -13,14 +13,17 @@ using Ghosts.Animator.Extensions; using Ghosts.Animator.Models; using ghosts.api.Areas.Animator.Infrastructure.Models; -using ghosts.api.Areas.Animator.Infrastructure.Services; using Ghosts.Api.Infrastructure.Data; +using Ghosts.Api.Infrastructure.Extensions; +using ghosts.api.Infrastructure.Models; +using ghosts.api.Infrastructure.Services; +using Ghosts.Domain.Code; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Filters; -namespace ghosts.api.Areas.Animator.Controllers.Api; +namespace ghosts.api.Controllers.Api; [ApiController] [Produces("application/json")] @@ -40,26 +43,26 @@ public NpcsController(ApplicationDbContext context, INpcService service) /// Returns all generated NPCs in the system (caution, could return a very large amount of data) /// /// IEnumerable<NpcProfile> - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] - [SwaggerOperation("getNPCs")] + [ProducesResponseType(typeof(ActionResult>), (int)HttpStatusCode.OK)] + [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(ActionResult>))] + [SwaggerOperation("NpcsGetAll")] [HttpGet] - public async Task> Get() + public async Task>> NpcsGetAll() { - return await this._service.GetAll(); + return Ok(await this._service.GetAll()); } /// /// Returns name and Id for all NPCs in the system (caution, could return a large amount of data) /// /// IEnumerable<NpcNameId> - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] - [SwaggerOperation("getNPCList")] + [ProducesResponseType(typeof(ActionResult>), (int)HttpStatusCode.OK)] + [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(ActionResult>))] + [SwaggerOperation("NpcsGetList")] [HttpGet("list")] - public async Task> List() + public async Task>> NpcsGetList() { - return await this._service.GetListAsync(); + return Ok(await this._service.GetListAsync()); } /// @@ -67,13 +70,13 @@ public async Task> List() /// /// /// - [ProducesResponseType(typeof(NpcRecord), (int)HttpStatusCode.OK)] - [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(NpcRecord))] - [SwaggerOperation("getNPCById")] + [ProducesResponseType(typeof(ActionResult), (int)HttpStatusCode.OK)] + [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(ActionResult))] + [SwaggerOperation("NpcsGetById")] [HttpGet("{id:guid}")] - public async Task GetById(Guid id) + public async Task> NpcsGetById(Guid id) { - return await this._service.GetById(id); + return Ok(await this._service.GetById(id)); } /// @@ -83,9 +86,9 @@ public async Task GetById(Guid id) /// [ProducesResponseType((int)HttpStatusCode.OK)] [SwaggerResponse((int)HttpStatusCode.OK)] - [SwaggerOperation("deleteNPCById")] + [SwaggerOperation("NpcsDeleteById")] [HttpDelete("{id:guid}")] - public async Task DeleteById(Guid id) + public async Task NpcsDeleteById(Guid id) { await this._service.DeleteById(id); } @@ -97,47 +100,28 @@ public async Task DeleteById(Guid id) /// [ProducesResponseType(typeof(IActionResult), (int)HttpStatusCode.OK)] [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(IActionResult))] - [SwaggerOperation("getNpcAvatarById")] + [SwaggerOperation("NpcsGetAvatarById")] [HttpGet("{id:guid}/photo")] - public async Task GetPhotoById(Guid id) + public async Task NpcsGetAvatarById(Guid id) { - //get npc and find image - var npc = await this._service.GetById(id); + // Get NPC and find image + var npc = await _service.GetById(id); if (npc == null) return NotFound(); - //load image as stream - var stream = new FileStream(npc.NpcProfile.PhotoLink, FileMode.Open); - return File(stream, "image/jpg", $"{npc.NpcProfile.Name.ToString()!.Replace(" ", "_")}.jpg"); - } - /// - /// Create one NPC (handy for syncing up from ghosts core api) - /// - /// NPC Profile - [ProducesResponseType(typeof(NpcRecord), (int)HttpStatusCode.OK)] - [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(NpcRecord))] - [SwaggerOperation("createNpc")] - [HttpPost] - public async Task Create(NpcProfile npcProfile, bool generate) - { - NpcRecord npc; - if (generate) - { - npc = NpcRecord.TransformToNpc(Npc.Generate(MilitaryUnits.GetServiceBranch())); - npc.NpcProfile.Name = npcProfile.Name; - npc.NpcProfile.Email = npcProfile.Email; - } - else + // Determine the image path + var imagePath = string.IsNullOrEmpty(npc.NpcProfile.PhotoLink) + ? ApplicationDetails.ConfigurationFiles.DefaultNpcImage + : npc.NpcProfile.PhotoLink; + + // Check if the image file exists + if (!System.IO.File.Exists(imagePath)) { - npc = NpcRecord.TransformToNpc(npcProfile); + imagePath = ApplicationDetails.ConfigurationFiles.DefaultNpcImage; } - npc.NpcProfile.Id = Guid.NewGuid(); - npc.NpcProfile.Created = DateTime.UtcNow; - npc.Id = npc.NpcProfile.Id; - - this._context.Npcs.Add(npc); - await this._context.SaveChangesAsync(); - return npc; + // Load image as stream and return as file result + var stream = new FileStream(imagePath, FileMode.Open, FileAccess.Read); + return File(stream, "image/jpg", $"{npc.NpcProfile.Name.ToString()!.Replace(" ", "_")}.jpg"); } /// @@ -148,8 +132,14 @@ public async Task Create(NpcProfile npcProfile, bool generate) /// [HttpPost("npc/{npcId:guid}")] [Obsolete("Obsolete")] - public async Task GetNpcReduced(Guid npcId, [FromBody] string[] fieldsToReturn) + [SwaggerOperation("NpcsGetReducedFields")] + public async Task NpcsGetReducedFields(Guid npcId, [FromBody] string[] fieldsToReturn) { + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + var npc = await this._service.GetById(npcId); if (npc == null) return null; return new NPCReduced(fieldsToReturn, npc).PropertySelection; @@ -163,9 +153,9 @@ public async Task GetNpcReduced(Guid npcId, [FromBody] string[] fieldsTo /// [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] - [SwaggerOperation("getEnclave")] + [SwaggerOperation("NpcsEnclaveGet")] [HttpGet("{campaign}/{enclave}")] - public async Task> GetEnclave(string campaign, string enclave) + public async Task> NpcsEnclaveGet(string campaign, string enclave) { return await _service.GetEnclave(campaign, enclave); } @@ -178,12 +168,13 @@ public async Task> GetEnclave(string campaign, string enc /// [ProducesResponseType(typeof(IActionResult), (int)HttpStatusCode.OK)] [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(IActionResult))] - [SwaggerOperation("getEnclaveCsv")] + [SwaggerOperation("NpcsEnclaveGetCsv")] [HttpGet("{campaign}/{enclave}/csv")] - public async Task GetAsCsv(string campaign, string enclave) + [Obsolete("Obsolete")] + public async Task NpcsEnclaveGetCsv(string campaign, string enclave) { var engine = new FileHelperEngine(); - var list = await GetEnclave(campaign, enclave); + var list = await NpcsEnclaveGet(campaign, enclave); var filteredList = list.Select(n => new NPCToCsv { Id = n.Id, Email = n.NpcProfile.Email }).ToList(); @@ -204,11 +195,11 @@ public async Task GetAsCsv(string campaign, string enclave) /// [ProducesResponseType((int)HttpStatusCode.OK)] [SwaggerResponse((int)HttpStatusCode.OK)] - [SwaggerOperation("deleteEnclave")] + [SwaggerOperation("NpcsEnclaveDelete")] [HttpDelete("{campaign}/{enclave}")] - public async Task DeleteEnclave(string campaign, string enclave) + public async Task NpcsEnclaveDelete(string campaign, string enclave) { - var list = await GetEnclave(campaign, enclave); + var list = await NpcsEnclaveGet(campaign, enclave); this._context.Npcs.RemoveRange(list); await this._context.SaveChangesAsync(); } @@ -222,9 +213,10 @@ public async Task DeleteEnclave(string campaign, string enclave) /// [HttpPost("{campaign}/{enclave}/custom")] [Obsolete("Obsolete")] - public async Task GetReducedNpcs(string campaign, string enclave, [FromBody] string[] fieldsToReturn) + [SwaggerOperation("NpcsEnclaveReducedFields")] + public async Task NpcsEnclaveReducedFields(string campaign, string enclave, [FromBody] string[] fieldsToReturn) { - var npcList = await GetEnclave(campaign, enclave); + var npcList = await NpcsEnclaveGet(campaign, enclave); var npcDetails = new Dictionary>(); foreach (var npc in npcList) @@ -253,9 +245,9 @@ public async Task GetReducedNpcs(string campaign, string enclave, /// [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] - [SwaggerOperation("getTeam")] + [SwaggerOperation("NpcsTeamGet")] [HttpGet("{campaign}/{enclave}/{team}")] - public async Task> GetTeam(string campaign, string enclave, string team) + public async Task> NpcsTeamGet(string campaign, string enclave, string team) { return await this._service.GetTeam(campaign, enclave, team); } @@ -269,12 +261,13 @@ public async Task> GetTeam(string campaign, string enclav /// [ProducesResponseType(typeof(IActionResult), (int)HttpStatusCode.OK)] [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(IActionResult))] - [SwaggerOperation("getTeamCsv")] + [SwaggerOperation("NpcsTeamGetCsv")] + [Obsolete("Obsolete")] [HttpGet("{campaign}/{enclave}/{team}/csv")] - public async Task GetAsCsv(string campaign, string enclave, string team) + public async Task NpcsTeamGetCsv(string campaign, string enclave, string team) { var engine = new FileHelperEngine(); - var list = await this.GetTeam(team, enclave, campaign); + var list = await this.NpcsTeamGet(team, enclave, campaign); var filteredList = list.Select(n => new NPCToCsv { Id = n.Id, Email = n.NpcProfile.Email }).ToList(); @@ -293,13 +286,21 @@ public async Task GetAsCsv(string campaign, string enclave, strin /// /// [ProducesResponseType(typeof(IActionResult), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(IActionResult))] - [SwaggerOperation("getBuildTfVars")] + [SwaggerResponse((int)HttpStatusCode.BadRequest, Type = typeof(string))] + [Obsolete("Obsolete")] + [SwaggerOperation("NpcsTfVarsGet")] [HttpPost("tfvars")] - public async Task GetTeamAsTfVars(TfVarsConfiguration configuration) + public async Task NpcsTfVarsGet(TfVarsConfiguration configuration) { + if (!ModelState.IsValid || configuration == null || !configuration.IsValid()) + { + return BadRequest(ModelState); + } + var s = new StringBuilder("users = {").Append(Environment.NewLine); - var list = await this.GetTeam(configuration.Campaign, configuration.Enclave, configuration.Team); + var list = await this.NpcsTeamGet(configuration.Campaign, configuration.Enclave, configuration.Team); var pool = configuration.GetIpPool(); foreach (var item in pool) @@ -360,15 +361,25 @@ public async Task GetTeamAsTfVars(TfVarsConfiguration configurati [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [SwaggerRequestExample(typeof(InsiderThreatGenerationConfiguration), typeof(InsiderThreatGenerationConfigurationExample))] - [SwaggerOperation("createInsiderThreatBuild")] + [SwaggerOperation("NpcsInsiderThreatCreate")] [HttpPost("insiderThreat")] - public async Task> Create(InsiderThreatGenerationConfiguration config, CancellationToken ct) + [Obsolete("Obsolete")] + [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] + [SwaggerResponse((int)HttpStatusCode.BadRequest, Type = typeof(string))] + public async Task>> NpcsInsiderThreatCreate(InsiderThreatGenerationConfiguration config, CancellationToken ct) { + if (!ModelState.IsValid || config?.Enclaves == null) + { + return BadRequest(ModelState); + } + var createdNpcs = new List(); foreach (var enclave in config.Enclaves) { + if (enclave.Teams == null) continue; foreach (var team in enclave.Teams) { + if (team.Npcs == null) continue; for (var i = 0; i < team.Npcs.Number; i++) { var branch = team.Npcs.Configuration.Branch ?? MilitaryUnits.GetServiceBranch(); @@ -413,9 +424,10 @@ public async Task> Create(InsiderThreatGenerationConfigur /// [ProducesResponseType(typeof(IActionResult), (int)HttpStatusCode.OK)] [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(IActionResult))] - [SwaggerOperation("getInsiderThreatCsv")] + [SwaggerOperation("NpcsInsiderThreatGetCsv")] [HttpGet("insiderThreat/csv")] - public async Task GetAsCsv() + [Obsolete("Obsolete")] + public async Task NpcsInsiderThreatGetCsv() { var engine = new FileHelperEngine(); engine.HeaderText = engine.GetFileHeader(); diff --git a/src/Ghosts.Api/Controllers/Api/NpcsGenerateController.cs b/src/Ghosts.Api/Controllers/Api/NpcsGenerateController.cs new file mode 100644 index 00000000..26e552f1 --- /dev/null +++ b/src/Ghosts.Api/Controllers/Api/NpcsGenerateController.cs @@ -0,0 +1,91 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + +using System.Collections.Generic; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using ghosts.api.Areas.Animator.Infrastructure.Models; +using ghosts.api.Infrastructure.Models; +using ghosts.api.Infrastructure.Services; +using Microsoft.AspNetCore.Mvc; +using NLog; +using Swashbuckle.AspNetCore.Annotations; + +namespace ghosts.api.Controllers.Api; + +/// +/// Build entire team of NPCs for a campaign and enclave +/// +[ApiController] +[Produces("application/json")] +[Route("api/[controller]")] +public class NpcsGenerateController : ControllerBase +{ + private static readonly Logger _log = LogManager.GetCurrentClassLogger(); + private readonly INpcService _service; + + public NpcsGenerateController(INpcService service) + { + this._service = service; + } + + /// + /// Returns all NPCs at the specified level - Campaign, Enclave, or Team + /// + /// campaign, enclave, team + /// + [ProducesResponseType(typeof(ActionResult>), (int)HttpStatusCode.OK)] + [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(ActionResult>))] + [SwaggerOperation("NpcsGenerateGetByKey")] + [HttpGet] + public async Task>> GetKeys(string key) + { + return Ok(await this._service.GetKeys(key)); + } + + /// + /// Create NPCs belonging to a campaign, enclave and team based on configuration + /// + /// + /// + /// + [ProducesResponseType(typeof(ActionResult>), (int)HttpStatusCode.OK)] + [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(ActionResult>))] + [SwaggerOperation("NpcsGenerateCreate")] + [HttpPost] + public async Task>> Create(GenerationConfiguration config, CancellationToken ct) + { + if (config == null || config.Enclaves == null) + { + return BadRequest(ModelState); + } + + return Ok(await this._service.Create(config, ct)); + } + + /// + /// Generate random NPC by random service branch + /// + /// NPC Profile + [ProducesResponseType(typeof(ActionResult), (int)HttpStatusCode.OK)] + [SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(ActionResult))] + [SwaggerOperation("NpcsGenerateCreateOne")] + [HttpPost("one")] + public async Task> CreateOne() + { + return await this._service.CreateOne(); + } + + /// + /// Ensures an NPC is created for each and every machine currentusername that exists + /// + /// NPC Profile + [ProducesResponseType((int)HttpStatusCode.OK)] + [SwaggerResponse((int)HttpStatusCode.OK)] + [SwaggerOperation("NpcsGenerateSyncWithMachineUsernames")] + [HttpPost("syncWithMachineUsernames")] + public async Task SyncWithMachineUsernames() + { + await this._service.SyncWithMachineUsernames(); + } +} \ No newline at end of file diff --git a/src/Ghosts.Api/Controllers/SurveysController.cs b/src/Ghosts.Api/Controllers/Api/SurveysController.cs similarity index 81% rename from src/Ghosts.Api/Controllers/SurveysController.cs rename to src/Ghosts.Api/Controllers/Api/SurveysController.cs index 46d70657..c134f419 100644 --- a/src/Ghosts.Api/Controllers/SurveysController.cs +++ b/src/Ghosts.Api/Controllers/Api/SurveysController.cs @@ -7,9 +7,12 @@ using ghosts.api.Infrastructure.Services; using Ghosts.Domain.Messages.MesssagesForServer; using Microsoft.AspNetCore.Mvc; +using Swashbuckle.AspNetCore.Annotations; -namespace ghosts.api.Controllers +namespace ghosts.api.Controllers.Api { + [Produces("application/json")] + [Route("api/[controller]")] public class SurveysController : Controller { private readonly ISurveyService _surveyService; @@ -20,6 +23,7 @@ public SurveysController(ISurveyService surveyService) } [ProducesResponseType(typeof(Survey), 200)] + [SwaggerOperation("SurveysGetLatestByMachineId")] [HttpGet("surveys/{machineId}")] public async Task Survey([FromRoute] Guid machineId, CancellationToken ct) { @@ -27,6 +31,7 @@ public async Task Survey([FromRoute] Guid machineId, Cancellation } [ProducesResponseType(typeof(IEnumerable), 200)] + [SwaggerOperation("SurveysGetAllByMachineId")] [HttpGet("surveys/{machineId}/all")] public async Task SurveyAll([FromRoute] Guid machineId, CancellationToken ct) { diff --git a/src/Ghosts.Api/Controllers/TimelinesController.cs b/src/Ghosts.Api/Controllers/Api/TimelinesController.cs similarity index 70% rename from src/Ghosts.Api/Controllers/TimelinesController.cs rename to src/Ghosts.Api/Controllers/Api/TimelinesController.cs index bd4c11f9..abbde858 100644 --- a/src/Ghosts.Api/Controllers/TimelinesController.cs +++ b/src/Ghosts.Api/Controllers/Api/TimelinesController.cs @@ -9,11 +9,13 @@ using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; -namespace ghosts.api.Controllers +namespace ghosts.api.Controllers.Api { /// /// Get or update a machine timeline via the API /// + [Produces("application/json")] + [Route("api/[controller]")] public class TimelinesController : Controller { private readonly ITimelineService _timelineService; @@ -33,15 +35,9 @@ public TimelinesController(ITimelineService timelineService, IMachineTimelinesSe /// Cancellation token /// MachineTimelines [ProducesResponseType(typeof(MachineTimeline), 200)] + [SwaggerOperation("TimelinesGetByMachineId")] [HttpGet("timelines/{machineId}")] - public async Task Timeline([FromRoute] Guid machineId, CancellationToken ct) - { - return Ok(await _machineTimelinesService.GetByMachineIdAsync(machineId, ct)); - } - - [ProducesResponseType(typeof(MachineTimeline), 200)] - [HttpGet("timelines/updates")] - public async Task TimelineUpdates([FromRoute] Guid machineId, CancellationToken ct) + public async Task TimelinesGetByMachineId([FromRoute] Guid machineId, CancellationToken ct) { return Ok(await _machineTimelinesService.GetByMachineIdAsync(machineId, ct)); } @@ -55,8 +51,9 @@ public async Task TimelineUpdates([FromRoute] Guid machineId, Can /// Cancellation token /// MachineTimeline [ProducesResponseType(typeof(MachineTimeline), 200)] + [SwaggerOperation("TimelinesGetByMachineIdAndTimelineId")] [HttpGet("timelines/{machineId}/{timelineId}")] - public async Task TimelineById([FromRoute] Guid machineId, [FromRoute] Guid timelineId, CancellationToken ct) + public async Task TimelinesGetByMachineIdAndTimelineId([FromRoute] Guid machineId, [FromRoute] Guid timelineId, CancellationToken ct) { return Ok(await _machineTimelinesService.GetByMachineIdAndTimelineIdAsync(machineId, timelineId, ct)); } @@ -68,17 +65,17 @@ public async Task TimelineById([FromRoute] Guid machineId, [FromR /// Cancellation token /// 204 No content [HttpPost("timelines")] - // [ProducesResponseType(typeof(Task), (int) HttpStatusCode.NoContent)] Swagger hates this https://stackoverflow.com/questions/35605427/swagger-ui-freezes-after-api-fetch-and-browser-crashes - [SwaggerOperation(OperationId = "createTimeline")] - public async Task Timeline([FromBody] MachineUpdateViewModel machineUpdate, CancellationToken ct) + // [ProducesResponseType(typeof(IActionResult), (int) HttpStatusCode.NoContent)] Swagger hates this https://stackoverflow.com/questions/35605427/swagger-ui-freezes-after-api-fetch-and-browser-crashes + [SwaggerOperation("TimelinesCreate")] + public async Task TimelinesCreate([FromBody] MachineUpdateViewModel machineUpdate, CancellationToken ct) { await _timelineService.UpdateAsync(machineUpdate, ct); return NoContent(); } [HttpPost("timelines/{machineId}/{timelineId}/stop")] - [SwaggerOperation(OperationId = "stopTimeline")] - public async Task Timeline([FromRoute] Guid machineId, [FromRoute] Guid timelineId, CancellationToken ct) + [SwaggerOperation("TimelinesStop")] + public async Task TimelinesStop([FromRoute] Guid machineId, [FromRoute] Guid timelineId, CancellationToken ct) { await _timelineService.StopAsync(machineId, timelineId, ct); return NoContent(); diff --git a/src/Ghosts.Api/Controllers/TrackablesController.cs b/src/Ghosts.Api/Controllers/Api/TrackablesController.cs similarity index 88% rename from src/Ghosts.Api/Controllers/TrackablesController.cs rename to src/Ghosts.Api/Controllers/Api/TrackablesController.cs index 43bfd9ae..98e0eefd 100644 --- a/src/Ghosts.Api/Controllers/TrackablesController.cs +++ b/src/Ghosts.Api/Controllers/Api/TrackablesController.cs @@ -5,8 +5,9 @@ using System.Threading.Tasks; using ghosts.api.Infrastructure.Services; using Microsoft.AspNetCore.Mvc; +using Swashbuckle.AspNetCore.Annotations; -namespace ghosts.api.Controllers +namespace ghosts.api.Controllers.Api { [Produces("application/json")] [Route("api/[controller]")] @@ -25,6 +26,7 @@ public TrackablesController(ITrackableService service) /// /// Cancellation Token /// List of Trackables + [SwaggerOperation("TrackablesGetAll")] [HttpGet] public async Task GetTrackables(CancellationToken ct) { @@ -33,6 +35,7 @@ public async Task GetTrackables(CancellationToken ct) return Ok(list); } + [SwaggerOperation("TrackablesGetHistoryById")] [HttpGet("{id}")] public async Task GetTrackableHistory([FromRoute] Guid id, CancellationToken ct) { diff --git a/src/Ghosts.Api/Controllers/WebhooksController.cs b/src/Ghosts.Api/Controllers/Api/WebhooksController.cs similarity index 78% rename from src/Ghosts.Api/Controllers/WebhooksController.cs rename to src/Ghosts.Api/Controllers/Api/WebhooksController.cs index d9b96982..cb4e44d1 100755 --- a/src/Ghosts.Api/Controllers/WebhooksController.cs +++ b/src/Ghosts.Api/Controllers/Api/WebhooksController.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using Ghosts.Api.Infrastructure.Data; using ghosts.api.Infrastructure.Models; @@ -11,11 +10,12 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Newtonsoft.Json.Linq; +using Swashbuckle.AspNetCore.Annotations; -namespace ghosts.api.Controllers +namespace ghosts.api.Controllers.Api { [Produces("application/json")] - [Route("api/webhooks")] + [Route("api/[controller]")] public class WebhooksController : Controller { private readonly ApplicationDbContext _context; @@ -31,6 +31,7 @@ public WebhooksController(ApplicationDbContext context, IBackgroundQueue service /// Gets all of the webhooks currently active on the system /// /// A list of all webhooks + [SwaggerOperation("WebhooksGetAll")] [HttpGet] public IEnumerable GetWebhooks() { @@ -42,6 +43,7 @@ public IEnumerable GetWebhooks() /// /// The webhook to retrieve /// The webhook + [SwaggerOperation("WebhooksGetById")] [HttpGet("{id}")] public async Task GetWebhook([FromRoute] Guid id) { @@ -60,6 +62,7 @@ public async Task GetWebhook([FromRoute] Guid id) /// The specific webhook to update /// The update to make /// The updated webhook + [SwaggerOperation("WebhooksUpdate")] [HttpPut("{id}")] public async Task PutWebhook([FromRoute] Guid id, [FromBody] Webhook webhook) { @@ -88,6 +91,7 @@ public async Task PutWebhook([FromRoute] Guid id, [FromBody] Webh /// /// The webhook to create /// The saved webhook + [SwaggerOperation("WebhooksCreate")] [HttpPost] public async Task PostWebhook([FromBody] Webhook webhook) { @@ -105,6 +109,7 @@ public async Task PostWebhook([FromBody] Webhook webhook) /// /// The Id of the webhook to delete /// 204 No Content + [SwaggerOperation("WebhooksDete")] [HttpDelete("{id}")] public async Task DeleteWebhook([FromRoute] Guid id) { @@ -124,6 +129,7 @@ public async Task DeleteWebhook([FromRoute] Guid id) /// /// The Id to test /// 204 No Content + [SwaggerOperation("WebhooksTest")] [HttpGet("{id}/test")] public async Task Test([FromRoute] Guid id) { @@ -145,23 +151,33 @@ public async Task Test([FromRoute] Guid id) /// The Id of the webhook /// The timeline item to hook /// 204 No Content + [SwaggerOperation("WebhooksTestById")] [HttpGet("{webhookid}/test/{historytimelineid}")] public async Task TestByID([FromRoute] Guid webhookid, int historytimelineid) { - var timeline = await _context.HistoryTimeline.FirstOrDefaultAsync(o => o.Id == historytimelineid); - - _service.Enqueue( - new QueueEntry - { - Type = QueueEntry.Types.Notification, - Payload = - new NotificationQueueEntry - { - Type = NotificationQueueEntry.NotificationType.Timeline, - Payload = (JObject) JToken.FromObject(timeline) - } - }); - + try + { + var timeline = await _context.HistoryTimeline.FirstOrDefaultAsync(o => o.Id == historytimelineid); + + _service.Enqueue( + new QueueEntry + { + Type = QueueEntry.Types.Notification, + Payload = + new NotificationQueueEntry + { + Type = NotificationQueueEntry.NotificationType.Timeline, + Payload = (JObject)JToken.FromObject(timeline) + } + }); + + + } + catch (Exception e) + { + Console.WriteLine(e.Message); + //todo log this + } return NoContent(); } diff --git a/src/Ghosts.Api/Controllers/HomeController.cs b/src/Ghosts.Api/Controllers/HomeController.cs index ea5afaf0..dd1ac540 100755 --- a/src/Ghosts.Api/Controllers/HomeController.cs +++ b/src/Ghosts.Api/Controllers/HomeController.cs @@ -6,6 +6,7 @@ using Ghosts.Domain.Code; using Microsoft.AspNetCore.Mvc; using NLog; +using Swashbuckle.AspNetCore.Annotations; namespace Ghosts.Api.Controllers { @@ -34,29 +35,32 @@ public IActionResult Index() /// Basic check information including version number, /// and a simple database connection counting machines and groups /// - [HttpGet("test")] [Produces("application/json")] [ResponseCache(Duration = 60)] + [SwaggerOperation("HomeTestApi")] + [HttpGet("test")] public IActionResult Test() { - var s = new Status(); - s.Version = ApplicationDetails.Version; - s.VersionFile = ApplicationDetails.VersionFile; - s.Created = DateTime.UtcNow; + var status = new Status + { + Version = ApplicationDetails.Version, + VersionFile = ApplicationDetails.VersionFile, + Created = DateTime.UtcNow + }; try { - s.Machines = _context.Machines.Count(); - s.Groups = _context.Groups.Count(); - s.Npcs = _context.Npcs.Count(); + status.Machines = _context.Machines.Count(); + status.Groups = _context.Groups.Count(); + status.Npcs = _context.Npcs.Count(); } catch (Exception e) { - _log.Error(e); - throw; + _log.Error(e, "An error occurred while counting database entities."); + return StatusCode(500, "Internal server error"); } - return Json(s); + return Json(status); } public class Status @@ -69,4 +73,4 @@ public class Status public DateTime Created { get; set; } } } -} \ No newline at end of file +} diff --git a/src/Ghosts.Api/Controllers/MachineUpdatesController.cs b/src/Ghosts.Api/Controllers/MachineUpdatesController.cs deleted file mode 100755 index 8960eef8..00000000 --- a/src/Ghosts.Api/Controllers/MachineUpdatesController.cs +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. - -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using ghosts.api.Infrastructure.Models; -using ghosts.api.Infrastructure.Services; -using Ghosts.Api.ViewModels; -using Ghosts.Domain; -using Microsoft.AspNetCore.Mvc; -using NLog; - -namespace Ghosts.Api.Controllers -{ - /// - /// Enter a machine command, so that the next time a machine checks in, - /// it executes the indicated MachineUpdate.Type command - /// - [Produces("application/json")] - [Route("api/[controller]")] - public class MachineUpdatesController : Controller - { - private static readonly Logger _log = LogManager.GetCurrentClassLogger(); - private readonly IBackgroundQueue _queue; - private readonly IMachineUpdateService _updateService; - - public MachineUpdatesController(IMachineUpdateService updateService, IBackgroundQueue queue) - { - _updateService = updateService; - _queue = queue; - } - - /// - /// Sends a command for machine to perform, - /// e.g. health or timeline updates, or post back current timeline - /// - /// The saved MachineUpdate record - [HttpPost] - [ProducesResponseType(200)] - [ProducesResponseType(401)] - [ProducesResponseType(404)] - public async Task Create([FromBody] MachineUpdate machineUpdate, CancellationToken ct) - { - var o = await _updateService.CreateAsync(machineUpdate, ct); - return Ok(o); - } - - /// - /// Send a new timeline to an entire group of machines - /// - /// Group Id - /// The update to send - /// Cancellation token - /// 204 No content - [HttpPost("group/{groupId}")] - public async Task GroupUpdate([FromRoute] int groupId, [FromBody] MachineUpdateViewModel machineUpdate, CancellationToken ct) - { - await this._updateService.UpdateGroupAsync(groupId, machineUpdate, ct); - return NoContent(); - } - - [HttpGet("{updateId}")] - public async Task GetById([FromRoute] int updateId, CancellationToken ct) - { - return await this._updateService.GetById(updateId, ct); - } - - [HttpGet("machine/{machineId}")] - public async Task> GetByMachineId([FromRoute] Guid machineId, CancellationToken ct) - { - return await this._updateService.GetByMachineId(machineId, ct); - } - - [HttpGet("type/{type}")] - public async Task> GetByType([FromRoute] UpdateClientConfig.UpdateType type, CancellationToken ct) - { - return await this._updateService.GetByType(type, ct); - } - - [HttpGet("status/{status}")] - public async Task> GetByStatus([FromRoute] StatusType status, CancellationToken ct) - { - return await this._updateService.GetByStatus(status, ct); - } - } -} \ No newline at end of file diff --git a/src/Ghosts.Api/Areas/Animator/Controllers/ViewActivitiesController.cs b/src/Ghosts.Api/Controllers/ViewActivitiesController.cs similarity index 82% rename from src/Ghosts.Api/Areas/Animator/Controllers/ViewActivitiesController.cs rename to src/Ghosts.Api/Controllers/ViewActivitiesController.cs index 5076dee5..43cf2336 100644 --- a/src/Ghosts.Api/Areas/Animator/Controllers/ViewActivitiesController.cs +++ b/src/Ghosts.Api/Controllers/ViewActivitiesController.cs @@ -1,15 +1,15 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + using System; using System.Linq; -using ghosts.api.Areas.Animator.Infrastructure.Models; using Ghosts.Api.Infrastructure.Data; using Microsoft.AspNetCore.Mvc; -namespace ghosts.api.Areas.Animator.Controllers; +namespace ghosts.api.Controllers; [Controller] [Produces("application/json")] -[Area("Animator")] -[Route("animator/view-activities")] +[Route("view-activities")] [ApiExplorerSettings(IgnoreApi = true)] public class ViewActivitiesController : Controller { diff --git a/src/Ghosts.Api/Areas/Animator/Controllers/ViewRelationshipsController.cs b/src/Ghosts.Api/Controllers/ViewRelationshipsController.cs similarity index 94% rename from src/Ghosts.Api/Areas/Animator/Controllers/ViewRelationshipsController.cs rename to src/Ghosts.Api/Controllers/ViewRelationshipsController.cs index 99cb6f64..8e29418c 100644 --- a/src/Ghosts.Api/Areas/Animator/Controllers/ViewRelationshipsController.cs +++ b/src/Ghosts.Api/Controllers/ViewRelationshipsController.cs @@ -5,13 +5,12 @@ using System.Text; using ghosts.api.Areas.Animator.Infrastructure.Models; using Ghosts.Api.Infrastructure.Data; +using ghosts.api.Infrastructure.Models; using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -namespace ghosts.api.Areas.Animator.Controllers; +namespace ghosts.api.Controllers; -[Area("Animator")] -[Route("animator/view-relationships")] +[Route("view-relationships")] [Controller] [Produces("application/json")] [ApiExplorerSettings(IgnoreApi = true)] diff --git a/src/Ghosts.Api/Areas/Animator/Controllers/ViewSocialController.cs b/src/Ghosts.Api/Controllers/ViewSocialController.cs similarity index 93% rename from src/Ghosts.Api/Areas/Animator/Controllers/ViewSocialController.cs rename to src/Ghosts.Api/Controllers/ViewSocialController.cs index 03ccee99..cc91f594 100644 --- a/src/Ghosts.Api/Areas/Animator/Controllers/ViewSocialController.cs +++ b/src/Ghosts.Api/Controllers/ViewSocialController.cs @@ -2,21 +2,14 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using Ghosts.Api; -using ghosts.api.Areas.Animator.Infrastructure.Animations; -using ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationDefinitions; -using ghosts.api.Areas.Animator.Infrastructure.Models; using Ghosts.Api.Infrastructure; using Microsoft.AspNetCore.Mvc; -using Newtonsoft.Json; using NLog; -namespace ghosts.api.Areas.Animator.Controllers; +namespace ghosts.api.Controllers; -[Area("Animator")] -[Route("animator/view-social")] +[Route("view-social")] [ApiExplorerSettings(IgnoreApi = true)] public class ViewSocialController : Controller { diff --git a/src/Ghosts.Api/Areas/Animator/Hubs/ActivityHub.cs b/src/Ghosts.Api/Hubs/ActivityHub.cs similarity index 91% rename from src/Ghosts.Api/Areas/Animator/Hubs/ActivityHub.cs rename to src/Ghosts.Api/Hubs/ActivityHub.cs index 829dd20e..d722a8e6 100644 --- a/src/Ghosts.Api/Areas/Animator/Hubs/ActivityHub.cs +++ b/src/Ghosts.Api/Hubs/ActivityHub.cs @@ -1,3 +1,5 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + using System; using System.Globalization; using System.Threading.Tasks; @@ -5,7 +7,7 @@ using Microsoft.AspNetCore.SignalR; using NLog; -namespace ghosts.api.Areas.Animator.Hubs; +namespace ghosts.api.Hubs; public class ActivityHub : Hub { diff --git a/src/Ghosts.Api/Areas/Animator/Hubs/ConnectionMapping.cs b/src/Ghosts.Api/Hubs/ConnectionMapping.cs similarity index 91% rename from src/Ghosts.Api/Areas/Animator/Hubs/ConnectionMapping.cs rename to src/Ghosts.Api/Hubs/ConnectionMapping.cs index 909e54fd..825ebfed 100644 --- a/src/Ghosts.Api/Areas/Animator/Hubs/ConnectionMapping.cs +++ b/src/Ghosts.Api/Hubs/ConnectionMapping.cs @@ -1,7 +1,9 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + using System.Collections.Generic; using System.Linq; -namespace ghosts.api.Areas.Animator.Hubs; +namespace ghosts.api.Hubs; public class ConnectionMapping { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/ChatClient.cs b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/ChatClient.cs similarity index 93% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/ChatClient.cs rename to src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/ChatClient.cs index 0d3c537d..b4d9797f 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/ChatClient.cs +++ b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/ChatClient.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Net; using System.Net.Http; @@ -9,16 +10,18 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; -using ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationDefinitions.Chat.Mattermost; -using ghosts.api.Areas.Animator.Infrastructure.ContentServices; -using ghosts.api.Areas.Animator.Infrastructure.ContentServices.Ollama; -using ghosts.api.Areas.Animator.Infrastructure.Extensions; -using ghosts.api.Areas.Animator.Infrastructure.Models; +using ghosts.api.Hubs; +using ghosts.api.Infrastructure.Animations.AnimationDefinitions.Chat.Mattermost; +using ghosts.api.Infrastructure.ContentServices; +using Ghosts.Api.Infrastructure.Data; +using ghosts.api.Infrastructure.Extensions; using Ghosts.Api.Infrastructure.Extensions; +using ghosts.api.Infrastructure.Models; using Ghosts.Domain.Code.Helpers; +using Microsoft.AspNetCore.SignalR; using NLog; -namespace ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationDefinitions.Chat; +namespace ghosts.api.Infrastructure.Animations.AnimationDefinitions.Chat; public class ChatClient { @@ -29,13 +32,18 @@ public class ChatClient private string _token; private string UserId { get; set; } private IFormatterService _formatterService; + + private readonly ApplicationDbContext _context; + private IHubContext _activityHubContext; - public ChatClient(ChatJobConfiguration config, IFormatterService formatterService) + public ChatClient(ChatJobConfiguration config, IFormatterService formatterService, IHubContext activityHubContext, ApplicationDbContext context) { _configuration = config; this._baseUrl = _configuration.Chat.BaseUrl; this._client = new HttpClient(); this._formatterService = formatterService; + this._context = context; + this._activityHubContext = activityHubContext; } private async Task AdminLogin() @@ -428,11 +436,11 @@ public async Task Step(Random random, IEnumerable agents) }); } - await this.StepEx(random, username, _configuration.Chat.DefaultUserPassword); + await this.StepEx(random, agent.Id, username, _configuration.Chat.DefaultUserPassword); } } - private async Task StepEx(Random random, string username, string password) + private async Task StepEx(Random random, Guid NpcId, string username, string password) { _log.Trace($"Managing {username}..."); @@ -557,6 +565,17 @@ private async Task StepEx(Random random, string username, string password) { var post = await this.CreatePost(randomChannelToPostTo, message); _log.Info($"{prompt}|SENT|{post.Message}"); + + + //post to hub + await this._activityHubContext.Clients.All.SendAsync("show", + 1, + NpcId, + "chat", + message, + DateTime.Now.ToString(CultureInfo.InvariantCulture) + ); + } else { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/ChatConfiguration.cs b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/ChatConfiguration.cs similarity index 88% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/ChatConfiguration.cs rename to src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/ChatConfiguration.cs index 3dc291ef..9e19b695 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/ChatConfiguration.cs +++ b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/ChatConfiguration.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; -namespace ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationDefinitions.Chat; +namespace ghosts.api.Infrastructure.Animations.AnimationDefinitions.Chat; public class ChatJobConfiguration { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Channel.cs b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Channel.cs similarity index 70% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Channel.cs rename to src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Channel.cs index dcf3d7bb..b5b7b83f 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Channel.cs +++ b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Channel.cs @@ -4,13 +4,13 @@ using System.Collections.Generic; using System.Text.Json.Serialization; -namespace ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationDefinitions.Chat.Mattermost; +namespace ghosts.api.Infrastructure.Animations.AnimationDefinitions.Chat.Mattermost; public class Channel { [JsonPropertyName("id")] public string Id { get; set; } - [JsonPropertyName("create_at")] public object CreateAt { get; set; } - [JsonPropertyName("update_at")] public object UpdateAt { get; set; } + [JsonPropertyName("create_at")] public long CreateAt { get; set; } + [JsonPropertyName("update_at")] public long UpdateAt { get; set; } [JsonPropertyName("delete_at")] public int DeleteAt { get; set; } [JsonPropertyName("team_id")] public string TeamId { get; set; } [JsonPropertyName("type")] public string Type { get; set; } @@ -18,31 +18,20 @@ public class Channel [JsonPropertyName("name")] public string Name { get; set; } [JsonPropertyName("header")] public string Header { get; set; } [JsonPropertyName("purpose")] public string Purpose { get; set; } - [JsonPropertyName("last_post_at")] public object LastPostAt { get; set; } + [JsonPropertyName("last_post_at")] public long LastPostAt { get; set; } [JsonPropertyName("total_msg_count")] public int TotalMsgCount { get; set; } [JsonPropertyName("extra_update_at")] public int ExtraUpdateAt { get; set; } [JsonPropertyName("creator_id")] public string CreatorId { get; set; } [JsonPropertyName("scheme_id")] public string SchemeId { get; set; } [JsonPropertyName("props")] public object Props { get; set; } - - [JsonPropertyName("group_constrained")] - public bool? GroupConstrained { get; set; } - + [JsonPropertyName("group_constrained")] public bool? GroupConstrained { get; set; } [JsonPropertyName("shared")] public object Shared { get; set; } - - [JsonPropertyName("total_msg_count_root")] - public int TotalMsgCountRoot { get; set; } - + [JsonPropertyName("total_msg_count_root")] public int TotalMsgCountRoot { get; set; } [JsonPropertyName("policy_id")] public object PolicyId { get; set; } - - [JsonPropertyName("last_root_post_at")] - public object LastRootPostAt { get; set; } - - [JsonPropertyName("team_display_name")] - public string TeamDisplayName { get; set; } - + [JsonPropertyName("last_root_post_at")] public long LastRootPostAt { get; set; } + [JsonPropertyName("team_display_name")] public string TeamDisplayName { get; set; } [JsonPropertyName("team_name")] public string TeamName { get; set; } - [JsonPropertyName("team_update_at")] public object TeamUpdateAt { get; set; } + [JsonPropertyName("team_update_at")] public long TeamUpdateAt { get; set; } } public class ChannelHistory diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Post.cs b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Post.cs similarity index 94% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Post.cs rename to src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Post.cs index c9932bf9..5b347c8f 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Post.cs +++ b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Post.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Text.Json.Serialization; -namespace ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationDefinitions.Chat.Mattermost; +namespace ghosts.api.Infrastructure.Animations.AnimationDefinitions.Chat.Mattermost; public class PostResponse { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Team.cs b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Team.cs similarity index 94% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Team.cs rename to src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Team.cs index e62de1f7..3c3c4eee 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Team.cs +++ b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/Team.cs @@ -5,7 +5,7 @@ #pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. -namespace ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationDefinitions.Chat.Mattermost; +namespace ghosts.api.Infrastructure.Animations.AnimationDefinitions.Chat.Mattermost; public class Team { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/User.cs b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/User.cs similarity index 96% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/User.cs rename to src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/User.cs index cf759271..d309c571 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/User.cs +++ b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/Mattermost/User.cs @@ -2,7 +2,7 @@ using System.Text.Json.Serialization; -namespace ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationDefinitions.Chat.Mattermost; +namespace ghosts.api.Infrastructure.Animations.AnimationDefinitions.Chat.Mattermost; public class User { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/ChatJob.cs b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/ChatJob.cs similarity index 86% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/ChatJob.cs rename to src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/ChatJob.cs index 1c7d427c..4d09f6b9 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/ChatJob.cs +++ b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/ChatJob.cs @@ -6,18 +6,16 @@ using System.Text.Json; using System.Threading; using Ghosts.Animator.Extensions; -using ghosts.api.Areas.Animator.Hubs; -using ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationDefinitions.Chat; -using ghosts.api.Areas.Animator.Infrastructure.ContentServices; -using ghosts.api.Areas.Animator.Infrastructure.ContentServices.Ollama; -using ghosts.api.Areas.Animator.Infrastructure.Models; +using ghosts.api.Hubs; using Ghosts.Api.Infrastructure; +using ghosts.api.Infrastructure.Animations.AnimationDefinitions.Chat; +using ghosts.api.Infrastructure.ContentServices; using Ghosts.Api.Infrastructure.Data; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; using NLog; -namespace ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationDefinitions; +namespace ghosts.api.Infrastructure.Animations.AnimationDefinitions; public class ChatJob { @@ -49,7 +47,7 @@ public class ChatJob this._formatterService = new ContentCreationService(_configuration.AnimatorSettings.Animations.Chat.ContentEngine).FormatterService; - this._chatClient = new ChatClient(chatConfiguration, this._formatterService); + this._chatClient = new ChatClient(chatConfiguration, this._formatterService, activityHubContext, this._context); while (!_cancellationToken.IsCancellationRequested) { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/FullAutonomyJob.cs b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/FullAutonomyJob.cs similarity index 94% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/FullAutonomyJob.cs rename to src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/FullAutonomyJob.cs index 841cb152..d64cec69 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/FullAutonomyJob.cs +++ b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/FullAutonomyJob.cs @@ -7,16 +7,15 @@ using System.Linq; using System.Threading; using Ghosts.Animator.Extensions; -using ghosts.api.Areas.Animator.Hubs; -using ghosts.api.Areas.Animator.Infrastructure.ContentServices; -using ghosts.api.Areas.Animator.Infrastructure.Models; +using ghosts.api.Hubs; using Ghosts.Api.Infrastructure; +using ghosts.api.Infrastructure.ContentServices; using Ghosts.Api.Infrastructure.Data; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; using NLog; -namespace ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationDefinitions; +namespace ghosts.api.Infrastructure.Animations.AnimationDefinitions; public class FullAutonomyJob { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/SocialBeliefJob.cs b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/SocialBeliefJob.cs similarity index 77% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/SocialBeliefJob.cs rename to src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/SocialBeliefJob.cs index 2314f8ab..d461a406 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/SocialBeliefJob.cs +++ b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/SocialBeliefJob.cs @@ -6,15 +6,16 @@ using System.Linq; using System.Threading; using Ghosts.Animator.Extensions; -using ghosts.api.Areas.Animator.Hubs; -using ghosts.api.Areas.Animator.Infrastructure.Models; +using ghosts.api.Areas.Animator.Infrastructure; +using ghosts.api.Hubs; using Ghosts.Api.Infrastructure; using Ghosts.Api.Infrastructure.Data; +using ghosts.api.Infrastructure.Models; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; using NLog; -namespace ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationDefinitions; +namespace ghosts.api.Infrastructure.Animations.AnimationDefinitions; public class SocialBeliefJob { @@ -29,13 +30,14 @@ public class SocialBeliefJob public static string[] Beliefs = { - "Strong passwords are essential.", "Regular software updates matter.", "Public Wi-Fi is risky.", - "Antivirus software is a must.", - "Encryption protects data.", "Phishing attacks are common.", "Two-factor authentication helps.", - "Social engineering is a threat.", - "IoT devices can be vulnerable.", "Cyberwarfare affects nations.", "Ransomware can cripple businesses.", - "Insider threats are real.", - "Dark web is a breeding ground.", "DDoS attacks disrupt services.", "Hacktivists promote causes." + // "Strong passwords are essential.", "Regular software updates matter.", "Public Wi-Fi is risky.", + // "Antivirus software is a must.", + // "Encryption protects data.", "Phishing attacks are common.", "Two-factor authentication helps.", + // "Social engineering is a threat.", + // "IoT devices can be vulnerable.", "Cyberwarfare affects nations.", "Ransomware can cripple businesses.", + // "Insider threats are real.", + // "Dark web is a breeding ground.", "DDoS attacks disrupt services.", "Hacktivists promote causes." + "I should vote for candidate A", "I should vote for candidate B" }; public SocialBeliefJob(ApplicationSettings configuration, IServiceScopeFactory scopeFactory, Random random, @@ -106,7 +108,10 @@ private void Step(NpcRecord npc) } npc.NpcSocialGraph = graph; - this._context.SaveChanges(); + this._context.Npcs.Update(npc); // Explicitly set the state to Modified + var o = this._context.SaveChanges(); + Console.WriteLine($"{o} rows were affected."); + _log.Trace($"Social graph saved for {npc.NpcProfile.Name}..."); } @@ -148,10 +153,12 @@ private void Step(NpcRecord npc) newBelief.Step, newBelief.To.ToString(), "belief", - $"{graph.Name} has deeper belief in {newBelief.Name}", - DateTime.Now.ToString(CultureInfo.InvariantCulture) - ); + $"{graph.Name} has updated posterior of {Math.Round(newBelief.Posterior, 2)} in {newBelief.Name}", + DateTime.Now.ToString(CultureInfo.InvariantCulture), cancellationToken: _cancellationToken); + + this._context.Npcs.Update(npc); // Explicitly set the state to Modified + var affectedRows = this._context.SaveChanges(); + Console.WriteLine($"{affectedRows} rows were affected."); - this._context.SaveChanges(); } } \ No newline at end of file diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/SocialGraphJob.cs b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/SocialGraphJob.cs similarity index 98% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/SocialGraphJob.cs rename to src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/SocialGraphJob.cs index fb7e362f..4f2a25b9 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationDefinitions/SocialGraphJob.cs +++ b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/SocialGraphJob.cs @@ -9,17 +9,17 @@ using System.Threading; using System.Threading.Tasks; using Ghosts.Animator.Extensions; -using ghosts.api.Areas.Animator.Hubs; -using ghosts.api.Areas.Animator.Infrastructure.Models; +using ghosts.api.Hubs; using Ghosts.Api.Infrastructure; using Ghosts.Api.Infrastructure.Data; +using ghosts.api.Infrastructure.Models; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using NLog; using Weighted_Randomizer; -namespace ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationDefinitions; +namespace ghosts.api.Infrastructure.Animations.AnimationDefinitions; public class SocialGraphJob { diff --git a/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/SocialSharingJob.cs b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/SocialSharingJob.cs new file mode 100644 index 00000000..d7dfa25d --- /dev/null +++ b/src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/SocialSharingJob.cs @@ -0,0 +1,218 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Ghosts.Animator.Extensions; +using ghosts.api.Hubs; +using Ghosts.Api.Infrastructure; +using ghosts.api.Infrastructure.ContentServices; +using Ghosts.Api.Infrastructure.Data; +using ghosts.api.Infrastructure.Models; +using ghosts.api.Infrastructure.Services; +using Ghosts.Domain; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using NLog; +using RestSharp; + +namespace ghosts.api.Infrastructure.Animations.AnimationDefinitions +{ + public class SocialSharingJob + { + private static readonly Logger _log = LogManager.GetCurrentClassLogger(); + private readonly ApplicationSettings _configuration; + private readonly Random _random; + private int _currentStep; + private readonly IHubContext _activityHubContext; + private readonly CancellationToken _cancellationToken; + private readonly ApplicationDbContext _context; + private readonly IMachineUpdateService _updateService; + private readonly IFormatterService _formatterService; + + public SocialSharingJob(ApplicationSettings configuration, IServiceScopeFactory scopeFactory, Random random, + IHubContext activityHubContext, CancellationToken cancellationToken) + { + try + { + this._activityHubContext = activityHubContext; + this._configuration = configuration; + this._random = random; + + using var innerScope = scopeFactory.CreateScope(); + this._context = innerScope.ServiceProvider.GetRequiredService(); + + this._cancellationToken = cancellationToken; + this._updateService = innerScope.ServiceProvider.GetRequiredService(); + + _formatterService = + new ContentCreationService(_configuration.AnimatorSettings.Animations.Chat.ContentEngine).FormatterService; + + if (!_configuration.AnimatorSettings.Animations.SocialSharing.IsInteracting) + { + _log.Trace($"Social sharing is not interacting. Exiting..."); + return; + } + + RunAsync().GetAwaiter().GetResult(); + } + catch (ThreadInterruptedException e) + { + _log.Info("Social sharing thread interrupted!"); + _log.Error(e); + } + catch (Exception e) + { + _log.Error(e); + } + _log.Info("Social sharing job complete. Exiting..."); + } + + private async Task RunAsync() + { + while (!this._cancellationToken.IsCancellationRequested) + { + if (this._currentStep > _configuration.AnimatorSettings.Animations.SocialSharing.MaximumSteps) + { + _log.Trace($"Maximum steps met: {this._currentStep - 1}. Social sharing is exiting..."); + return; + } + + await this.Step(); + await Task.Delay(this._configuration.AnimatorSettings.Animations.SocialSharing.TurnLength, this._cancellationToken); + this._currentStep++; + } + } + + private async Task Step() + { + _log.Trace("Social sharing step proceeding..."); + + //take some random NPCs + var activities = new List(); + var rawAgents = this._context.Npcs.ToList(); + if (!rawAgents.Any()) + { + _log.Warn("No NPCs found. Is this correct?"); + return; + } + _log.Trace($"Found {rawAgents.Count()} raw agents..."); + + var agents = rawAgents.Shuffle(_random).Take(_random.Next(5, 20)).ToList(); + _log.Trace($"Processing {agents.Count()} agents..."); + foreach (var agent in agents) + { + _log.Trace($"Processing agent {agent.NpcProfile.Email}..."); + var tweetText = await this._formatterService.GenerateTweet(agent); + if (string.IsNullOrEmpty(tweetText)) + { + _log.Trace($"Content service generated no payload..."); + return; + } + + activities.Add(new NpcActivity { ActivityType = NpcActivity.ActivityTypes.SocialMediaPost, NpcId = agent.Id, CreatedUtc = DateTime.UtcNow, Detail = tweetText }); + + // the payloads to socializer are a bit randomized + var userFormValue = new[] { "user", "usr", "u", "uid", "user_id", "u_id" }.RandomFromStringArray(); + var messageFormValue = + new[] { "message", "msg", "m", "message_id", "msg_id", "msg_text", "text", "payload" } + .RandomFromStringArray(); + + if (_configuration.AnimatorSettings.Animations.SocialSharing.IsSendingTimelinesDirectToSocializer) + { + var client = new RestClient(_configuration.AnimatorSettings.Animations.SocialSharing.PostUrl); + var request = new RestRequest("/", Method.Post) + { + RequestFormat = DataFormat.Json + }; + request.AddParameter(userFormValue, agent.NpcProfile.Name.ToString()); + request.AddParameter(messageFormValue, tweetText); + + try + { + var response = client.Execute(request); + if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.NoContent) + { + throw (new Exception( + $"Socializer responded with {response.StatusCode} to the request agent: {agent.NpcProfile.Name} text: {tweetText}")); + } + } + catch (Exception e) + { + _log.Error( + $"Could not post timeline command to Socializer {_configuration.AnimatorSettings.Animations.SocialSharing.PostUrl}: {e}"); + } + } + + if (_configuration.AnimatorSettings.Animations.SocialSharing.IsSendingTimelinesToGhostsApi) + { + var payload = new + { + Uri = _configuration.AnimatorSettings.Animations.SocialSharing.PostUrl, + Category = "social", + Method = "POST", + Headers = new Dictionary + { + { "u", agent.NpcProfile.Email } + }, + FormValues = new Dictionary + { + { userFormValue, agent.NpcProfile.Email }, + { messageFormValue, tweetText } + } + }; + + var t = new Timeline(); + t.Id = Guid.NewGuid(); + t.Status = Timeline.TimelineStatus.Run; + var th = new TimelineHandler(); + th.HandlerType = HandlerType.BrowserFirefox; + th.Initial = "about:blank"; + th.UtcTimeOn = new TimeSpan(0, 0, 0); + th.UtcTimeOff = new TimeSpan(23, 59, 59); + th.HandlerArgs = new Dictionary(); + th.HandlerArgs.Add("isheadless", "false"); + th.Loop = false; + var te = new TimelineEvent(); + te.Command = "browse"; + te.CommandArgs = new List(); + te.CommandArgs.Add(JsonConvert.SerializeObject(payload)); + te.DelayAfter = 0; + te.DelayBefore = 0; + th.TimeLineEvents.Add(te); + t.TimeLineHandlers.Add(th); + + var machineUpdate = new MachineUpdate(); + if (agent.MachineId.HasValue) + { + machineUpdate.MachineId = agent.MachineId.Value; + } + + machineUpdate.Update = t; //JsonConvert.SerializeObject(t); + machineUpdate.Username = agent.NpcProfile.Email; + machineUpdate.Status = StatusType.Active; + machineUpdate.Type = UpdateClientConfig.UpdateType.TimelinePartial; + + _ = await _updateService.CreateAsync(machineUpdate, _cancellationToken); + } + + //post to hub + await this._activityHubContext.Clients.All.SendAsync("show", + "1", + agent.Id.ToString(), + "social", + tweetText, + DateTime.Now.ToString(CultureInfo.InvariantCulture), + cancellationToken: _cancellationToken); + } + + await this._context.NpcActivities.AddRangeAsync(activities, _cancellationToken); + await this._context.SaveChangesAsync(this._cancellationToken); + } + } +} diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationsManager.cs b/src/Ghosts.Api/Infrastructure/Animations/AnimationsManager.cs similarity index 98% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationsManager.cs rename to src/Ghosts.Api/Infrastructure/Animations/AnimationsManager.cs index ed6d611b..9f19c5a5 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Animations/AnimationsManager.cs +++ b/src/Ghosts.Api/Infrastructure/Animations/AnimationsManager.cs @@ -7,9 +7,9 @@ using System.Threading; using System.Threading.Tasks; using Ghosts.Api; -using ghosts.api.Areas.Animator.Hubs; -using ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationDefinitions; +using ghosts.api.Hubs; using Ghosts.Api.Infrastructure; +using ghosts.api.Infrastructure.Animations.AnimationDefinitions; using ghosts.api.Infrastructure.Extensions; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; @@ -18,7 +18,7 @@ using Newtonsoft.Json.Converters; using NLog; -namespace ghosts.api.Areas.Animator.Infrastructure.Animations; +namespace ghosts.api.Infrastructure.Animations; public interface IManageableHostedService : IHostedService { @@ -291,7 +291,7 @@ private void Run(AnimationConfiguration animationConfiguration) } break; - case "SOCIALBELIEFS": + case "SOCIALBELIEF": var beliefSettings = JsonConvert.DeserializeObject(animationConfiguration .JobConfiguration); settings.AnimatorSettings.Animations.SocialBelief = beliefSettings; diff --git a/src/Ghosts.Api/Infrastructure/ApIDetails.cs b/src/Ghosts.Api/Infrastructure/ApIDetails.cs index 575894f8..89c690b4 100755 --- a/src/Ghosts.Api/Infrastructure/ApIDetails.cs +++ b/src/Ghosts.Api/Infrastructure/ApIDetails.cs @@ -29,7 +29,6 @@ public static void LoadConfiguration() config.GetSection("InitSettings").Bind(initConfig); Program.ApplicationSettings = appConfig; - Program.InitSettings = initConfig; } } } \ No newline at end of file diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Bayes.cs b/src/Ghosts.Api/Infrastructure/Bayes.cs similarity index 100% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Bayes.cs rename to src/Ghosts.Api/Infrastructure/Bayes.cs diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/ContentCreationService.cs b/src/Ghosts.Api/Infrastructure/ContentServices/ContentCreationService.cs similarity index 87% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/ContentCreationService.cs rename to src/Ghosts.Api/Infrastructure/ContentServices/ContentCreationService.cs index ad13b6fc..1170440d 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/ContentCreationService.cs +++ b/src/Ghosts.Api/Infrastructure/ContentServices/ContentCreationService.cs @@ -2,14 +2,14 @@ using System; using System.Threading.Tasks; -using ghosts.api.Areas.Animator.Infrastructure.ContentServices.Ollama; -using ghosts.api.Areas.Animator.Infrastructure.ContentServices.OpenAi; -using ghosts.api.Areas.Animator.Infrastructure.ContentServices.Shadows; -using ghosts.api.Areas.Animator.Infrastructure.Models; using Ghosts.Api.Infrastructure; +using ghosts.api.Infrastructure.ContentServices.Ollama; +using ghosts.api.Infrastructure.ContentServices.OpenAi; +using ghosts.api.Infrastructure.ContentServices.Shadows; +using ghosts.api.Infrastructure.Models; using NLog; -namespace ghosts.api.Areas.Animator.Infrastructure.ContentServices; +namespace ghosts.api.Infrastructure.ContentServices; public class ContentCreationService { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/GenericContentHelpers.cs b/src/Ghosts.Api/Infrastructure/ContentServices/GenericContentHelpers.cs similarity index 87% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/GenericContentHelpers.cs rename to src/Ghosts.Api/Infrastructure/ContentServices/GenericContentHelpers.cs index 60f47ac6..43605379 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/GenericContentHelpers.cs +++ b/src/Ghosts.Api/Infrastructure/ContentServices/GenericContentHelpers.cs @@ -1,9 +1,9 @@ // Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. -using ghosts.api.Areas.Animator.Infrastructure.Models; +using ghosts.api.Infrastructure.Models; using Newtonsoft.Json; -namespace ghosts.api.Areas.Animator.Infrastructure.ContentServices; +namespace ghosts.api.Infrastructure.ContentServices; public class GenericContentHelpers { diff --git a/src/Ghosts.Api/Infrastructure/ContentServices/IContentService.cs b/src/Ghosts.Api/Infrastructure/ContentServices/IContentService.cs new file mode 100644 index 00000000..f2617764 --- /dev/null +++ b/src/Ghosts.Api/Infrastructure/ContentServices/IContentService.cs @@ -0,0 +1,10 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + +using System.Threading.Tasks; + +namespace ghosts.api.Infrastructure.ContentServices; + +public interface IContentService +{ + Task ExecuteQuery(string prompt); +} \ No newline at end of file diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/IFormatterService.cs b/src/Ghosts.Api/Infrastructure/ContentServices/IFormatterService.cs similarity index 54% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/IFormatterService.cs rename to src/Ghosts.Api/Infrastructure/ContentServices/IFormatterService.cs index 86c0e06c..edd213db 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/IFormatterService.cs +++ b/src/Ghosts.Api/Infrastructure/ContentServices/IFormatterService.cs @@ -1,7 +1,9 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + using System.Threading.Tasks; -using ghosts.api.Areas.Animator.Infrastructure.Models; +using ghosts.api.Infrastructure.Models; -namespace ghosts.api.Areas.Animator.Infrastructure.ContentServices; +namespace ghosts.api.Infrastructure.ContentServices; public interface IFormatterService { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/Native/NativeContentFormatterService.cs b/src/Ghosts.Api/Infrastructure/ContentServices/Native/NativeContentFormatterService.cs similarity index 96% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/Native/NativeContentFormatterService.cs rename to src/Ghosts.Api/Infrastructure/ContentServices/Native/NativeContentFormatterService.cs index 9ea6a741..5366c021 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/Native/NativeContentFormatterService.cs +++ b/src/Ghosts.Api/Infrastructure/ContentServices/Native/NativeContentFormatterService.cs @@ -1,13 +1,15 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + using System; using System.Linq; using System.Threading.Tasks; using Ghosts.Animator; using Ghosts.Animator.Extensions; using Ghosts.Animator.Models; -using ghosts.api.Areas.Animator.Infrastructure.Models; +using ghosts.api.Infrastructure.Models; using NLog; -namespace ghosts.api.Areas.Animator.Infrastructure.ContentServices.Native; +namespace ghosts.api.Infrastructure.ContentServices.Native; public class NativeContentFormatterService : IFormatterService { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/Ollama/OllamaConnectorService.cs b/src/Ghosts.Api/Infrastructure/ContentServices/Ollama/OllamaConnectorService.cs similarity index 97% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/Ollama/OllamaConnectorService.cs rename to src/Ghosts.Api/Infrastructure/ContentServices/Ollama/OllamaConnectorService.cs index d53860c9..aba829c4 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/Ollama/OllamaConnectorService.cs +++ b/src/Ghosts.Api/Infrastructure/ContentServices/Ollama/OllamaConnectorService.cs @@ -13,7 +13,7 @@ using NLog; using JsonSerializer = System.Text.Json.JsonSerializer; -namespace ghosts.api.Areas.Animator.Infrastructure.ContentServices.Ollama; +namespace ghosts.api.Infrastructure.ContentServices.Ollama; public class OllamaConnectorService : IContentService { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/Ollama/OllamaFormatterService.cs b/src/Ghosts.Api/Infrastructure/ContentServices/Ollama/OllamaFormatterService.cs similarity index 92% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/Ollama/OllamaFormatterService.cs rename to src/Ghosts.Api/Infrastructure/ContentServices/Ollama/OllamaFormatterService.cs index 2a9ab4a2..2d0fc638 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/Ollama/OllamaFormatterService.cs +++ b/src/Ghosts.Api/Infrastructure/ContentServices/Ollama/OllamaFormatterService.cs @@ -1,13 +1,15 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + using System; using System.IO; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -using ghosts.api.Areas.Animator.Infrastructure.Models; using Ghosts.Api.Infrastructure; +using ghosts.api.Infrastructure.Models; using NLog; -namespace ghosts.api.Areas.Animator.Infrastructure.ContentServices.Ollama; +namespace ghosts.api.Infrastructure.ContentServices.Ollama; public class OllamaFormatterService : IFormatterService { @@ -82,15 +84,13 @@ public async Task GenerateNextAction(NpcRecord npc, string history) var prompt = await File.ReadAllTextAsync(promptPath); var messages = new StringBuilder(); - foreach (var p in prompt.Split(System.Environment.NewLine)) + foreach (var p in prompt.Split(Environment.NewLine)) { - var s = p.Replace("[[flattenedAgent]]", flattenedAgent[..3050]); + var s = p.Replace("[[flattenedAgent]]", flattenedAgent); s = s.Replace("[[history]]", history); messages.Append(s).Append(' '); } - // _log.Trace(messages.ToString()); - return await _connectorService.ExecuteQuery(messages.ToString()); } } \ No newline at end of file diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/OpenAi/OpenAIConnectorService.cs b/src/Ghosts.Api/Infrastructure/ContentServices/OpenAi/OpenAIConnectorService.cs similarity index 94% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/OpenAi/OpenAIConnectorService.cs rename to src/Ghosts.Api/Infrastructure/ContentServices/OpenAi/OpenAIConnectorService.cs index 2c8dbf7c..e9851400 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/OpenAi/OpenAIConnectorService.cs +++ b/src/Ghosts.Api/Infrastructure/ContentServices/OpenAi/OpenAIConnectorService.cs @@ -1,3 +1,5 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + using System.Collections.Generic; using System.Linq; using System.Net; @@ -9,7 +11,7 @@ using OpenAI.Managers; using OpenAI.ObjectModels.RequestModels; -namespace ghosts.api.Areas.Animator.Infrastructure.ContentServices.OpenAi; +namespace ghosts.api.Infrastructure.ContentServices.OpenAi; public class OpenAiConnectorService : IContentService { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/OpenAi/OpenAIFormatterService.cs b/src/Ghosts.Api/Infrastructure/ContentServices/OpenAi/OpenAIFormatterService.cs similarity index 94% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/OpenAi/OpenAIFormatterService.cs rename to src/Ghosts.Api/Infrastructure/ContentServices/OpenAi/OpenAIFormatterService.cs index 53ad4bf4..ef370800 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/OpenAi/OpenAIFormatterService.cs +++ b/src/Ghosts.Api/Infrastructure/ContentServices/OpenAi/OpenAIFormatterService.cs @@ -1,11 +1,13 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + using System.Collections.Generic; using System.IO; using System.Threading.Tasks; -using ghosts.api.Areas.Animator.Infrastructure.Models; +using ghosts.api.Infrastructure.Models; using NLog; using OpenAI.ObjectModels.RequestModels; -namespace ghosts.api.Areas.Animator.Infrastructure.ContentServices.OpenAi; +namespace ghosts.api.Infrastructure.ContentServices.OpenAi; public class OpenAiFormatterService : IFormatterService { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/OpenAi/OpenAIHelpers.cs b/src/Ghosts.Api/Infrastructure/ContentServices/OpenAi/OpenAIHelpers.cs similarity index 64% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/OpenAi/OpenAIHelpers.cs rename to src/Ghosts.Api/Infrastructure/ContentServices/OpenAi/OpenAIHelpers.cs index d0ebb7c5..3f2c3494 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/OpenAi/OpenAIHelpers.cs +++ b/src/Ghosts.Api/Infrastructure/ContentServices/OpenAi/OpenAIHelpers.cs @@ -1,6 +1,8 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + using System; -namespace ghosts.api.Areas.Animator.Infrastructure.ContentServices.OpenAi; +namespace ghosts.api.Infrastructure.ContentServices.OpenAi; public static class OpenAiHelpers { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/Shadows/ShadowsConnectorService.cs b/src/Ghosts.Api/Infrastructure/ContentServices/Shadows/ShadowsConnectorService.cs similarity index 97% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/Shadows/ShadowsConnectorService.cs rename to src/Ghosts.Api/Infrastructure/ContentServices/Shadows/ShadowsConnectorService.cs index a62c080c..565d018e 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/Shadows/ShadowsConnectorService.cs +++ b/src/Ghosts.Api/Infrastructure/ContentServices/Shadows/ShadowsConnectorService.cs @@ -13,7 +13,7 @@ using NLog; using JsonSerializer = System.Text.Json.JsonSerializer; -namespace ghosts.api.Areas.Animator.Infrastructure.ContentServices.Shadows; +namespace ghosts.api.Infrastructure.ContentServices.Shadows; public class ShadowsConnectorService : IContentService { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/Shadows/ShadowsFormatterService.cs b/src/Ghosts.Api/Infrastructure/ContentServices/Shadows/ShadowsFormatterService.cs similarity index 94% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/Shadows/ShadowsFormatterService.cs rename to src/Ghosts.Api/Infrastructure/ContentServices/Shadows/ShadowsFormatterService.cs index 83a4cd77..bdd1bd70 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/ContentServices/Shadows/ShadowsFormatterService.cs +++ b/src/Ghosts.Api/Infrastructure/ContentServices/Shadows/ShadowsFormatterService.cs @@ -1,13 +1,15 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + using System; using System.IO; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -using ghosts.api.Areas.Animator.Infrastructure.Models; using Ghosts.Api.Infrastructure; +using ghosts.api.Infrastructure.Models; using NLog; -namespace ghosts.api.Areas.Animator.Infrastructure.ContentServices.Shadows; +namespace ghosts.api.Infrastructure.ContentServices.Shadows; public class ShadowsFormatterService : IFormatterService { diff --git a/src/Ghosts.Api/Infrastructure/Data/ApplicationDbContext.cs b/src/Ghosts.Api/Infrastructure/Data/ApplicationDbContext.cs index 69fff684..92489c34 100644 --- a/src/Ghosts.Api/Infrastructure/Data/ApplicationDbContext.cs +++ b/src/Ghosts.Api/Infrastructure/Data/ApplicationDbContext.cs @@ -52,6 +52,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) base.OnModelCreating(modelBuilder); // Add your customizations after calling base.OnModelCreating(modelBuilder); + modelBuilder.ApplyConfiguration(new MachineUpdateConfiguration()); + + modelBuilder.Entity().Property(o => o.NpcProfile).HasColumnType("jsonb"); modelBuilder.Entity().Property(o => o.NpcSocialGraph).HasColumnType("jsonb"); diff --git a/src/Ghosts.Api/Infrastructure/Examples.cs b/src/Ghosts.Api/Infrastructure/Examples.cs new file mode 100644 index 00000000..7eb5d0e3 --- /dev/null +++ b/src/Ghosts.Api/Infrastructure/Examples.cs @@ -0,0 +1,50 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + +using System; +using System.IO; +using Ghosts.Api.Infrastructure.Data; +using ghosts.api.Infrastructure.Models; +using Ghosts.Domain; +using Ghosts.Domain.Code; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using Swashbuckle.AspNetCore.Filters; + +namespace ghosts.api.Infrastructure; + +public class MachineUpdateExample : IExamplesProvider +{ + private readonly IServiceProvider _serviceProvider; + + public MachineUpdateExample(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public MachineUpdate GetExamples() + { + using var scope = _serviceProvider.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + var machineId = context.Machines + .Select(m => (Guid?)m.Id) // Cast to nullable Guid + .FirstOrDefaultAsync() + .Result ?? Guid.Empty; // Default to Guid.Empty if no machine found + + var o = File.ReadAllText(Path.Combine(ApplicationDetails.InstalledPath, "config", "timelines", "BrowserFirefox.json")); + var timeline = JsonConvert.DeserializeObject(o); + timeline.Id = Guid.NewGuid(); + timeline.Status = Timeline.TimelineStatus.Run; + + return new MachineUpdate + { + MachineId = machineId, + ActiveUtc = DateTime.UtcNow, + CreatedUtc = DateTime.UtcNow, + Status = StatusType.Active, + Type = UpdateClientConfig.UpdateType.TimelinePartial, + Update = timeline + }; + } +} diff --git a/src/Ghosts.Api/Infrastructure/Exceptions.cs b/src/Ghosts.Api/Infrastructure/Exceptions.cs deleted file mode 100644 index 89066cb5..00000000 --- a/src/Ghosts.Api/Infrastructure/Exceptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. - -using System; - -namespace Ghosts.Api.Infrastructure -{ - public class Exceptions - { - public class GhostsClientFormattingException : ArgumentException - { - public GhostsClientFormattingException() { } - - public GhostsClientFormattingException(string message) : base(message) { } - } - } -} \ No newline at end of file diff --git a/src/Ghosts.Api/Infrastructure/Extensions/DateTimeExtensions.cs b/src/Ghosts.Api/Infrastructure/Extensions/DateTimeExtensions.cs index f10b591f..f2bdc3b2 100644 --- a/src/Ghosts.Api/Infrastructure/Extensions/DateTimeExtensions.cs +++ b/src/Ghosts.Api/Infrastructure/Extensions/DateTimeExtensions.cs @@ -1,6 +1,8 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + using System; -namespace ghosts.api.Areas.Animator.Infrastructure.Extensions; +namespace ghosts.api.Infrastructure.Extensions; public static class DateTimeExtensions { diff --git a/src/Ghosts.Api/Infrastructure/Extensions/DictionaryExtensions.cs b/src/Ghosts.Api/Infrastructure/Extensions/DictionaryExtensions.cs index 77acf441..b1efb7f3 100644 --- a/src/Ghosts.Api/Infrastructure/Extensions/DictionaryExtensions.cs +++ b/src/Ghosts.Api/Infrastructure/Extensions/DictionaryExtensions.cs @@ -1,3 +1,5 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + using System.Collections.Generic; using System.Text; diff --git a/src/Ghosts.Api/Infrastructure/Extensions/EnumeratorExtensions.cs b/src/Ghosts.Api/Infrastructure/Extensions/EnumeratorExtensions.cs index fe76fc41..8d513152 100644 --- a/src/Ghosts.Api/Infrastructure/Extensions/EnumeratorExtensions.cs +++ b/src/Ghosts.Api/Infrastructure/Extensions/EnumeratorExtensions.cs @@ -1,7 +1,9 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + using System; using System.Collections.Generic; -namespace ghosts.api.Areas.Animator.Infrastructure.Extensions; +namespace ghosts.api.Infrastructure.Extensions; public static class EnumeratorExtensions { diff --git a/src/Ghosts.Api/Infrastructure/Extensions/JsonExtensions.cs b/src/Ghosts.Api/Infrastructure/Extensions/JsonExtensions.cs new file mode 100644 index 00000000..73296996 --- /dev/null +++ b/src/Ghosts.Api/Infrastructure/Extensions/JsonExtensions.cs @@ -0,0 +1,14 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + +using Newtonsoft.Json; + +namespace Ghosts.Api.Infrastructure.Extensions; + +public static class JsonExtensions +{ + public static bool ContainsInvalidUnicode(this T o) + { + var jsonString = JsonConvert.SerializeObject(o); + return jsonString.Contains("\\u0000"); + } +} \ No newline at end of file diff --git a/src/Ghosts.Api/Infrastructure/Extensions/StringExtensions.cs b/src/Ghosts.Api/Infrastructure/Extensions/StringExtensions.cs index db697d77..ceaf2e8a 100644 --- a/src/Ghosts.Api/Infrastructure/Extensions/StringExtensions.cs +++ b/src/Ghosts.Api/Infrastructure/Extensions/StringExtensions.cs @@ -4,7 +4,10 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; -using ghosts.api.Areas.Animator.Infrastructure.Extensions; +using ghosts.api.Infrastructure.Extensions; +using Newtonsoft.Json; +using NPOI.SS.Formula.Functions; +using Match = System.Text.RegularExpressions.Match; namespace Ghosts.Api.Infrastructure.Extensions { @@ -65,6 +68,7 @@ public static string Clean(this string message, IEnumerable list, Random public static string CreateUsernameFromEmail(this string email) { + if (string.IsNullOrEmpty(email)) return string.Empty; return email.Split('@')[0].Replace(".mil", "").Replace(".civ", "").Replace(".ctr", ""); } diff --git a/src/Ghosts.Api/Infrastructure/Extensions/ZipExtensions.cs b/src/Ghosts.Api/Infrastructure/Extensions/ZipExtensions.cs index ed9f2916..0c88a54c 100644 --- a/src/Ghosts.Api/Infrastructure/Extensions/ZipExtensions.cs +++ b/src/Ghosts.Api/Infrastructure/Extensions/ZipExtensions.cs @@ -1,3 +1,5 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + using System.IO; using System.IO.Compression; diff --git a/src/Ghosts.Api/Infrastructure/Filters/CustomDocumentFilter.cs b/src/Ghosts.Api/Infrastructure/Filters/CustomDocumentFilter.cs new file mode 100644 index 00000000..1d0f7ec0 --- /dev/null +++ b/src/Ghosts.Api/Infrastructure/Filters/CustomDocumentFilter.cs @@ -0,0 +1,28 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + +using System.Linq; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Ghosts.Api.Infrastructure.Filters; + +public class CustomDocumentFilter : IDocumentFilter +{ + public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) + { + foreach (var path in swaggerDoc.Paths) + { + foreach (var operation in path.Value.Operations) + { + var actionDescriptor = context.ApiDescriptions + .FirstOrDefault(desc => desc.RelativePath == path.Key.Substring(1)) + ?.ActionDescriptor; + + if (actionDescriptor != null && actionDescriptor.RouteValues.TryGetValue("action", out var value)) + { + operation.Value.OperationId = value; + } + } + } + } +} \ No newline at end of file diff --git a/src/Ghosts.Api/Infrastructure/GroupNames.cs b/src/Ghosts.Api/Infrastructure/GroupNames.cs index 5d176a48..15912b82 100644 --- a/src/Ghosts.Api/Infrastructure/GroupNames.cs +++ b/src/Ghosts.Api/Infrastructure/GroupNames.cs @@ -11,7 +11,7 @@ namespace Ghosts.Api.Infrastructure { public static class GroupNames { - private static readonly Logger log = LogManager.GetCurrentClassLogger(); + private static readonly Logger _log = LogManager.GetCurrentClassLogger(); private static string FormatToken(List delimeters, ApplicationSettings.GroupingOptions.GroupingDefinitionOption d, string o) { @@ -84,7 +84,7 @@ public static IEnumerable GetGroupNames(Machine machine) } catch (Exception e) { - log.Trace(e); + _log.Trace(e); } return list; diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/EnclaveReducedCsv.cs b/src/Ghosts.Api/Infrastructure/Models/EnclaveReducedCsv.cs similarity index 94% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Models/EnclaveReducedCsv.cs rename to src/Ghosts.Api/Infrastructure/Models/EnclaveReducedCsv.cs index 8b6c7683..abb5c634 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/EnclaveReducedCsv.cs +++ b/src/Ghosts.Api/Infrastructure/Models/EnclaveReducedCsv.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Linq; -namespace ghosts.api.Areas.Animator.Infrastructure.Models; +namespace ghosts.api.Infrastructure.Models; public class EnclaveReducedCsv { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/GenerationConfiguration.cs b/src/Ghosts.Api/Infrastructure/Models/GenerationConfiguration.cs similarity index 97% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Models/GenerationConfiguration.cs rename to src/Ghosts.Api/Infrastructure/Models/GenerationConfiguration.cs index 05c08662..5e93a242 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/GenerationConfiguration.cs +++ b/src/Ghosts.Api/Infrastructure/Models/GenerationConfiguration.cs @@ -6,7 +6,7 @@ using Ghosts.Animator.Models; using Swashbuckle.AspNetCore.Filters; -namespace ghosts.api.Areas.Animator.Infrastructure.Models; +namespace ghosts.api.Infrastructure.Models; /// /// The configuration for generating a large number of NPCs for your scenario diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/InsiderThreatGenerationConfiguration.cs b/src/Ghosts.Api/Infrastructure/Models/InsiderThreatGenerationConfiguration.cs similarity index 97% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Models/InsiderThreatGenerationConfiguration.cs rename to src/Ghosts.Api/Infrastructure/Models/InsiderThreatGenerationConfiguration.cs index fb89e67f..d04725c9 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/InsiderThreatGenerationConfiguration.cs +++ b/src/Ghosts.Api/Infrastructure/Models/InsiderThreatGenerationConfiguration.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Ghosts.Animator.Enums; using Ghosts.Animator.Models; +using ghosts.api.Infrastructure.Models; using Swashbuckle.AspNetCore.Filters; namespace ghosts.api.Areas.Animator.Infrastructure.Models; diff --git a/src/Ghosts.Api/Infrastructure/Models/MachineGroup.cs b/src/Ghosts.Api/Infrastructure/Models/MachineGroup.cs index 3c082ead..8effeae2 100644 --- a/src/Ghosts.Api/Infrastructure/Models/MachineGroup.cs +++ b/src/Ghosts.Api/Infrastructure/Models/MachineGroup.cs @@ -15,10 +15,11 @@ public class Group public Group() { GroupMachines = new List(); - Machines = new List(); } - [Key] public int Id { get; set; } + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } [Required] public string Name { get; set; } @@ -26,13 +27,13 @@ public Group() [JsonConverter(typeof(StringEnumConverter))] public StatusType Status { get; set; } - - [NotMapped] public IList Machines { get; set; } } [Table("group_machines")] public class GroupMachine { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } [ForeignKey("GroupId")] public int GroupId { get; set; } diff --git a/src/Ghosts.Api/Infrastructure/Models/MachineUpdate.cs b/src/Ghosts.Api/Infrastructure/Models/MachineUpdate.cs index 5dc0a56f..8f61710e 100644 --- a/src/Ghosts.Api/Infrastructure/Models/MachineUpdate.cs +++ b/src/Ghosts.Api/Infrastructure/Models/MachineUpdate.cs @@ -3,6 +3,8 @@ using System; using System.ComponentModel.DataAnnotations.Schema; using Ghosts.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -16,17 +18,18 @@ public class MachineUpdate public Guid MachineId { get; set; } public string Username { get; set; } - + [JsonConverter(typeof(StringEnumConverter))] public UpdateClientConfig.UpdateType Type { get; set; } public DateTime ActiveUtc { get; set; } public DateTime CreatedUtc { get; set; } + [JsonConverter(typeof(StringEnumConverter))] public StatusType Status { get; set; } - public string Update { get; set; } - + public Timeline Update { get; set; } + public MachineUpdate() { var now = DateTime.UtcNow; @@ -34,4 +37,17 @@ public MachineUpdate() this.CreatedUtc = now; } } + + public class MachineUpdateConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + // Configure the Update property to be stored as JSON in the database + builder.Property(e => e.Update) + .HasConversion( + v => JsonConvert.SerializeObject(v, Formatting.None), + v => JsonConvert.DeserializeObject(v)) + .HasColumnName("update"); + } + } } \ No newline at end of file diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPC.cs b/src/Ghosts.Api/Infrastructure/Models/NPC.cs similarity index 89% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPC.cs rename to src/Ghosts.Api/Infrastructure/Models/NPC.cs index 6f699e79..24a951db 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPC.cs +++ b/src/Ghosts.Api/Infrastructure/Models/NPC.cs @@ -1,15 +1,14 @@ // Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -using System.Text.Json; using AutoMapper; using Ghosts.Animator.Models; +using ghosts.api.Areas.Animator.Infrastructure.Models; -namespace ghosts.api.Areas.Animator.Infrastructure.Models; +namespace ghosts.api.Infrastructure.Models; [Table("npcs")] public class NpcRecord @@ -46,11 +45,15 @@ public class NpcRecord public static NpcRecord TransformToNpc(NpcProfile o) { + if (o == null) return new NpcRecord(); + var n = new NpcRecord { - NpcProfile = o + NpcProfile = o, + NpcSocialGraph = new NpcSocialGraph(), + Id = o.Id }; - n.Id = n.NpcProfile.Id; + return n; } diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPCIpAddress.cs b/src/Ghosts.Api/Infrastructure/Models/NPCIpAddress.cs similarity index 90% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPCIpAddress.cs rename to src/Ghosts.Api/Infrastructure/Models/NPCIpAddress.cs index ada2e7e4..d22bf7c7 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPCIpAddress.cs +++ b/src/Ghosts.Api/Infrastructure/Models/NPCIpAddress.cs @@ -4,7 +4,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace ghosts.api.Areas.Animator.Infrastructure.Models; +namespace ghosts.api.Infrastructure.Models; [Table("ips")] public class NPCIpAddress diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPCNameId.cs b/src/Ghosts.Api/Infrastructure/Models/NPCNameId.cs similarity index 77% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPCNameId.cs rename to src/Ghosts.Api/Infrastructure/Models/NPCNameId.cs index 54f13c44..3cd6212a 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPCNameId.cs +++ b/src/Ghosts.Api/Infrastructure/Models/NPCNameId.cs @@ -2,7 +2,7 @@ using System; -namespace ghosts.api.Areas.Animator.Infrastructure.Models; +namespace ghosts.api.Infrastructure.Models; public class NpcNameId { diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPCReduced.cs b/src/Ghosts.Api/Infrastructure/Models/NPCReduced.cs similarity index 97% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPCReduced.cs rename to src/Ghosts.Api/Infrastructure/Models/NPCReduced.cs index 7942cd51..2531ee08 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPCReduced.cs +++ b/src/Ghosts.Api/Infrastructure/Models/NPCReduced.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Reflection; -namespace ghosts.api.Areas.Animator.Infrastructure.Models; +namespace ghosts.api.Infrastructure.Models; [Obsolete("This will move to an Automapper map from NPC to NpcProfileSummary class")] public class NPCReduced diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPCToCsv.cs b/src/Ghosts.Api/Infrastructure/Models/NPCToCsv.cs similarity index 80% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPCToCsv.cs rename to src/Ghosts.Api/Infrastructure/Models/NPCToCsv.cs index 3ec361df..5049e4e3 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NPCToCsv.cs +++ b/src/Ghosts.Api/Infrastructure/Models/NPCToCsv.cs @@ -3,7 +3,7 @@ using System; using FileHelpers; -namespace ghosts.api.Areas.Animator.Infrastructure.Models; +namespace ghosts.api.Infrastructure.Models; [DelimitedRecord(",")] public class NPCToCsv diff --git a/src/Ghosts.Api/Infrastructure/Models/NPCToInsiderThreatCsv.cs b/src/Ghosts.Api/Infrastructure/Models/NPCToInsiderThreatCsv.cs new file mode 100644 index 00000000..9f399109 --- /dev/null +++ b/src/Ghosts.Api/Infrastructure/Models/NPCToInsiderThreatCsv.cs @@ -0,0 +1,525 @@ +// Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using FileHelpers; +using Ghosts.Animator; +using Ghosts.Animator.Extensions; +using Ghosts.Animator.Models.InsiderThreat; + +namespace ghosts.api.Infrastructure.Models; + +[DelimitedRecord(",")] +public class NPCToInsiderThreatCsv +{ + [FieldQuoted] public Guid Id { get; set; } + [FieldQuoted] public string Hostname { get; set; } + [FieldQuoted] public string DNS { get; set; } + [FieldQuoted] public string OpenVPNUsername { get; set; } + [FieldQuoted] public string OpenVPNPassword { get; set; } + [FieldQuoted] public string GmailUsername { get; set; } + [FieldQuoted] public string GmailPassword { get; set; } + [FieldQuoted] public string NmailUser { get; set; } + [FieldQuoted] public string NmailPassword { get; set; } + [FieldQuoted] public string IPAddress { get; set; } + [FieldQuoted] public string DomainUser { get; set; } + [FieldQuoted] public string FirstName { get; set; } + [FieldQuoted] public string LastName { get; set; } + [FieldQuoted] public string Password { get; set; } + [FieldQuoted] public string Company { get; set; } + [FieldQuoted] public string StartDate { get; set; } + [FieldQuoted] public string Department { get; set; } + [FieldQuoted] public string Organization { get; set; } + [FieldQuoted] public string JobTitle { get; set; } + [FieldQuoted] public int JobLevel { get; set; } + [FieldQuoted] public string Salary { get; set; } + + [FieldConverter(typeof(EmptyGuidConverter))] + [FieldQuoted] + public Guid Manager { get; set; } + + [FieldQuoted] public string EmailSuffix { get; set; } + [FieldQuoted] public string Email { get; set; } + [FieldQuoted] public string Type { get; set; } + [FieldQuoted] public string Address { get; set; } + [FieldQuoted] public string City { get; set; } + [FieldQuoted] public string Phone { get; set; } + [FieldQuoted] public string State { get; set; } + [FieldQuoted] public string Zip { get; set; } + [FieldQuoted] public string Country { get; set; } + [FieldQuoted] public string EmploymentStatus { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool Disgruntled { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool Demoted { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool MissedRaises { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool TeamLayoffs { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool NotifiedOfTermination { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool AnnouncesTermination { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool AnnouncesResignation { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool Threats { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool MissedPromotion { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool Insubordination { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool Absenteeism { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool HRComplaints { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool ITPolicyViolations { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool IPPolicyViolations { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool DrugAlcoholAbuse { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool CoworkerConflict { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool EAPReferral { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool AccessRevoked { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool UnauthorizedCodingChange { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool UnauthorizedAccessChange { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool FinancialProblems { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool ArrestRecord { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool GamblingHistory { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool ServiceAccountUse { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool RemoteAccess { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool BackdoorAccountUse { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool SolicitedByCompetitor { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool AfterHoursLogin { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool PrivilegeCreep { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool FileExtensionModification { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool FileHeaderModification { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool FileContentModification { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool FileTypeModification { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool SensitiveInformationCopied { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool ScreenShots { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool ZipFile { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool Encryption { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool DocumentMarkingTampering { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool Steganography { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool LogDeletion { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool ConcealmentInformation { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool SaleAttempt { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool Scanner { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool CloudStorage { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool RemovableMedia { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool Print { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool NetworkShare { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool PersonalEmailAccount { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool EmailToConspirator { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool DNSExfiltrationTool { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool FileDeletion { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool RecentHRTicket { get; set; } + + [FieldConverter(typeof(TrueFalseToXConverter))] + [FieldQuoted] + public bool BackgroundCkResult { get; set; } + + [FieldQuoted] public int InterpersonalSkills { get; set; } + [FieldQuoted] public int AdherenceToPolicy { get; set; } + [FieldQuoted] public int EnthusiasmAndAttitude { get; set; } + [FieldQuoted] public int OpenToFeedback { get; set; } + [FieldQuoted] public int GeneralPerformance { get; set; } + [FieldQuoted] public int OverallPerformance { get; set; } + + public static IEnumerable ConvertToCsv(IEnumerable npcs) + { + var finalList = new List(); + foreach (var n in npcs) + { + if (n.NpcProfile == null || n.NpcProfile.Accounts == null || n.NpcProfile.Address == null) continue; + + var events = n.NpcProfile.InsiderThreat.GetAllEvents(); + + var i = AnimatorRandom.Rand.Next(0, 99); + var o = new NPCToInsiderThreatCsv + { + Id = n.Id, + Hostname = $"user{i}", + DNS = $"FIN-USR-{i}" + }; + + foreach (var account in n.NpcProfile.Accounts.Where(x => + x.Url != null && x.Url.ToLower().Contains("openvpn"))) + { + o.OpenVPNUsername = account.Username; + o.OpenVPNPassword = account.Password; + } + + // o.HRMUserID + // o.HRMManagerID + // o.TestCase + // o.ExampleCase + + foreach (var account in + n.NpcProfile.Accounts.Where(x => x.Url != null && x.Url.ToLower().Contains("gmail"))) + { + o.GmailUsername = account.Username; + o.GmailPassword = account.Password; + } + + foreach (var account in + n.NpcProfile.Accounts.Where(x => x.Url != null && x.Url.ToLower().Contains("nmail"))) + { + o.NmailUser = account.Username; + o.NmailPassword = account.Password; + } + + o.IPAddress = n.NpcProfile.Workstation.IPAddress; + + o.DomainUser = n.NpcProfile.Workstation.Username; + o.FirstName = n.NpcProfile.Name.First; + o.LastName = n.NpcProfile.Name.Last; + o.Password = n.NpcProfile.Workstation.Password; + o.Type = "User"; + if (n.NpcProfile.Employment.EmploymentRecords != null && n.NpcProfile.Employment.EmploymentRecords.Any()) + { + var currentEmployer = n.NpcProfile.Employment.EmploymentRecords.LastOrDefault(); + o.EmploymentStatus = "Employed"; + o.Company = currentEmployer?.Company; + o.StartDate = currentEmployer?.StartDate.ToString(CultureInfo.InvariantCulture); + o.Department = currentEmployer?.Department; + o.Organization = currentEmployer?.Organization; + o.JobTitle = currentEmployer?.JobTitle; + o.JobLevel = currentEmployer.Level; + o.Salary = currentEmployer?.Salary.ToString(CultureInfo.InvariantCulture); + o.Manager = currentEmployer.Manager; + if (o.JobLevel > 2) + { + if (AnimatorRandom.Rand.Next(0, 100) > 50) + { + o.Type = "Administrator"; + } + } + } + else + { + o.EmploymentStatus = "Unemployed"; + } + + o.EmailSuffix = n.NpcProfile.Email.After("@"); + o.Email = n.NpcProfile.Email; + + o.Address = n.NpcProfile.Address.FirstOrDefault()?.Address1; + o.City = n.NpcProfile.Address.FirstOrDefault()?.City; + o.Phone = n.NpcProfile.CellPhone; + o.State = n.NpcProfile.Address.FirstOrDefault()?.State; + o.Zip = n.NpcProfile.Address.FirstOrDefault()?.PostalCode; + o.Country = "US"; + var relatedEvents = events as RelatedEvent[] ?? events.ToArray(); + if (!relatedEvents.Any()) continue; + try + { + o.Demoted = relatedEvents.Any(x => + x.Description.Contains("Demoted", StringComparison.CurrentCultureIgnoreCase)); + o.Disgruntled = relatedEvents.Length > 3; + o.MissedRaises = relatedEvents.Any(x => + x.Description.Contains("Missed raise", StringComparison.CurrentCultureIgnoreCase)); + o.TeamLayoffs = relatedEvents.Any(x => + x.Description.Contains("Team layoffs", StringComparison.CurrentCultureIgnoreCase)); + o.NotifiedOfTermination = relatedEvents.Any(x => + x.Description.Contains("Notified of termination", StringComparison.CurrentCultureIgnoreCase)); + o.AnnouncesTermination = relatedEvents.Any(x => + x.Description.Contains("Announces Termination", StringComparison.CurrentCultureIgnoreCase)); + o.AnnouncesResignation = relatedEvents.Any(x => + x.Description.Contains("Announces Resignation", StringComparison.CurrentCultureIgnoreCase)); + o.Threats = relatedEvents.Any(x => + x.Description.Contains("Threatening", StringComparison.CurrentCultureIgnoreCase)) || + relatedEvents.Any(x => + x.Description.Contains("threatened", StringComparison.CurrentCultureIgnoreCase)); + o.MissedPromotion = relatedEvents.Any(x => + x.Description.Contains("Missed Promotion", StringComparison.CurrentCultureIgnoreCase)); + o.Insubordination = relatedEvents.Any(x => + x.Description.Contains("insubordinate", StringComparison.CurrentCultureIgnoreCase)); + o.Absenteeism = relatedEvents.Any(x => + x.Description.Contains("missed work", StringComparison.CurrentCultureIgnoreCase)); + o.HRComplaints = relatedEvents.Any(x => + x.Description.Contains("Human Resource Complaint", StringComparison.CurrentCultureIgnoreCase)); + o.ITPolicyViolations = relatedEvents.Any(x => + x.Description.Contains("Employee violated company IT policy", + StringComparison.CurrentCultureIgnoreCase)); + o.IPPolicyViolations = relatedEvents.Any(x => + x.Description.Contains("Compliance Violation", StringComparison.CurrentCultureIgnoreCase)); + o.DrugAlcoholAbuse = n.NpcProfile.InsiderThreat.SubstanceAbuseAndAddictiveBehaviors.RelatedEvents.Any(); + o.CoworkerConflict = relatedEvents.Any(x => + x.Description.Contains("coworker", StringComparison.CurrentCultureIgnoreCase)); + o.EAPReferral = relatedEvents.Any(x => + x.Description.Contains("EAP Referral", StringComparison.CurrentCultureIgnoreCase)); + o.AccessRevoked = relatedEvents.Any(x => + x.Description.Contains("Access Revoked", StringComparison.CurrentCultureIgnoreCase)); + o.UnauthorizedCodingChange = relatedEvents.Any(x => + x.Description.Contains("unauthorized changes to a code base", + StringComparison.CurrentCultureIgnoreCase)); + o.UnauthorizedAccessChange = relatedEvents.Any(x => + x.Description.Contains("unauthorized changes to access", + StringComparison.CurrentCultureIgnoreCase)); + o.FinancialProblems = relatedEvents.Any(x => + x.Description.Contains("Financial Problems", + StringComparison.CurrentCultureIgnoreCase)) || + n.NpcProfile.InsiderThreat.FinancialConsiderations.RelatedEvents.Any(); + o.ArrestRecord = relatedEvents.Any(x => + x.Description.Contains("arrest", StringComparison.CurrentCultureIgnoreCase)); + o.GamblingHistory = relatedEvents.Any(x => + x.Description.Contains("gambling", StringComparison.CurrentCultureIgnoreCase)); + o.ServiceAccountUse = relatedEvents.Any(x => + x.Description.Contains("service account", StringComparison.CurrentCultureIgnoreCase)); + o.RemoteAccess = relatedEvents.Any(x => + x.Description.Contains("Virtual Access Anomaly", StringComparison.CurrentCultureIgnoreCase)); + o.BackdoorAccountUse = relatedEvents.Any(x => + x.Description.Contains("backdoor account", StringComparison.CurrentCultureIgnoreCase)); + o.SolicitedByCompetitor = relatedEvents.Any(x => + x.Description.Contains("Solicited by Competitor", StringComparison.CurrentCultureIgnoreCase)); + o.AfterHoursLogin = relatedEvents.Any(x => + x.Description.Contains("After Hours Login", StringComparison.CurrentCultureIgnoreCase)); + o.PrivilegeCreep = relatedEvents.Any(x => + x.Description.Contains("Misusing Privileged Function", StringComparison.CurrentCultureIgnoreCase)); + o.FileExtensionModification = relatedEvents.Any(x => + x.Description.Contains("modified file extension", StringComparison.CurrentCultureIgnoreCase)); + o.FileHeaderModification = relatedEvents.Any(x => + x.Description.Contains("modified file header", StringComparison.CurrentCultureIgnoreCase)); + o.FileContentModification = relatedEvents.Any(x => + x.Description.Contains("altered document", StringComparison.CurrentCultureIgnoreCase)); + o.FileTypeModification = relatedEvents.Any(x => + x.Description.Contains("modified file extension", StringComparison.CurrentCultureIgnoreCase)); + o.SensitiveInformationCopied = relatedEvents.Any(x => + x.Description.Contains("copied sensitive information", StringComparison.CurrentCultureIgnoreCase)); + o.ScreenShots = relatedEvents.Any(x => + x.Description.Contains("took screenshots", StringComparison.CurrentCultureIgnoreCase)); + o.ZipFile = relatedEvents.Any(x => + x.Description.Contains("compressed files", StringComparison.CurrentCultureIgnoreCase)); + o.Encryption = relatedEvents.Any(x => + x.Description.Contains("encrypted files", StringComparison.CurrentCultureIgnoreCase)); + o.DocumentMarkingTampering = relatedEvents.Any(x => + x.Description.Contains("altered document markings", StringComparison.CurrentCultureIgnoreCase)); + o.Steganography = relatedEvents.Any(x => + x.Description.Contains("used steganography", StringComparison.CurrentCultureIgnoreCase)); + o.LogDeletion = relatedEvents.Any(x => + x.Description.Contains("deleted logs", StringComparison.CurrentCultureIgnoreCase)); + o.ConcealmentInformation = relatedEvents.Any(x => + x.Description.Contains("concealed actions", StringComparison.CurrentCultureIgnoreCase)); + o.SaleAttempt = relatedEvents.Any(x => + x.Description.Contains("Information Sale Attempt", StringComparison.CurrentCultureIgnoreCase)); + o.Scanner = relatedEvents.Any(x => + x.Description.Contains("scanned files", StringComparison.CurrentCultureIgnoreCase)); + o.CloudStorage = relatedEvents.Any(x => + x.Description.Contains("cloud storage", StringComparison.CurrentCultureIgnoreCase)); + o.RemovableMedia = relatedEvents.Any(x => + x.Description.Contains("removable media device", StringComparison.CurrentCultureIgnoreCase)); + o.Print = relatedEvents.Any(x => + x.Description.Contains("printed sensitive files", StringComparison.CurrentCultureIgnoreCase)); + o.NetworkShare = relatedEvents.Any(x => + x.Description.Contains("unauthorized changes", StringComparison.CurrentCultureIgnoreCase)); + o.PersonalEmailAccount = relatedEvents.Any(x => + x.Description.Contains("personal email account", StringComparison.CurrentCultureIgnoreCase)); + o.EmailToConspirator = relatedEvents.Any(x => + x.Description.Contains("Email to Conspirator", StringComparison.CurrentCultureIgnoreCase)); + o.DNSExfiltrationTool = relatedEvents.Any(x => + x.Description.Contains("dns exfiltration", StringComparison.CurrentCultureIgnoreCase)); + o.FileDeletion = relatedEvents.Any(x => + x.Description.Contains("deleted files", StringComparison.CurrentCultureIgnoreCase)); + o.RecentHRTicket = + relatedEvents.Any(x => x.Reported > DateTime.Now.AddYears(-1)); //reported in the last year + o.BackgroundCkResult = n.NpcProfile.InsiderThreat.IsBackgroundCheckStatusClear; + o.InterpersonalSkills = n.NpcProfile.MentalHealth.InterpersonalSkills; + o.AdherenceToPolicy = n.NpcProfile.MentalHealth.AdherenceToPolicy; + o.EnthusiasmAndAttitude = n.NpcProfile.MentalHealth.EnthusiasmAndAttitude; + o.OpenToFeedback = n.NpcProfile.MentalHealth.OpenToFeedback; + o.GeneralPerformance = n.NpcProfile.MentalHealth.GeneralPerformance; + o.OverallPerformance = n.NpcProfile.MentalHealth.OverallPerformance; + } + catch (Exception e) + { + //todo log this + Console.WriteLine(e); + } + finalList.Add(o); + } + + foreach (var npc in finalList.Where(x => x.Manager != Guid.Empty)) + { + var manager = finalList.FirstOrDefault(x => x.Id == npc.Manager); + if (manager == null) continue; + if (npc.Company != manager.Company || npc.Department != manager.Department) + { + npc.Manager = Guid.Empty; + } + } + + return finalList; + } +} + +public class TrueFalseToXConverter : ConverterBase +{ + public override object StringToField(string o) + { + return o.Equals("X", StringComparison.InvariantCultureIgnoreCase); + } + + public override string FieldToString(object o) + { + return Convert.ToBoolean(o) ? "X" : string.Empty; + } +} + +public class EmptyGuidConverter : ConverterBase +{ + public override object StringToField(string o) + { + return o.Equals(Guid.Empty.ToString(), StringComparison.InvariantCultureIgnoreCase); + } + + public override string FieldToString(object o) + { + var x = Guid.Parse(o.ToString() ?? ""); + return x == Guid.Empty ? "" : x.ToString(); + } +} \ No newline at end of file diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NpcActivity.cs b/src/Ghosts.Api/Infrastructure/Models/NpcActivity.cs similarity index 89% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NpcActivity.cs rename to src/Ghosts.Api/Infrastructure/Models/NpcActivity.cs index f2887286..fa984341 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NpcActivity.cs +++ b/src/Ghosts.Api/Infrastructure/Models/NpcActivity.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace ghosts.api.Areas.Animator.Infrastructure.Models; +namespace ghosts.api.Infrastructure.Models; [Table("npc_activity")] public class NpcActivity diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NpcSocialGraph.cs b/src/Ghosts.Api/Infrastructure/Models/NpcSocialGraph.cs similarity index 91% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NpcSocialGraph.cs rename to src/Ghosts.Api/Infrastructure/Models/NpcSocialGraph.cs index bd8f7f82..d26deab2 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/NpcSocialGraph.cs +++ b/src/Ghosts.Api/Infrastructure/Models/NpcSocialGraph.cs @@ -2,8 +2,9 @@ using System; using System.Collections.Generic; +using Newtonsoft.Json; -namespace ghosts.api.Areas.Animator.Infrastructure.Models; +namespace ghosts.api.Infrastructure.Models; public class NpcSocialGraph { @@ -73,12 +74,13 @@ public class Belief public decimal Likelihood { get; set; } public decimal Posterior { get; set; } - public Belief(Guid to, Guid from, string name, long currentStep, decimal likelihood, decimal posterior) + [JsonConstructor] + public Belief(Guid to, Guid from, string name, long step, decimal likelihood, decimal posterior) { this.From = from; this.To = to; this.Name = name; - this.Step = currentStep; + this.Step = step; this.Likelihood = likelihood; this.Posterior = posterior; } @@ -93,4 +95,5 @@ public static string ToHeader() return "To,From,Name,Step,Likelihood,Posterior"; } } + } \ No newline at end of file diff --git a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/TfVarsConfiguration.cs b/src/Ghosts.Api/Infrastructure/Models/TfVarsConfiguration.cs similarity index 72% rename from src/Ghosts.Api/Areas/Animator/Infrastructure/Models/TfVarsConfiguration.cs rename to src/Ghosts.Api/Infrastructure/Models/TfVarsConfiguration.cs index eaa64b7e..403c2d28 100644 --- a/src/Ghosts.Api/Areas/Animator/Infrastructure/Models/TfVarsConfiguration.cs +++ b/src/Ghosts.Api/Infrastructure/Models/TfVarsConfiguration.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; -namespace ghosts.api.Areas.Animator.Infrastructure.Models; +namespace ghosts.api.Infrastructure.Models; public class TfVarsConfiguration { @@ -15,6 +15,19 @@ public class TfVarsConfiguration public string Gateway { get; set; } public string Mask { get; set; } + public bool IsValid() + { + if (string.IsNullOrEmpty(this.IpAddressLow) + || string.IsNullOrEmpty(this.IpAddressHigh) + || string.IsNullOrEmpty(this.Campaign) + || string.IsNullOrEmpty(this.Enclave) + || string.IsNullOrEmpty(this.Team) + || string.IsNullOrEmpty(this.Gateway) + || string.IsNullOrEmpty(this.Mask)) + return false; + return true; + } + public IList GetIpPool() { var pool = new List(); @@ -24,20 +37,21 @@ public IList GetIpPool() var low = Convert.ToInt32(lowArr[lowArr.GetUpperBound(0)]); var high = Convert.ToInt32(highArr[highArr.GetUpperBound(0)]); - + for (var i = low; i < high; i++) { pool.Add(ReplaceLastOccurrence(this.IpAddressLow, low.ToString(), i.ToString())); } + pool.Add(this.IpAddressHigh); return pool; } - + private static string ReplaceLastOccurrence(string Source, string Find, string Replace) { var place = Source.LastIndexOf(Find, StringComparison.CurrentCultureIgnoreCase); - if(place == -1) + if (place == -1) return Source; var result = Source.Remove(place, Find.Length).Insert(place, Replace); diff --git a/src/Ghosts.Api/Infrastructure/Services/MachineGroupService.cs b/src/Ghosts.Api/Infrastructure/Services/MachineGroupService.cs index 8c95d9eb..0e59ed22 100644 --- a/src/Ghosts.Api/Infrastructure/Services/MachineGroupService.cs +++ b/src/Ghosts.Api/Infrastructure/Services/MachineGroupService.cs @@ -34,17 +34,7 @@ public MachineGroupService(ApplicationDbContext context) public async Task> GetAsync(string q, CancellationToken ct) { - var list = await _context.Groups.Include(o => o.GroupMachines).ToListAsync(ct); - foreach (var group in list) - foreach (var machineMapping in group.GroupMachines) - { - var machine = await _context.Machines.FirstOrDefaultAsync(m => m.Id == machineMapping.MachineId && m.Status == StatusType.Active, ct); - if (machine == null) - continue; - group.Machines.Add(machine); - } - - return list; + return await _context.Groups.Include(o => o.GroupMachines).ToListAsync(ct); } public async Task GetAsync(int id, CancellationToken ct) @@ -76,7 +66,7 @@ public async Task UpdateAsync(Group model, CancellationToken ct) this._context.GroupMachines.Remove(g); } - if (model.GroupMachines.Count > 0) + if (model.GroupMachines != null && model.GroupMachines.Count > 0) { // Add new GroupMachines from the model foreach (var g in model.GroupMachines) @@ -88,10 +78,6 @@ public async Task UpdateAsync(Group model, CancellationToken ct) } } } - else - { - originalRecord.Machines.Clear(); - } // Update properties of the original record to match those of the model // Assuming Group has properties Name and Description @@ -125,18 +111,26 @@ public async Task DeleteAsync(int id, CancellationToken ct) public async Task> GetActivity(int id, int skip, int take, CancellationToken ct) { + if (take < 1) + take = 20; + var machineGroup = await _context.Groups.Include(o => o.GroupMachines).FirstOrDefaultAsync(o => o.Id == id, ct); + if (machineGroup == null) return new List(); + var machineIds = machineGroup.GroupMachines.Select(m => m.MachineId).ToList(); - + if (machineIds.Count < 1) return new List(); + try { - return (from o in _context.HistoryTimeline where machineIds.Contains(o.MachineId) select o).Skip(skip).Take(take).ToList(); + return (from o in _context.HistoryTimeline where machineIds.Contains(o.MachineId) select o) + .OrderByDescending(x => x.CreatedUtc).Skip(skip).Take(take).ToList(); } catch (Exception e) { - _log.Debug(e); - throw; + _log.Error(e); } + + return new List(); } } } \ No newline at end of file diff --git a/src/Ghosts.Api/Infrastructure/Services/MachineService.cs b/src/Ghosts.Api/Infrastructure/Services/MachineService.cs index 0e3ada5a..0a09e718 100755 --- a/src/Ghosts.Api/Infrastructure/Services/MachineService.cs +++ b/src/Ghosts.Api/Infrastructure/Services/MachineService.cs @@ -250,6 +250,9 @@ public async Task> GetMachineHistory(Guid id, C public async Task> GetActivity(Guid id, int skip, int take, CancellationToken ct) { + if (take < 1) + take = 25; + var machine = await _context.Machines.FirstOrDefaultAsync(o => o.Id == id, ct); if (machine == null) { diff --git a/src/Ghosts.Api/Infrastructure/Services/MachineUpdateService.cs b/src/Ghosts.Api/Infrastructure/Services/MachineUpdateService.cs index 5a387e8f..becf5c88 100644 --- a/src/Ghosts.Api/Infrastructure/Services/MachineUpdateService.cs +++ b/src/Ghosts.Api/Infrastructure/Services/MachineUpdateService.cs @@ -109,6 +109,12 @@ public async Task> GetByStatus(StatusType status, Can public async Task CreateAsync(MachineUpdate model, CancellationToken ct) { + var machineUpdate = await this.GetById(model.Id, ct); + if (machineUpdate != null) + return machineUpdate; + + model.Update.Id = Guid.NewGuid(); + _context.MachineUpdates.Add(model); await _context.SaveChangesAsync(ct); return model; diff --git a/src/Ghosts.Api/Infrastructure/Services/NpcService.cs b/src/Ghosts.Api/Infrastructure/Services/NpcService.cs new file mode 100644 index 00000000..a84dad07 --- /dev/null +++ b/src/Ghosts.Api/Infrastructure/Services/NpcService.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Ghosts.Animator; +using Ghosts.Animator.Models; +using ghosts.api.Areas.Animator.Infrastructure.Models; +using Ghosts.Api.Infrastructure.Data; +using ghosts.api.Infrastructure.Models; +using Microsoft.EntityFrameworkCore; +using NLog; + +namespace ghosts.api.Infrastructure.Services; + +public interface INpcService +{ + public Task> GetAll(); + public Task> GetEnclave(string campaign, string enclave); + public Task> GetListAsync(); + public Task> GetTeam(string campaign, string enclave, string team); + public Task GetById(Guid id); + public Task> Create(GenerationConfiguration config, CancellationToken ct); + public Task CreateOne(); + public Task DeleteById(Guid id); + public Task> GetKeys(string key); + public Task SyncWithMachineUsernames(); +} + +public class NpcService : INpcService +{ + private static readonly Logger _log = LogManager.GetCurrentClassLogger(); + private readonly ApplicationDbContext _context; + + public NpcService(ApplicationDbContext context) + { + _context = context; + } + + public async Task> GetAll() + { + return await this._context.Npcs.ToListAsync(); + } + + public async Task> GetEnclave(string campaign, string enclave) + { + return await _context.Npcs.Where(x => x.Campaign == campaign && x.Enclave == enclave).ToListAsync(); + } + + public async Task> GetListAsync() + { + return await this._context.Npcs + .Select(item => new NpcNameId + { + Id = item.Id, + Name = $"{item.NpcProfile.Name.First} {item.NpcProfile.Name.Last}" + }) + .ToListAsync(); + } + + public async Task> GetListAsync(string campaign) + { + return await this._context.Npcs + .Where(x=>x.Campaign == campaign) + .Select(item => new NpcNameId + { + Id = item.Id, + Name = $"{item.NpcProfile.Name.First} {item.NpcProfile.Name.Last}" + }) + .ToListAsync(); + } + + public async Task> GetTeam(string campaign, string enclave, string team) + { + return await this._context.Npcs.Where(x => x.Campaign == campaign && x.Enclave == enclave && x.Team == team).ToListAsync(); + } + + public async Task GetById(Guid id) + { + return await this._context.Npcs.FirstOrDefaultAsync(x => x.Id == id); + } + + public async Task CreateOne() + { + var npc = NpcRecord.TransformToNpc(Npc.Generate(MilitaryUnits.GetServiceBranch())); + npc.Id = npc.NpcProfile.Id; + this._context.Npcs.Add(npc); + await this._context.SaveChangesAsync(); + return npc; + } + + public async Task> Create(GenerationConfiguration config, CancellationToken ct) + { + var t = new Stopwatch(); + t.Start(); + + var createdNpcs = new List(); + foreach (var enclave in config.Enclaves) + { + if (enclave.Teams == null) continue; + foreach (var team in enclave.Teams) + { + if (team.Npcs == null) continue; + if (team.Npcs.Number > 25) + { + _log.Warn("Cannot generate more than 25 NPCs at a time, sorry."); + team.Npcs.Number = 25; + } + for (var i = 0; i < team.Npcs.Number; i++) + { + var last = t.ElapsedMilliseconds; + var branch = team.Npcs.Configuration?.Branch ?? MilitaryUnits.GetServiceBranch(); + var npc = NpcRecord.TransformToNpc(Npc.Generate(new NpcGenerationConfiguration + { Branch = branch, PreferenceSettings = team.PreferenceSettings })); + npc.Id = npc.NpcProfile.Id; + npc.Team = team.Name; + npc.Campaign = config.Campaign; + npc.Enclave = enclave.Name; + + this._context.Npcs.Add(npc); + createdNpcs.Add(npc); + _log.Trace($"{i} generated in {t.ElapsedMilliseconds - last} ms"); + } + } + } + + await this._context.SaveChangesAsync(ct); + + t.Stop(); + _log.Trace($"{createdNpcs.Count} NPCs generated in {t.ElapsedMilliseconds} ms"); + + return createdNpcs; + } + + public async Task> GetKeys(string key) + { + if (key == null) + return new List(); + + return key.ToLower() switch + { + "campaign" => await _context.Npcs.Where(x => x.Campaign != null).Select(x => x.Campaign).Distinct().ToListAsync(), + "enclave" => await _context.Npcs.Where(x => x.Enclave != null).Select(x => x.Enclave).Distinct().ToListAsync(), + "team" => await _context.Npcs.Where(x => x.Team != null).Select(x => x.Team).Distinct().ToListAsync(), + _ => null + }; + } + + public async Task DeleteById(Guid id) + { + var o = await this._context.Npcs.FindAsync(id); + if (o != null) + { + this._context.Npcs.Remove(o); + await this._context.SaveChangesAsync(); + } + } + + public async Task SyncWithMachineUsernames() + { + var machines = this._context.Machines.ToList(); + var npcs = this._context.Npcs.ToArray(); + + foreach (var machine in machines) + { + if (npcs.Any(x => x.MachineId == machine.Id)) + continue; + if (npcs.Any(x => string.Equals(x.NpcProfile.Name.ToString()?.Replace(" ", "."), + machine.CurrentUsername, StringComparison.InvariantCultureIgnoreCase))) + continue; + + var npc = NpcRecord.TransformToNpc(Npc.Generate(MilitaryUnits.GetServiceBranch(), machine.CurrentUsername)); + + //todo: need to be sure user is aligned with the machine currentusername + + npc.Id = npc.NpcProfile.Id; + npc.MachineId = machine.Id; + this._context.Npcs.Add(npc); + _log.Trace($"NPC created for {machine.CurrentUsername}..."); + } + + await this._context.SaveChangesAsync(); + _log.Trace($"NPCs created for each username in machines"); + } +} \ No newline at end of file diff --git a/src/Ghosts.Api/Infrastructure/Services/QueueSyncService.cs b/src/Ghosts.Api/Infrastructure/Services/QueueSyncService.cs index 3dc92929..0e432128 100755 --- a/src/Ghosts.Api/Infrastructure/Services/QueueSyncService.cs +++ b/src/Ghosts.Api/Infrastructure/Services/QueueSyncService.cs @@ -383,6 +383,8 @@ private async Task ProcessMachine(IServiceScope scope, ApplicationDbContext cont internal static async void HandleWebhook(Webhook webhook, NotificationQueueEntry payload) { + if (webhook == null || payload == null) return; + string formattedResponse; if (payload.Type == NotificationQueueEntry.NotificationType.TimelineDelivered) { @@ -394,6 +396,7 @@ internal static async void HandleWebhook(Webhook webhook, NotificationQueueEntry // Serialize our concrete class into a JSON String formattedResponse = webhook.PostbackFormat; + if(string.IsNullOrEmpty(formattedResponse)) return; var isValid = false; var reg = new Regex(@"\[(.*?)\]"); diff --git a/src/Ghosts.Api/Infrastructure/Services/TimelineService.cs b/src/Ghosts.Api/Infrastructure/Services/TimelineService.cs index 9da1be39..64dce530 100644 --- a/src/Ghosts.Api/Infrastructure/Services/TimelineService.cs +++ b/src/Ghosts.Api/Infrastructure/Services/TimelineService.cs @@ -29,6 +29,8 @@ public TimelineService(ApplicationDbContext context) public async Task UpdateAsync(MachineUpdateViewModel machineUpdateViewModel, CancellationToken ct) { + if (machineUpdateViewModel == null) return; + var machineUpdate = machineUpdateViewModel.ToMachineUpdate(); _context.MachineUpdates.Add(machineUpdate); @@ -61,7 +63,7 @@ public async Task StopAsync(Guid machineId, Guid timelineId, CancellationToken c var o = new MachineUpdate { Status = StatusType.Active, - Update = JsonConvert.SerializeObject(timeline), + Update = timeline, ActiveUtc = DateTime.UtcNow, CreatedUtc = DateTime.UtcNow, MachineId = machineId, diff --git a/src/Ghosts.Api/Infrastructure/Services/TrackableService.cs b/src/Ghosts.Api/Infrastructure/Services/TrackableService.cs index 37489a23..8cb5b559 100755 --- a/src/Ghosts.Api/Infrastructure/Services/TrackableService.cs +++ b/src/Ghosts.Api/Infrastructure/Services/TrackableService.cs @@ -5,11 +5,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Ghosts.Api; using Ghosts.Api.Infrastructure.Data; using ghosts.api.Infrastructure.Models; using Microsoft.EntityFrameworkCore; -using NLog; namespace ghosts.api.Infrastructure.Services { @@ -21,9 +19,7 @@ public interface ITrackableService public class TrackableService : ITrackableService { - private static readonly Logger _log = LogManager.GetCurrentClassLogger(); private readonly ApplicationDbContext _context; - private readonly int _lookBack = Program.ApplicationSettings.LookbackRecords; public TrackableService(ApplicationDbContext context) { diff --git a/src/Ghosts.Api/Infrastructure/WebRequestReader.cs b/src/Ghosts.Api/Infrastructure/WebRequestReader.cs index 1691af43..8787eea6 100644 --- a/src/Ghosts.Api/Infrastructure/WebRequestReader.cs +++ b/src/Ghosts.Api/Infrastructure/WebRequestReader.cs @@ -26,7 +26,7 @@ public static Machine GetMachine(HttpContext context) HostIp = context.Request.Headers["ghosts-ip"], CurrentUsername = CheckIfBase64Encoded(context.Request.Headers["ghosts-user"]), ClientVersion = context.Request.Headers["ghosts-version"], - IPAddress = context.Connection.RemoteIpAddress.ToString(), + IPAddress = context.Connection.RemoteIpAddress?.ToString(), StatusUp = Machine.UpDownStatus.Up }; diff --git a/src/Ghosts.Api/Program.cs b/src/Ghosts.Api/Program.cs index 1127e752..53d0b081 100755 --- a/src/Ghosts.Api/Program.cs +++ b/src/Ghosts.Api/Program.cs @@ -17,8 +17,7 @@ public class Program private static readonly Logger _log = LogManager.GetCurrentClassLogger(); public static ApplicationSettings ApplicationSettings { get; set; } - public static InitOptions InitSettings { get; set; } - + public static void Main(string[] args) { Console.WriteLine(ApplicationDetails.Header); diff --git a/src/Ghosts.Api/Startup.cs b/src/Ghosts.Api/Startup.cs index 32f7f01b..fbf41665 100755 --- a/src/Ghosts.Api/Startup.cs +++ b/src/Ghosts.Api/Startup.cs @@ -4,15 +4,16 @@ using System.IO; using System.Reflection; using System.Text.Json.Serialization; -using ghosts.api.Areas.Animator.Hubs; -using ghosts.api.Areas.Animator.Infrastructure.Animations; -using ghosts.api.Areas.Animator.Infrastructure.Services; +using ghosts.api.Hubs; using Ghosts.Api.Hubs; +using ghosts.api.Infrastructure; +using ghosts.api.Infrastructure.Animations; using Ghosts.Api.Infrastructure.Data; using Ghosts.Api.Infrastructure.Extensions; +using Ghosts.Api.Infrastructure.Filters; using ghosts.api.Infrastructure.Services; -using Ghosts.Api.ViewModels; using Ghosts.Domain.Code; +using Ghosts.Domain.Code.Helpers; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -22,6 +23,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; +using Newtonsoft.Json.Converters; using Swashbuckle.AspNetCore.Filters; namespace Ghosts.Api @@ -39,7 +41,6 @@ public Startup(IConfiguration configuration) public void ConfigureServices(IServiceCollection services) { - //services.AddDbContext(opt => opt.UseInMemoryDatabase("ghosts")); services.AddDbContext(options => options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"))); services.TryAddTransient(); @@ -63,16 +64,16 @@ public void ConfigureServices(IServiceCollection services) Name = "Copyright 2017 Carnegie Mellon University. All Rights Reserved. See LICENSE.md file for terms" } }); - + // Set the comments path for the Swagger JSON and UI. var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + c.DocumentFilter(); c.IncludeXmlComments(xmlPath); c.ExampleFilters(); }); + services.AddSwaggerGenNewtonsoftSupport(); // explicit opt-in - needs to be placed after AddSwaggerGen() services.AddSwaggerExamplesFromAssemblies(Assembly.GetEntryAssembly()); - services.AddSwaggerExamplesFromAssemblyOf(); - // Add application services. services.AddScoped(); @@ -84,6 +85,9 @@ public void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddSwaggerExamplesFromAssemblyOf(); + services.AddSingleton(); services.AddSingleton(); @@ -91,7 +95,11 @@ public void ConfigureServices(IServiceCollection services) services.AddControllers().AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())); - services.AddMvc().AddNewtonsoftJson(); + services.AddMvc().AddNewtonsoftJson(options => + { + options.SerializerSettings.Converters.Add(new TimeSpanConverter()); + options.SerializerSettings.Converters.Add(new StringEnumConverter()); + }); services.AddSignalR(); services.AddControllersWithViews().AddRazorRuntimeCompilation(); @@ -128,8 +136,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseSwaggerUI(c => { c.SwaggerEndpoint($"/swagger/v{ApiVersion}/swagger.json", $"GHOSTS API v{ApiVersion}"); - // c.RoutePrefix = string.Empty; // this places the swagger file at the site root }); } } -} \ No newline at end of file +} diff --git a/src/Ghosts.Api/ViewModels/MachineUpdateViewModel.cs b/src/Ghosts.Api/ViewModels/MachineUpdateViewModel.cs index fdfc8c7c..9968e1de 100644 --- a/src/Ghosts.Api/ViewModels/MachineUpdateViewModel.cs +++ b/src/Ghosts.Api/ViewModels/MachineUpdateViewModel.cs @@ -27,7 +27,7 @@ public MachineUpdate ToMachineUpdate() { CreatedUtc = DateTime.UtcNow, Status = Status, - Update = JsonConvert.SerializeObject(Update), + Update = Update, //JsonConvert.SerializeObject(Update), MachineId = MachineId, Type = Type, ActiveUtc = ActiveUtc @@ -56,7 +56,7 @@ public MachineUpdateViewModel GetExamples() { HandlerType = HandlerType.BrowserFirefox, Initial = "https://cmu.edu", - UtcTimeOn = TimeSpan.Parse("0000:00"), + UtcTimeOn = TimeSpan.Parse("00:00:00"), UtcTimeOff = TimeSpan.Parse("23:59:59"), Loop = true, TimeLineEvents = new List diff --git a/src/Ghosts.Api/Areas/Animator/Views/Animations/Index.cshtml b/src/Ghosts.Api/Views/Animations/Index.cshtml similarity index 95% rename from src/Ghosts.Api/Areas/Animator/Views/Animations/Index.cshtml rename to src/Ghosts.Api/Views/Animations/Index.cshtml index c973ba15..75326ce9 100644 --- a/src/Ghosts.Api/Areas/Animator/Views/Animations/Index.cshtml +++ b/src/Ghosts.Api/Views/Animations/Index.cshtml @@ -1,5 +1,5 @@ -@using ghosts.api.Areas.Animator.Infrastructure.Animations -@model ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationConfiguration +@using ghosts.api.Infrastructure.Animations +@model ghosts.api.Infrastructure.Animations.AnimationConfiguration

GHOSTS Animations

diff --git a/src/Ghosts.Api/Areas/Animator/Views/Animations/Started.cshtml b/src/Ghosts.Api/Views/Animations/Started.cshtml similarity index 96% rename from src/Ghosts.Api/Areas/Animator/Views/Animations/Started.cshtml rename to src/Ghosts.Api/Views/Animations/Started.cshtml index cacf4540..ecb83cd0 100644 --- a/src/Ghosts.Api/Areas/Animator/Views/Animations/Started.cshtml +++ b/src/Ghosts.Api/Views/Animations/Started.cshtml @@ -1,4 +1,4 @@ -@model ghosts.api.Areas.Animator.Infrastructure.Animations.AnimationConfiguration +@model ghosts.api.Infrastructure.Animations.AnimationConfiguration

Animations!

diff --git a/src/Ghosts.Api/Views/Shared/_Layout.cshtml b/src/Ghosts.Api/Views/Shared/_Layout.cshtml index 605a57b0..dc92ff5f 100644 --- a/src/Ghosts.Api/Views/Shared/_Layout.cshtml +++ b/src/Ghosts.Api/Views/Shared/_Layout.cshtml @@ -31,16 +31,16 @@
diff --git a/src/Ghosts.Api/Areas/Animator/Views/ViewActivities/Index.cshtml b/src/Ghosts.Api/Views/ViewActivities/Index.cshtml similarity index 91% rename from src/Ghosts.Api/Areas/Animator/Views/ViewActivities/Index.cshtml rename to src/Ghosts.Api/Views/ViewActivities/Index.cshtml index a0a84700..703890d9 100644 --- a/src/Ghosts.Api/Areas/Animator/Views/ViewActivities/Index.cshtml +++ b/src/Ghosts.Api/Views/ViewActivities/Index.cshtml @@ -1,4 +1,4 @@ -@model IEnumerable +@model IEnumerable
-

Animator Activities

+

Activities

@@ -58,6 +58,8 @@ var icon = ""; if (type === "belief") icon = ""; + else if (type === "chat") + icon = ""; else if (type === "knowledge") icon = ""; else if (type === "relationship") diff --git a/src/Ghosts.Api/Areas/Animator/Views/ViewActivities/detail.cshtml b/src/Ghosts.Api/Views/ViewActivities/detail.cshtml similarity index 99% rename from src/Ghosts.Api/Areas/Animator/Views/ViewActivities/detail.cshtml rename to src/Ghosts.Api/Views/ViewActivities/detail.cshtml index be134531..84fb33f5 100644 --- a/src/Ghosts.Api/Areas/Animator/Views/ViewActivities/detail.cshtml +++ b/src/Ghosts.Api/Views/ViewActivities/detail.cshtml @@ -1,4 +1,4 @@ -@model ghosts.api.Areas.Animator.Infrastructure.Models.NpcRecord +@model ghosts.api.Infrastructure.Models.NpcRecord