github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/teams/chain_test.go (about) 1 package teams 2 3 import ( 4 "encoding/json" 5 "testing" 6 7 "golang.org/x/net/context" 8 9 "github.com/davecgh/go-spew/spew" 10 "github.com/keybase/client/go/kbtest" 11 "github.com/keybase/client/go/libkb" 12 "github.com/keybase/client/go/protocol/keybase1" 13 "github.com/keybase/go-codec/codec" 14 "github.com/stretchr/testify/require" 15 ) 16 17 // A chain with a stubbed link 18 // Output of `rotate_root_team_key` test in team_integration.iced 19 const teamChain1 = ` 20 {"status":{"code":0,"name":"OK"},"chain":[{"seqno":1,"sig":"g6Rib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEgewPUgFaXvAjHWBC4BnTzSdMT3izYy89VX+4rjh2NlMkKp3BheWxvYWTEJ5UCAcDEIL95/rs8CFK3VSp4W4nYmxAdatMKJf+BB1xWH3eB/K7vIaNzaWfEQAez3e1PlPXJUg2vITA8/ZKeIKBp67DQQaNSKchzpPaMN8BFtvlY2qQStmk5Jn9mcN7m5x7xGBvRLksLkz4fOQSoc2lnX3R5cGUgo3RhZ80CAqd2ZXJzaW9uAQ==","payload_json":"{\"body\":{\"key\":{\"eldest_kid\":\"01207b03d4805697bc08c75810b80674f349d313de2cd8cbcf555fee2b8e1d8d94c90a\",\"host\":\"keybase.io\",\"kid\":\"01207b03d4805697bc08c75810b80674f349d313de2cd8cbcf555fee2b8e1d8d94c90a\",\"uid\":\"5bf82de4331b50b32cbbcfeadc2f3119\",\"username\":\"d_af8eac8c\"},\"team\":{\"id\":\"64d27654bef64bdb3d78d84f186c4224\",\"members\":{\"admin\":[\"53e315afb4b419931b0a6a1eaa09e219\"],\"owner\":[\"5bf82de4331b50b32cbbcfeadc2f3119\"],\"reader\":[\"13e18aeafa4df6c94bf6af7d7bb98d19\"],\"writer\":[\"4bf92804c02fb7d2cd36a6d420d6f619\"]},\"name\":\"t_9d6d1e37\",\"per_team_key\":{\"encryption_kid\":\"0121bf2085a5f1b4f8e0ad5095fb29ae65f7e52a4fa5d9bc90757515c7dd860767020a\",\"generation\":1,\"reverse_sig\":\"g6Rib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEgYr3LeU54Mu4AdjeZ3bG+7c0yEL51p2dfHxneCIxTVPEKp3BheWxvYWTFA3R7ImJvZHkiOnsia2V5Ijp7ImVsZGVzdF9raWQiOiIwMTIwN2IwM2Q0ODA1Njk3YmMwOGM3NTgxMGI4MDY3NGYzNDlkMzEzZGUyY2Q4Y2JjZjU1NWZlZTJiOGUxZDhkOTRjOTBhIiwiaG9zdCI6ImtleWJhc2UuaW8iLCJraWQiOiIwMTIwN2IwM2Q0ODA1Njk3YmMwOGM3NTgxMGI4MDY3NGYzNDlkMzEzZGUyY2Q4Y2JjZjU1NWZlZTJiOGUxZDhkOTRjOTBhIiwidWlkIjoiNWJmODJkZTQzMzFiNTBiMzJjYmJjZmVhZGMyZjMxMTkiLCJ1c2VybmFtZSI6ImRfYWY4ZWFjOGMifSwidGVhbSI6eyJpZCI6IjY0ZDI3NjU0YmVmNjRiZGIzZDc4ZDg0ZjE4NmM0MjI0IiwibWVtYmVycyI6eyJhZG1pbiI6WyI1M2UzMTVhZmI0YjQxOTkzMWIwYTZhMWVhYTA5ZTIxOSJdLCJvd25lciI6WyI1YmY4MmRlNDMzMWI1MGIzMmNiYmNmZWFkYzJmMzExOSJdLCJyZWFkZXIiOlsiMTNlMThhZWFmYTRkZjZjOTRiZjZhZjdkN2JiOThkMTkiXSwid3JpdGVyIjpbIjRiZjkyODA0YzAyZmI3ZDJjZDM2YTZkNDIwZDZmNjE5Il19LCJuYW1lIjoidF85ZDZkMWUzNyIsInBlcl90ZWFtX2tleSI6eyJlbmNyeXB0aW9uX2tpZCI6IjAxMjFiZjIwODVhNWYxYjRmOGUwYWQ1MDk1ZmIyOWFlNjVmN2U1MmE0ZmE1ZDliYzkwNzU3NTE1YzdkZDg2MDc2NzAyMGEiLCJnZW5lcmF0aW9uIjoxLCJyZXZlcnNlX3NpZyI6bnVsbCwic2lnbmluZ19raWQiOiIwMTIwNjJiZGNiNzk0ZTc4MzJlZTAwNzYzNzk5ZGRiMWJlZWRjZDMyMTBiZTc1YTc2NzVmMWYxOWRlMDg4YzUzNTRmMTBhIn19LCJ0eXBlIjoidGVhbS5yb290IiwidmVyc2lvbiI6Mn0sImN0aW1lIjoxNDk3MjM4NTMyLCJleHBpcmVfaW4iOjE1NzY4MDAwMCwicHJldiI6bnVsbCwic2VxX3R5cGUiOjMsInNlcW5vIjoxLCJ0YWciOiJzaWduYXR1cmUifaNzaWfEQLrzfIl+c/rDlaTL9hW5emLJSJOoyhWw0gKnShh4v5FzX0tunexOId0U87etEgT1P+uJ6KYCSQTh8ZdCmDWJ7Q6oc2lnX3R5cGUgo3RhZ80CAqd2ZXJzaW9uAQ==\",\"signing_kid\":\"012062bdcb794e7832ee00763799ddb1beedcd3210be75a7675f1f19de088c5354f10a\"}},\"type\":\"team.root\",\"version\":2},\"ctime\":1497238532,\"expire_in\":157680000,\"prev\":null,\"seq_type\":3,\"seqno\":1,\"tag\":\"signature\"}","version":2,"uid":"5bf82de4331b50b32cbbcfeadc2f3119"},{"seqno":2,"sig":"g6Rib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEgewPUgFaXvAjHWBC4BnTzSdMT3izYy89VX+4rjh2NlMkKp3BheWxvYWTESJUCAsQgYst2GtNGo9KL4e7RB8NVSKLdb63pIZo4WRhB9i1YbP7EICPQdqUmdeZU/gRJXd5+gUP8HSxtn4xMeZ7lssS3Pm+8IqNzaWfEQHkBp46skYqz62rMjoxZGq4HVjhJHCS4zYjmrMDhQQrl3fu76HeRqTeuLWCh0741OyvwXTjGNY7oCJiT5YvZSw+oc2lnX3R5cGUgo3RhZ80CAqd2ZXJzaW9uAQ==","version":2,"uid":"5bf82de4331b50b32cbbcfeadc2f3119"},{"seqno":3,"sig":"g6Rib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEgTqfhtGmmOGhixMw9YsHIrmrk0txnZBM4A/hqS1gWbzUKp3BheWxvYWTESJUCA8Qgg2TPT1Vbq+1yiOlYEFHqQYcccB4W6bevJFRK1jc1ov7EIHbADJMePlGQ1+JCJe9AQiftKeKwAIKLYLC1pPvcWyZmJKNzaWfEQPNVaBljU0mSO/27FfQDZiNaNYfbZ+lG1QF2WaOoUBgtChMxEek+3jKWTGkfWSvjL+MynM8ve+egRteBY8jhoQioc2lnX3R5cGUgo3RhZ80CAqd2ZXJzaW9uAQ==","payload_json":"{\"body\":{\"key\":{\"eldest_kid\":\"01204ea7e1b469a6386862c4cc3d62c1c8ae6ae4d2dc6764133803f86a4b58166f350a\",\"host\":\"keybase.io\",\"kid\":\"01204ea7e1b469a6386862c4cc3d62c1c8ae6ae4d2dc6764133803f86a4b58166f350a\",\"uid\":\"4bf92804c02fb7d2cd36a6d420d6f619\",\"username\":\"b_7804991a\"},\"team\":{\"id\":\"64d27654bef64bdb3d78d84f186c4224\",\"per_team_key\":{\"encryption_kid\":\"0121e2511cbfb0418187a8e19183a1cd92637bc83fe116d1eb8984f52394495b5f120a\",\"generation\":2,\"reverse_sig\":\"g6Rib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEggC9V7V4U9hxzCHG16Z1jcFT/f1ugwbLMUrFU47r4CgAKp3BheWxvYWTFAuJ7ImJvZHkiOnsia2V5Ijp7ImVsZGVzdF9raWQiOiIwMTIwNGVhN2UxYjQ2OWE2Mzg2ODYyYzRjYzNkNjJjMWM4YWU2YWU0ZDJkYzY3NjQxMzM4MDNmODZhNGI1ODE2NmYzNTBhIiwiaG9zdCI6ImtleWJhc2UuaW8iLCJraWQiOiIwMTIwNGVhN2UxYjQ2OWE2Mzg2ODYyYzRjYzNkNjJjMWM4YWU2YWU0ZDJkYzY3NjQxMzM4MDNmODZhNGI1ODE2NmYzNTBhIiwidWlkIjoiNGJmOTI4MDRjMDJmYjdkMmNkMzZhNmQ0MjBkNmY2MTkiLCJ1c2VybmFtZSI6ImJfNzgwNDk5MWEifSwidGVhbSI6eyJpZCI6IjY0ZDI3NjU0YmVmNjRiZGIzZDc4ZDg0ZjE4NmM0MjI0IiwicGVyX3RlYW1fa2V5Ijp7ImVuY3J5cHRpb25fa2lkIjoiMDEyMWUyNTExY2JmYjA0MTgxODdhOGUxOTE4M2ExY2Q5MjYzN2JjODNmZTExNmQxZWI4OTg0ZjUyMzk0NDk1YjVmMTIwYSIsImdlbmVyYXRpb24iOjIsInJldmVyc2Vfc2lnIjpudWxsLCJzaWduaW5nX2tpZCI6IjAxMjA4MDJmNTVlZDVlMTRmNjFjNzMwODcxYjVlOTlkNjM3MDU0ZmY3ZjViYTBjMWIyY2M1MmIxNTRlM2JhZjgwYTAwMGEifX0sInR5cGUiOiJ0ZWFtLnJvdGF0ZV9rZXkiLCJ2ZXJzaW9uIjoyfSwiY3RpbWUiOjE0OTcyMzg1MzUsImV4cGlyZV9pbiI6MTU3NjgwMDAwLCJwcmV2IjoiODM2NGNmNGY1NTViYWJlZDcyODhlOTU4MTA1MWVhNDE4NzFjNzAxZTE2ZTliN2FmMjQ1NDRhZDYzNzM1YTJmZSIsInNlcV90eXBlIjozLCJzZXFubyI6MywidGFnIjoic2lnbmF0dXJlIn2jc2lnxECfcafw2CoIzFKtmN2nt3A28wYS7clrmEZvjLEziNmoWy525gvyxJEHiENfxQ5kt9Uxb0cCDChlktHvz23my6QAqHNpZ190eXBlIKN0YWfNAgKndmVyc2lvbgE=\",\"signing_kid\":\"0120802f55ed5e14f61c730871b5e99d637054ff7f5ba0c1b2cc52b154e3baf80a000a\"}},\"type\":\"team.rotate_key\",\"version\":2},\"ctime\":1497238535,\"expire_in\":157680000,\"prev\":\"8364cf4f555babed7288e9581051ea41871c701e16e9b7af24544ad63735a2fe\",\"seq_type\":3,\"seqno\":3,\"tag\":\"signature\"}","version":2,"uid":"4bf92804c02fb7d2cd36a6d420d6f619"}],"box":{"nonce":"5VPBQypqrcLiuW1i6fVROxmuBQUAAAAD","sender_kid":"012112f29aa42e14053a057a790a198a5b7fb25512c8458b4b32d7bbd04c0d52093b0a","generation":2,"ctext":"HgbVY5cswOvxu9PMOP75NdYqftGtgenRABKtjHgervLK6/oaF1vTW2U9vt+0JixD","per_user_key_seqno":3},"prevs":{"2":"9301c418e553c1432a6aadc2e2b96d62e9f5513b19ae050500000000c4304e966d60d2ccee5433fa1cf08f11de02b0d4e749798925d925896edc6c0b12288a975db3b29f6efed165b38775b64d42"},"reader_key_masks":[{"mask":"gKBbFV3J3L0j/gFtruyCKpZHf7Y837Q2ezFtrAK6xIE=","application":1,"generation":1},{"mask":"ZYdXI0jfXscYwgXO3J3A1T9YRJ+GlkNvtEZJ4nvmKbk=","application":2,"generation":1},{"mask":"Dq4iXhUs9BxX1DHYP7wE/vFOpG4SABwzHZRQeavnKjM=","application":1,"generation":2},{"mask":"AEedcZX7wZyvyAOyc9EQINr3MyqbKMKXLfZVHHZqz7U=","application":2,"generation":2}],"id":"64d27654bef64bdb3d78d84f186c4224","name":{"parts":["t_9d6d1e37"]},"csrf_token":"lgHZIDRiZjkyODA0YzAyZmI3ZDJjZDM2YTZkNDIwZDZmNjE5zlk+C/7OAAFRgMDEIFLYZSdoOin9JRKgyjN8z/JMVQ4Az3O1ZcUyT43DTlXV"} 21 ` 22 23 // A chain with a change_membership link, generated via: `change_membership_promote_to_writer_happy_path` 24 const teamChain2 = ` 25 {"status":{"code":0,"name":"OK"},"chain":[{"seqno":1,"sig":"g6Rib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEg0n4BukVK2MAN0FR3OKg2LjyIRb927JAVdNeKFKxDRTsKp3BheWxvYWTEJ5UCAcDEIKkpvmNu1RlxqBwYtqk4eG/jKv7KD/GllQ23k/Dd6VB1IaNzaWfEQCNHcFQwf9uDBjhzyXDydfUn/7QK1MgC4T2xW/4hywf9vXdVLJ3sPWb+gvk2ZoPCdiwmiAl3CCMiUIuaZrEIawSoc2lnX3R5cGUgo3RhZ80CAqd2ZXJzaW9uAQ==","payload_json":"{\"body\":{\"key\":{\"eldest_kid\":\"0120d27e01ba454ad8c00dd0547738a8362e3c8845bf76ec901574d78a14ac43453b0a\",\"host\":\"keybase.io\",\"kid\":\"0120d27e01ba454ad8c00dd0547738a8362e3c8845bf76ec901574d78a14ac43453b0a\",\"uid\":\"99759da4f968b16121ece44652f01a19\",\"username\":\"d_6d4e925d\"},\"team\":{\"id\":\"5d2c9db17c2309bf818ceefece77b624\",\"members\":{\"admin\":[\"b720a648e02b99c10d50de0c4f265419\"],\"owner\":[\"99759da4f968b16121ece44652f01a19\"],\"reader\":[\"c8f463c79c83fec675c398b6aa3fa719\"],\"writer\":[\"921f0e1f2632277cc1fa6600e0906819\"]},\"name\":\"t_bfaadb41\",\"per_team_key\":{\"encryption_kid\":\"01218ca00b08b4ee5729d957cf14155098b74199588bb5eee778ad1eae58bce26c370a\",\"generation\":1,\"reverse_sig\":\"g6Rib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEgiyRSCOiVHN56vT9eusVXlCTlHtCVH+FKyKeZbJzPiuUKp3BheWxvYWTFA3R7ImJvZHkiOnsia2V5Ijp7ImVsZGVzdF9raWQiOiIwMTIwZDI3ZTAxYmE0NTRhZDhjMDBkZDA1NDc3MzhhODM2MmUzYzg4NDViZjc2ZWM5MDE1NzRkNzhhMTRhYzQzNDUzYjBhIiwiaG9zdCI6ImtleWJhc2UuaW8iLCJraWQiOiIwMTIwZDI3ZTAxYmE0NTRhZDhjMDBkZDA1NDc3MzhhODM2MmUzYzg4NDViZjc2ZWM5MDE1NzRkNzhhMTRhYzQzNDUzYjBhIiwidWlkIjoiOTk3NTlkYTRmOTY4YjE2MTIxZWNlNDQ2NTJmMDFhMTkiLCJ1c2VybmFtZSI6ImRfNmQ0ZTkyNWQifSwidGVhbSI6eyJpZCI6IjVkMmM5ZGIxN2MyMzA5YmY4MThjZWVmZWNlNzdiNjI0IiwibWVtYmVycyI6eyJhZG1pbiI6WyJiNzIwYTY0OGUwMmI5OWMxMGQ1MGRlMGM0ZjI2NTQxOSJdLCJvd25lciI6WyI5OTc1OWRhNGY5NjhiMTYxMjFlY2U0NDY1MmYwMWExOSJdLCJyZWFkZXIiOlsiYzhmNDYzYzc5YzgzZmVjNjc1YzM5OGI2YWEzZmE3MTkiXSwid3JpdGVyIjpbIjkyMWYwZTFmMjYzMjI3N2NjMWZhNjYwMGUwOTA2ODE5Il19LCJuYW1lIjoidF9iZmFhZGI0MSIsInBlcl90ZWFtX2tleSI6eyJlbmNyeXB0aW9uX2tpZCI6IjAxMjE4Y2EwMGIwOGI0ZWU1NzI5ZDk1N2NmMTQxNTUwOThiNzQxOTk1ODhiYjVlZWU3NzhhZDFlYWU1OGJjZTI2YzM3MGEiLCJnZW5lcmF0aW9uIjoxLCJyZXZlcnNlX3NpZyI6bnVsbCwic2lnbmluZ19raWQiOiIwMTIwOGIyNDUyMDhlODk1MWNkZTdhYmQzZjVlYmFjNTU3OTQyNGU1MWVkMDk1MWZlMTRhYzhhNzk5NmM5Y2NmOGFlNTBhIn19LCJ0eXBlIjoidGVhbS5yb290IiwidmVyc2lvbiI6Mn0sImN0aW1lIjoxNDk3MjM5NTY2LCJleHBpcmVfaW4iOjE1NzY4MDAwMCwicHJldiI6bnVsbCwic2VxX3R5cGUiOjMsInNlcW5vIjoxLCJ0YWciOiJzaWduYXR1cmUifaNzaWfEQIhIqjwqQQFY8WglSLtvZu1hpncnMutA/jLaFmQJWcjdoMilr4ttLg3wlxKm6m+zWJC0Y9tiYBeHqGLYj5Mmkgaoc2lnX3R5cGUgo3RhZ80CAqd2ZXJzaW9uAQ==\",\"signing_kid\":\"01208b245208e8951cde7abd3f5ebac5579424e51ed0951fe14ac8a7996c9ccf8ae50a\"}},\"type\":\"team.root\",\"version\":2},\"ctime\":1497239566,\"expire_in\":157680000,\"prev\":null,\"seq_type\":3,\"seqno\":1,\"tag\":\"signature\"}","version":2,"uid":"99759da4f968b16121ece44652f01a19"},{"seqno":2,"sig":"g6Rib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEg0n4BukVK2MAN0FR3OKg2LjyIRb927JAVdNeKFKxDRTsKp3BheWxvYWTESJUCAsQgaCisMgGb2MAyfqV80hthgsO25NQIAYK7ARn5pjCDP1HEIKj3ZEpkyhQYH2mYtiHiXQVe7V5D4qgNwupJ5Nr/2nhcI6NzaWfEQNKql5dvPsQJK+pZKVGiLWS723t9SgaaDFx6NicXJzOM0VnLHKnql50wUcY/KsJOCqUIpKvJmNj6ogbwN/ljTwaoc2lnX3R5cGUgo3RhZ80CAqd2ZXJzaW9uAQ==","payload_json":"{\"body\":{\"key\":{\"eldest_kid\":\"0120d27e01ba454ad8c00dd0547738a8362e3c8845bf76ec901574d78a14ac43453b0a\",\"host\":\"keybase.io\",\"kid\":\"0120d27e01ba454ad8c00dd0547738a8362e3c8845bf76ec901574d78a14ac43453b0a\",\"uid\":\"99759da4f968b16121ece44652f01a19\",\"username\":\"d_6d4e925d\"},\"team\":{\"id\":\"5d2c9db17c2309bf818ceefece77b624\",\"members\":{\"writer\":[\"c8f463c79c83fec675c398b6aa3fa719\"]}},\"type\":\"team.change_membership\",\"version\":2},\"ctime\":1497239567,\"expire_in\":157680000,\"prev\":\"6828ac32019bd8c0327ea57cd21b6182c3b6e4d4080182bb0119f9a630833f51\",\"seq_type\":3,\"seqno\":2,\"tag\":\"signature\"}","version":2,"uid":"99759da4f968b16121ece44652f01a19"}],"box":{"nonce":"hdDTz9Scb6dWbe+BZsyZaXx76aQAAAAB","sender_kid":"0121a4f15b1009430ff69224c0c659eba66d61ca743b0d661a79075c8ca63a6e535d0a","generation":1,"ctext":"4Dz4i3Wzdj/BdH/kYCXvFnl1XKCqhxc58Z53j9VDloy/KG2ZzH204Sw5Q1xkzFkV","per_user_key_seqno":3},"prevs":{},"reader_key_masks":[{"mask":"tewSORjnVoyuHKR6ztsND+MNP2Pp9skEEEYmMBIQ5cY=","application":1,"generation":1},{"mask":"x7ZvY+WfK6WaJOCulxfOpdLgBEyuzSc8KgyIQGxT2uQ=","application":2,"generation":1}],"id":"5d2c9db17c2309bf818ceefece77b624","name":{"parts":["t_bfaadb41"]},"csrf_token":"lgHZIDk5NzU5ZGE0Zjk2OGIxNjEyMWVjZTQ0NjUyZjAxYTE5zlk+EA3OAAFRgMDEIKCIaEA5GV5wng89rpM0UlLiqxcOyNIVk8KdsEjQpmAm"} 26 ` 27 28 // A chain with an invite and a cancelation, as generated via: `cancel_invite_happy_path` 29 const teamChainWithInvites = ` 30 {"status":{"code":0,"name":"OK"},"chain":[{"seqno":1,"sig":"g6Rib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEglAdcaNPA48Z7qTCfxDuSWqaFhsz2wnhzP4XAf5xi8cEKp3BheWxvYWTEJ5UCAcDEIEi+9D4UalDtumiKbRRbMIx6b3w7pJ/wE/+P50FWPLiwIaNzaWfEQGBtlFet2J8qB9rUjGAFU8w8WBECB2zn5Tr5g1PbXJK3q6uG0GPWbUFrsY8DCopgKcucthrz0/58ehnwpvbwjAyoc2lnX3R5cGUgo3RhZ80CAqd2ZXJzaW9uAQ==","payload_json":"{\"body\":{\"key\":{\"eldest_kid\":\"012094075c68d3c0e3c67ba9309fc43b925aa68586ccf6c278733f85c07f9c62f1c10a\",\"host\":\"keybase.io\",\"kid\":\"012094075c68d3c0e3c67ba9309fc43b925aa68586ccf6c278733f85c07f9c62f1c10a\",\"uid\":\"934b8105dc1bd94d8be109b08ef5c119\",\"username\":\"a66714531\"},\"merkle_root\":{\"ctime\":1499358688,\"hash\":\"8d60b173eadcb63f20fd6089fed7c1f4031b805e3892ef6c4be432737e6470ff9d1dbd48d6e81d33dd6480b56b6451775c4d266ba2f12bacc00a758f95614844\",\"hash_meta\":\"98d75dd2b3b27dba1599babdfdbd0b7cac312c2c9b0d96ad438277bef5454566\",\"seqno\":332458},\"team\":{\"id\":\"7ebe4dbfe458fde9a5e2ffdc4eb96a24\",\"members\":{\"owner\":[\"934b8105dc1bd94d8be109b08ef5c119\"],\"writer\":[\"d71179f6b33171b72695ee23a44ecc19\"]},\"name\":\"t_efc95e26\",\"per_team_key\":{\"encryption_kid\":\"0121c41ecfba3ec588c029a096adc78f9419c935059c8a694ac30c0c7f30b499485e0a\",\"generation\":1,\"reverse_sig\":\"g6Rib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEg/jl9IghbR8CHSTUp2sYPIBLOEMa4b9enId/u5nD/OMoKp3BheWxvYWTFBCN7ImJvZHkiOnsia2V5Ijp7ImVsZGVzdF9raWQiOiIwMTIwOTQwNzVjNjhkM2MwZTNjNjdiYTkzMDlmYzQzYjkyNWFhNjg1ODZjY2Y2YzI3ODczM2Y4NWMwN2Y5YzYyZjFjMTBhIiwiaG9zdCI6ImtleWJhc2UuaW8iLCJraWQiOiIwMTIwOTQwNzVjNjhkM2MwZTNjNjdiYTkzMDlmYzQzYjkyNWFhNjg1ODZjY2Y2YzI3ODczM2Y4NWMwN2Y5YzYyZjFjMTBhIiwidWlkIjoiOTM0YjgxMDVkYzFiZDk0ZDhiZTEwOWIwOGVmNWMxMTkiLCJ1c2VybmFtZSI6ImE2NjcxNDUzMSJ9LCJtZXJrbGVfcm9vdCI6eyJjdGltZSI6MTQ5OTM1ODY4OCwiaGFzaCI6IjhkNjBiMTczZWFkY2I2M2YyMGZkNjA4OWZlZDdjMWY0MDMxYjgwNWUzODkyZWY2YzRiZTQzMjczN2U2NDcwZmY5ZDFkYmQ0OGQ2ZTgxZDMzZGQ2NDgwYjU2YjY0NTE3NzVjNGQyNjZiYTJmMTJiYWNjMDBhNzU4Zjk1NjE0ODQ0IiwiaGFzaF9tZXRhIjoiOThkNzVkZDJiM2IyN2RiYTE1OTliYWJkZmRiZDBiN2NhYzMxMmMyYzliMGQ5NmFkNDM4Mjc3YmVmNTQ1NDU2NiIsInNlcW5vIjozMzI0NTh9LCJ0ZWFtIjp7ImlkIjoiN2ViZTRkYmZlNDU4ZmRlOWE1ZTJmZmRjNGViOTZhMjQiLCJtZW1iZXJzIjp7Im93bmVyIjpbIjkzNGI4MTA1ZGMxYmQ5NGQ4YmUxMDliMDhlZjVjMTE5Il0sIndyaXRlciI6WyJkNzExNzlmNmIzMzE3MWI3MjY5NWVlMjNhNDRlY2MxOSJdfSwibmFtZSI6InRfZWZjOTVlMjYiLCJwZXJfdGVhbV9rZXkiOnsiZW5jcnlwdGlvbl9raWQiOiIwMTIxYzQxZWNmYmEzZWM1ODhjMDI5YTA5NmFkYzc4Zjk0MTljOTM1MDU5YzhhNjk0YWMzMGMwYzdmMzBiNDk5NDg1ZTBhIiwiZ2VuZXJhdGlvbiI6MSwicmV2ZXJzZV9zaWciOm51bGwsInNpZ25pbmdfa2lkIjoiMDEyMGZlMzk3ZDIyMDg1YjQ3YzA4NzQ5MzUyOWRhYzYwZjIwMTJjZTEwYzZiODZmZDdhNzIxZGZlZWU2NzBmZjM4Y2EwYSJ9fSwidHlwZSI6InRlYW0ucm9vdCIsInZlcnNpb24iOjJ9LCJjdGltZSI6MTQ5OTM1ODY4OCwiZXhwaXJlX2luIjoxNTc2ODAwMDAsInByZXYiOm51bGwsInNlcV90eXBlIjozLCJzZXFubyI6MSwidGFnIjoic2lnbmF0dXJlIn2jc2lnxEDB7QWbAjIcXgVqq6GnGXNK/IbwNMDq9EVTOfUGQYk63CSRL3zLKCmQh0s3qdPiT5SXNipSBXdX+pvp8/cK1wILqHNpZ190eXBlIKN0YWfNAgKndmVyc2lvbgE=\",\"signing_kid\":\"0120fe397d22085b47c087493529dac60f2012ce10c6b86fd7a721dfeee670ff38ca0a\"}},\"type\":\"team.root\",\"version\":2},\"ctime\":1499358688,\"expire_in\":157680000,\"prev\":null,\"seq_type\":3,\"seqno\":1,\"tag\":\"signature\"}","version":2,"uid":"934b8105dc1bd94d8be109b08ef5c119"},{"seqno":2,"sig":"g6Rib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEglAdcaNPA48Z7qTCfxDuSWqaFhsz2wnhzP4XAf5xi8cEKp3BheWxvYWTESJUCAsQg7vTbqMWQF4drUC6qX8AT4D8QyRBuebus07XiQT9rdD/EIKpkXqyCDdvYFx8b3A0ROEjwmgS26msj5nVkjPPWpwrFKKNzaWfEQNVi98WQKl9JvPfQxAU8QtxIajxKgwUVwKMcpRoAlfViMXHizVl7EQbnPtAL7AfE26HGd2pVFzCJgyP19Smzpgqoc2lnX3R5cGUgo3RhZ80CAqd2ZXJzaW9uAQ==","payload_json":"{\"body\":{\"key\":{\"eldest_kid\":\"012094075c68d3c0e3c67ba9309fc43b925aa68586ccf6c278733f85c07f9c62f1c10a\",\"host\":\"keybase.io\",\"kid\":\"012094075c68d3c0e3c67ba9309fc43b925aa68586ccf6c278733f85c07f9c62f1c10a\",\"uid\":\"934b8105dc1bd94d8be109b08ef5c119\",\"username\":\"a66714531\"},\"merkle_root\":{\"ctime\":1499358688,\"hash\":\"1dab3ca40bb0eccc8561e20f356bdd4c46cca79e418ccc4280d59ebdac022180d547f8765b1f89fc650c535d65516c96b5e4fc811383b711df550af459dc8deb\",\"hash_meta\":\"96d350830b218ac64ad415c8d94f219a85813d451a6d69fd358badb353ac69b1\",\"seqno\":332460},\"team\":{\"admin\":{\"seq_type\":3,\"seqno\":1,\"team_id\":\"7ebe4dbfe458fde9a5e2ffdc4eb96a24\"},\"id\":\"7ebe4dbfe458fde9a5e2ffdc4eb96a24\",\"invites\":{\"admin\":[{\"id\":\"6acd369ec6f5649c4ef83c8753b3aa27\",\"name\":\"u_8114060fcef4\",\"type\":\"twitter\"}],\"reader\":[{\"id\":\"117b4f1d1048042cb67e204c84d07927\",\"name\":\"u_8114060fcef4\",\"type\":\"reddit\"}],\"writer\":[{\"id\":\"4f66ee0fa60ecb10b9f59ff6b7157527\",\"name\":\"max+8114060fcef4@keyba.se\",\"type\":\"email\"},{\"id\":\"b90e024124ddd80870759bae42143227\",\"name\":\"u_8114060fcef4\",\"type\":\"rooter\"}]}},\"type\":\"team.invite\",\"version\":2},\"ctime\":1499358688,\"expire_in\":157680000,\"prev\":\"eef4dba8c59017876b502eaa5fc013e03f10c9106e79bbacd3b5e2413f6b743f\",\"seq_type\":3,\"seqno\":2,\"tag\":\"signature\"}","version":2,"uid":"934b8105dc1bd94d8be109b08ef5c119"},{"seqno":3,"sig":"g6Rib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEglAdcaNPA48Z7qTCfxDuSWqaFhsz2wnhzP4XAf5xi8cEKp3BheWxvYWTESJUCA8Qg0sz2c4Djy6V9F8p/maoKpNc4XLhuY+eU585IPs3mOdrEIMS//+b6ohtqz57toWOEF4HKUAHyBQYvGAV9h9NfaszqKKNzaWfEQLf3O7lXJFVK/fetuCPuYqBUeITs7aVBmOVoZm02xRNzNw940jZRAqFZrX7c/6lqqC/ARl+5iLdnbwkErRjcWAmoc2lnX3R5cGUgo3RhZ80CAqd2ZXJzaW9uAQ==","payload_json":"{\"body\":{\"key\":{\"eldest_kid\":\"012094075c68d3c0e3c67ba9309fc43b925aa68586ccf6c278733f85c07f9c62f1c10a\",\"host\":\"keybase.io\",\"kid\":\"012094075c68d3c0e3c67ba9309fc43b925aa68586ccf6c278733f85c07f9c62f1c10a\",\"uid\":\"934b8105dc1bd94d8be109b08ef5c119\",\"username\":\"a66714531\"},\"merkle_root\":{\"ctime\":1499358688,\"hash\":\"fdc2a6eb4d685e8b8048946eaf62fe1b5caddb84bcbb1207c1ccf5e65f1d656f5feb4cfc467c121966d6adb128dd8c50b92b64a523f08f8b566b8600df7d9a35\",\"hash_meta\":\"d5818082cee4e8add8081fb605c9cb06cbdc6c4343085d07f9b89e61d2e671fa\",\"seqno\":332461},\"team\":{\"admin\":{\"seq_type\":3,\"seqno\":1,\"team_id\":\"7ebe4dbfe458fde9a5e2ffdc4eb96a24\"},\"id\":\"7ebe4dbfe458fde9a5e2ffdc4eb96a24\",\"invites\":{\"cancel\":[\"117b4f1d1048042cb67e204c84d07927\"]}},\"type\":\"team.invite\",\"version\":2},\"ctime\":1499358689,\"expire_in\":157680000,\"prev\":\"d2ccf67380e3cba57d17ca7f99aa0aa4d7385cb86e63e794e7ce483ecde639da\",\"seq_type\":3,\"seqno\":3,\"tag\":\"signature\"}","version":2,"uid":"934b8105dc1bd94d8be109b08ef5c119"}],"box":{"nonce":"u0oZgbBS5lMOVM9po9vfjwGoxAgAAAAB","sender_kid":"0121d11a0dcb4e0fb545087ef58ff0366ed348f391c9d823ad5ed6616895cc9911030a","generation":1,"ctext":"c9wOION/iDRyTkaN1oTmrSsr/gAgIdULzPqroisEEWU3aCjkGBuu27NqGfXIAe5T","per_user_key_seqno":3},"prevs":{},"reader_key_masks":[{"mask":"2UljdLeivosnFh5Zx0HhPMNYD2n4kz4+cKisRp83q+M=","application":1,"generation":1},{"mask":"gIQztS2j/k26inXIgguOjuk3/AaGfc2thu5pDhtgBvw=","application":2,"generation":1},{"mask":"XGmeo2qgtH62ihZ6hTfmlvGm1PiIkpyyvicUt8VSepk=","application":3,"generation":1}],"id":"7ebe4dbfe458fde9a5e2ffdc4eb96a24","name":{"parts":["t_efc95e26"]},"csrf_token":"lgHZIDkzNGI4MTA1ZGMxYmQ5NGQ4YmUxMDliMDhlZjVjMTE5zlleZeDOAAFRgMDEIGUrg9Ioa5XufDOGCznLyJGlYgcc0d9GzaudNoIqDU8S"} 31 ` 32 33 type DeconstructJig struct { 34 Chain []json.RawMessage `json:"chain"` 35 } 36 37 func TestTeamSigChainParse(t *testing.T) { 38 tc := SetupTest(t, "test_team_chains", 1) 39 defer tc.Cleanup() 40 41 var jig DeconstructJig 42 err := json.Unmarshal([]byte(teamChain1), &jig) 43 require.NoError(t, err) 44 45 for _, link := range jig.Chain { 46 // t.Logf("link: %v", string(link)) 47 48 chainLink, err := ParseTeamChainLink(string(link)) 49 require.NoError(t, err) 50 51 t.Logf("chainLink: %v", spew.Sdump(chainLink)) 52 53 if len(chainLink.Payload) > 0 { 54 payload, err := chainLink.UnmarshalPayload() 55 require.NoError(t, err) 56 t.Logf("payload: %v", spew.Sdump(payload)) 57 } else { 58 t.Logf("payload stubbed") 59 } 60 } 61 } 62 63 func assertHighSeqForTeam(t *testing.T, tc libkb.TestContext, teamID *keybase1.TeamID, expected int) { 64 team, err := Load(context.TODO(), tc.G, keybase1.LoadTeamArg{ 65 ID: *teamID, 66 ForceRepoll: true, 67 }) 68 require.NoError(t, err) 69 actual := int(team.chain().GetLatestHighSeqno()) 70 require.Equal(t, expected, actual) 71 } 72 73 func TestTeamSigChainHighLinks(t *testing.T) { 74 tc := SetupTest(t, "team_sig_chain_high_links", 1) 75 defer tc.Cleanup() 76 ctx := context.TODO() 77 78 // Create some users. The owner is last so that it has the active session. 79 u2, err := kbtest.CreateAndSignupFakeUser("we", tc.G) // admin 80 require.NoError(t, err) 81 u3, err := kbtest.CreateAndSignupFakeUser("ji", tc.G) // non-admin 82 require.NoError(t, err) 83 u4, err := kbtest.CreateAndSignupFakeUser("botua", tc.G) // bot 84 require.NoError(t, err) 85 u5, err := kbtest.CreateAndSignupFakeUser("rua", tc.G) // restricted_bot 86 require.NoError(t, err) 87 u1, err := kbtest.CreateAndSignupFakeUser("je", tc.G) // owner 88 require.NoError(t, err) 89 t.Logf("create the team...") 90 // Create a team. This creates the first high link. 91 teamName := u1.Username + "t" 92 teamNameObj, err := keybase1.TeamNameFromString(teamName) 93 require.NoError(t, err) 94 teamID, err := CreateRootTeam(ctx, tc.G, teamName, keybase1.TeamSettings{}) 95 require.NoError(t, err) 96 assertHighSeqForTeam(t, tc, teamID, 1) 97 98 t.Logf("adding new reader...") 99 // Adding a new reader is not a high link, so the lastest high seq won't change. 100 _, err = AddMember(ctx, tc.G, teamName, u3.Username, keybase1.TeamRole_READER, nil) 101 require.NoError(t, err) 102 assertHighSeqForTeam(t, tc, teamID, 1) 103 104 // Adding a new bot is not a high link, so the latest high seq won't 105 // change. Note adding a RESTRICTEDBOT results in two sigs being added, one 106 // for the membership addition and a second for bot_settings. 107 _, err = AddMember(ctx, tc.G, teamName, u4.Username, keybase1.TeamRole_RESTRICTEDBOT, &keybase1.TeamBotSettings{}) 108 require.NoError(t, err) 109 assertHighSeqForTeam(t, tc, teamID, 1) 110 111 _, err = AddMember(ctx, tc.G, teamName, u5.Username, keybase1.TeamRole_BOT, nil) 112 require.NoError(t, err) 113 assertHighSeqForTeam(t, tc, teamID, 1) 114 115 t.Logf("adding new admin...") 116 // Adding a new admin IS a high link, so we should jump to 6. 117 _, err = AddMember(ctx, tc.G, teamName, u2.Username, keybase1.TeamRole_ADMIN, nil) 118 require.NoError(t, err) 119 assertHighSeqForTeam(t, tc, teamID, 6) 120 121 t.Logf("promoting from admin to owner...") 122 // Promoting from admin to owner is a high link. 123 err = EditMember(ctx, tc.G, teamName, u2.Username, keybase1.TeamRole_OWNER, nil) 124 require.NoError(t, err) 125 assertHighSeqForTeam(t, tc, teamID, 7) 126 127 t.Logf("demoting from owner to admin...") 128 // Demoting from owner to admin is a high link. 129 err = EditMember(ctx, tc.G, teamName, u2.Username, keybase1.TeamRole_ADMIN, nil) 130 require.NoError(t, err) 131 assertHighSeqForTeam(t, tc, teamID, 8) 132 133 t.Logf("adding new subteam...") 134 // Creating a subteam is not a high link for the parent team 135 // but it is for the subteam. The link types are different 136 // "team.root" and "team.subteam_head" so we should check both teams. 137 sub := "sub" 138 subteamID, err := CreateSubteam(ctx, tc.G, sub, teamNameObj, keybase1.TeamRole_ADMIN) 139 require.NoError(t, err) 140 assertHighSeqForTeam(t, tc, subteamID, 1) 141 assertHighSeqForTeam(t, tc, teamID, 8) 142 143 t.Logf("adding new admin to subteam...") 144 // Adding an admin to the subteam is a high link for the subteam but not 145 // the parent. Primarily, we do this to verify that high links advance 146 // the same way on subteams (since there are places that default to a 147 // value of 1 for sequence number). It's overkill to test any more than 148 // just this on the subteam since it should work the same way. 149 _, err = AddMemberByID(ctx, tc.G, *subteamID, u3.Username, keybase1.TeamRole_ADMIN, nil, nil /* emailInviteMsg */) 150 require.NoError(t, err) 151 assertHighSeqForTeam(t, tc, subteamID, 2) 152 assertHighSeqForTeam(t, tc, teamID, 8) 153 154 t.Logf("demoting admin to writer...") 155 // Back to the root team... downgrading an admin IS a high link for the root team 156 // but it is not one for the subteam, because the subteam needs to care about it's 157 // parent's high links anyway. 158 err = EditMember(ctx, tc.G, teamName, u2.Username, keybase1.TeamRole_WRITER, nil) 159 require.NoError(t, err) 160 assertHighSeqForTeam(t, tc, teamID, 10) 161 assertHighSeqForTeam(t, tc, subteamID, 2) 162 163 t.Logf("demoting admin...") 164 // Back to the root team... downgrading an admin IS a high link for the root team 165 // but it is not one for the subteam, because the subteam needs to care about it's 166 // parent's high links anyway. 167 err = EditMember(ctx, tc.G, teamName, u2.Username, keybase1.TeamRole_WRITER, nil) 168 require.NoError(t, err) 169 assertHighSeqForTeam(t, tc, teamID, 10) 170 assertHighSeqForTeam(t, tc, subteamID, 2) 171 172 t.Logf("rotating keys...") 173 // Rotated keys do not create high links. 174 err = RotateKeyVisible(ctx, tc.G, *teamID) 175 require.NoError(t, err) 176 assertHighSeqForTeam(t, tc, teamID, 10) 177 assertHighSeqForTeam(t, tc, subteamID, 2) 178 179 t.Logf("deleting subteam...") 180 // Deleting a subteam will not create a high link. 181 err = Delete(ctx, tc.G, &teamsUI{}, *subteamID) 182 require.NoError(t, err) 183 assertHighSeqForTeam(t, tc, teamID, 10) 184 } 185 186 func TestTeamSigChainPlay1(t *testing.T) { 187 tc := SetupTest(t, "test_team_chains", 1) 188 defer tc.Cleanup() 189 190 var jig DeconstructJig 191 err := json.Unmarshal([]byte(teamChain1), &jig) 192 require.NoError(t, err) 193 194 var chainLinks []SCChainLink 195 for i, link := range jig.Chain { 196 // t.Logf("link: %v", string(link)) 197 198 chainLink, err := ParseTeamChainLink(string(link)) 199 require.NoError(t, err) 200 201 t.Logf("%v chainLink: %v", i, spew.Sdump(chainLink)) 202 chainLinks = append(chainLinks, chainLink) 203 } 204 205 consumer := NewUserVersion(keybase1.UID("4bf92804c02fb7d2cd36a6d420d6f619"), 1) 206 var state *TeamSigChainState 207 for _, cLink := range chainLinks { 208 link, err := unpackChainLink(&cLink) 209 require.NoError(t, err) 210 var signer *keybase1.UserVersion 211 if !link.isStubbed() { 212 // Assume the signing user has never reset. 213 signer = &keybase1.UserVersion{ 214 Uid: link.inner.Body.Key.UID, 215 EldestSeqno: keybase1.Seqno(1), 216 } 217 } 218 newState, err := AppendChainLink(context.TODO(), tc.G, consumer, state, link, signerToX(signer)) 219 require.NoError(t, err) 220 state = &newState 221 } 222 223 // Check once before and after serializing and deserializing 224 mctx := libkb.NewMetaContextForTest(tc) 225 for i := 0; i < 2; i++ { 226 if i == 0 { 227 t.Logf("testing fresh") 228 } else { 229 t.Logf("testing serde") 230 } 231 232 require.Equal(t, "t_9d6d1e37", string(state.LatestLastNamePart())) 233 require.False(t, state.IsSubteam()) 234 ptk, err := state.GetLatestPerTeamKey(mctx) 235 require.NoError(t, err) 236 require.Equal(t, keybase1.PerTeamKeyGeneration(2), ptk.Gen) 237 require.Equal(t, keybase1.Seqno(3), ptk.Seqno) 238 require.Equal(t, "0120802f55ed5e14f61c730871b5e99d637054ff7f5ba0c1b2cc52b154e3baf80a000a", string(ptk.SigKID)) 239 require.Equal(t, "0121e2511cbfb0418187a8e19183a1cd92637bc83fe116d1eb8984f52394495b5f120a", string(ptk.EncKID)) 240 require.Equal(t, keybase1.Seqno(3), state.GetLatestSeqno()) 241 require.Equal(t, state.GetLatestHighSeqno(), keybase1.Seqno(1)) 242 require.Equal(t, state.GetLatestHighLinkID(), keybase1.LinkID("62cb761ad346a3d28be1eed107c35548a2dd6fade9219a38591841f62d586cfe")) 243 244 checkRole := func(uid keybase1.UID, role keybase1.TeamRole) { 245 uv := NewUserVersion(uid, 1) 246 r, err := state.GetUserRole(uv) 247 require.NoError(t, err) 248 require.Equal(t, role, r) 249 } 250 251 checkRole("5bf82de4331b50b32cbbcfeadc2f3119", keybase1.TeamRole_OWNER) // the "doug" user 252 checkRole("53e315afb4b419931b0a6a1eaa09e219", keybase1.TeamRole_ADMIN) // the "charlie" user 253 checkRole("4bf92804c02fb7d2cd36a6d420d6f619", keybase1.TeamRole_WRITER) // the "bob" user 254 checkRole("13e18aeafa4df6c94bf6af7d7bb98d19", keybase1.TeamRole_READER) // the "alice" user 255 checkRole("popeye", keybase1.TeamRole_NONE) 256 257 linkIDProto := state.GetLatestLinkID() 258 require.Equal(t, "c94234a4855e47d5833a7f43b221ca5e5ccf9970465b77167a793366acf39b16", string(linkIDProto)) 259 linkIDLibkb, err := libkb.ImportLinkID(linkIDProto) 260 require.NoError(t, err) 261 require.Equal(t, linkIDProto, linkIDLibkb.Export()) 262 263 // Reserialize 264 bs, err := encode(state.inner) 265 require.NoError(t, err, "encode") 266 state = &TeamSigChainState{} 267 err = decode(bs, &state.inner) 268 require.NoError(t, err, "decode") 269 } 270 } 271 272 func TestTeamSigChainPlay2(t *testing.T) { 273 tc := SetupTest(t, "test_team_chains", 1) 274 defer tc.Cleanup() 275 276 var jig DeconstructJig 277 err := json.Unmarshal([]byte(teamChain2), &jig) 278 require.NoError(t, err) 279 280 var chainLinks []SCChainLink 281 for i, link := range jig.Chain { 282 // t.Logf("link: %v", string(link)) 283 284 chainLink, err := ParseTeamChainLink(string(link)) 285 require.NoError(t, err) 286 287 t.Logf("%v chainLink: %v", i, spew.Sdump(chainLink)) 288 chainLinks = append(chainLinks, chainLink) 289 } 290 291 consumer := NewUserVersion("99759da4f968b16121ece44652f01a19", 1) 292 var state *TeamSigChainState 293 for _, cLink := range chainLinks { 294 link, err := unpackChainLink(&cLink) 295 require.NoError(t, err) 296 var signer *keybase1.UserVersion 297 if !link.isStubbed() { 298 // Assume the signing user has never reset. 299 signer = &keybase1.UserVersion{ 300 Uid: link.inner.Body.Key.UID, 301 EldestSeqno: keybase1.Seqno(1), 302 } 303 } 304 newState, err := AppendChainLink(context.TODO(), tc.G, consumer, state, link, signerToX(signer)) 305 require.NoError(t, err) 306 state = &newState 307 } 308 require.NoError(t, err) 309 310 mctx := libkb.NewMetaContextForTest(tc) 311 312 // Check once before and after serializing and deserializing 313 for i := 0; i < 2; i++ { 314 require.Equal(t, "t_bfaadb41", string(state.LatestLastNamePart())) 315 require.False(t, state.IsSubteam()) 316 ptk, err := state.GetLatestPerTeamKey(mctx) 317 require.NoError(t, err) 318 require.Equal(t, keybase1.PerTeamKeyGeneration(1), ptk.Gen) 319 require.Equal(t, keybase1.Seqno(1), ptk.Seqno) 320 require.Equal(t, "01208b245208e8951cde7abd3f5ebac5579424e51ed0951fe14ac8a7996c9ccf8ae50a", string(ptk.SigKID)) 321 require.Equal(t, "01218ca00b08b4ee5729d957cf14155098b74199588bb5eee778ad1eae58bce26c370a", string(ptk.EncKID)) 322 require.Equal(t, keybase1.Seqno(2), state.GetLatestSeqno()) 323 require.Equal(t, state.GetLatestHighSeqno(), keybase1.Seqno(1)) 324 require.Equal(t, state.GetLatestHighLinkID(), keybase1.LinkID("6828ac32019bd8c0327ea57cd21b6182c3b6e4d4080182bb0119f9a630833f51")) 325 326 checkRole := func(uid keybase1.UID, role keybase1.TeamRole) { 327 uv := NewUserVersion(uid, 1) 328 r, err := state.GetUserRole(uv) 329 require.NoError(t, err) 330 require.Equal(t, role, r) 331 } 332 333 checkRole(keybase1.UID("99759da4f968b16121ece44652f01a19"), keybase1.TeamRole_OWNER) // the 'doug' user 334 checkRole(keybase1.UID("b720a648e02b99c10d50de0c4f265419"), keybase1.TeamRole_ADMIN) // the 'charlie' user 335 checkRole(keybase1.UID("921f0e1f2632277cc1fa6600e0906819"), keybase1.TeamRole_WRITER) // the 'bob' user 336 checkRole(keybase1.UID("c8f463c79c83fec675c398b6aa3fa719"), keybase1.TeamRole_WRITER) // changed role for 'alice'; used to be a reader 337 338 xs, err := state.GetUsersWithRole(keybase1.TeamRole_OWNER) 339 require.NoError(t, err) 340 require.Len(t, xs, 1) 341 xs, err = state.GetUsersWithRole(keybase1.TeamRole_WRITER) 342 require.NoError(t, err) 343 require.Len(t, xs, 2) 344 xs, err = state.GetUsersWithRole(keybase1.TeamRole_READER) 345 require.NoError(t, err) 346 require.Len(t, xs, 0) 347 xs, err = state.GetUsersWithRole(keybase1.TeamRole_BOT) 348 require.NoError(t, err) 349 require.Len(t, xs, 0) 350 xs, err = state.GetUsersWithRole(keybase1.TeamRole_RESTRICTEDBOT) 351 require.NoError(t, err) 352 require.Len(t, xs, 0) 353 354 // Reserialize 355 bs, err := encode(state.inner) 356 require.NoError(t, err, "encode") 357 state = &TeamSigChainState{} 358 err = decode(bs, &state.inner) 359 require.NoError(t, err, "decode") 360 } 361 } 362 363 func encode(input interface{}) ([]byte, error) { 364 mh := codec.MsgpackHandle{WriteExt: true} 365 var data []byte 366 enc := codec.NewEncoderBytes(&data, &mh) 367 if err := enc.Encode(input); err != nil { 368 return nil, err 369 } 370 return data, nil 371 } 372 373 func decode(data []byte, res interface{}) error { 374 mh := codec.MsgpackHandle{WriteExt: true} 375 dec := codec.NewDecoderBytes(data, &mh) 376 err := dec.Decode(res) 377 return err 378 } 379 380 func TestTeamSigChainWithInvites(t *testing.T) { 381 tc := SetupTest(t, "test_team_chains", 1) 382 defer tc.Cleanup() 383 384 var jig DeconstructJig 385 err := json.Unmarshal([]byte(teamChainWithInvites), &jig) 386 require.NoError(t, err) 387 388 var chainLinks []SCChainLink 389 for i, link := range jig.Chain { 390 391 chainLink, err := ParseTeamChainLink(string(link)) 392 require.NoError(t, err) 393 394 t.Logf("%v chainLink: %v", i, spew.Sdump(chainLink)) 395 chainLinks = append(chainLinks, chainLink) 396 } 397 398 consumer := NewUserVersion("99759da4f968b16121ece44652f01a19", 1) 399 var state *TeamSigChainState 400 for _, cLink := range chainLinks { 401 link, err := unpackChainLink(&cLink) 402 require.NoError(t, err) 403 var signer *keybase1.UserVersion 404 if !link.isStubbed() { 405 // Assume the signing user has never reset. 406 signer = &keybase1.UserVersion{ 407 Uid: link.inner.Body.Key.UID, 408 EldestSeqno: keybase1.Seqno(1), 409 } 410 } 411 newState, err := AppendChainLink(context.TODO(), tc.G, consumer, state, link, signerToX(signer)) 412 require.NoError(t, err) 413 state = &newState 414 } 415 checkInvite := func(s string, f func(i *keybase1.TeamInvite)) { 416 var i *keybase1.TeamInvite 417 tmpMD, ok := state.FindActiveInviteMDByID(keybase1.TeamInviteID(s)) 418 if ok { 419 tmp := tmpMD.Invite 420 i = &tmp 421 } 422 f(i) 423 } 424 checkInvite("117b4f1d1048042cb67e204c84d07927", func(i *keybase1.TeamInvite) { 425 require.Nil(t, i) 426 }) 427 checkInvite("b90e024124ddd80870759bae42143227", func(i *keybase1.TeamInvite) { 428 require.NotNil(t, i) 429 require.Equal(t, i.Role, keybase1.TeamRole_WRITER) 430 require.Equal(t, i.Type.Sbs(), keybase1.TeamInviteSocialNetwork("rooter")) 431 require.Equal(t, i.Name, keybase1.TeamInviteName("u_8114060fcef4")) 432 }) 433 checkInvite("4f66ee0fa60ecb10b9f59ff6b7157527", func(i *keybase1.TeamInvite) { 434 require.NotNil(t, i) 435 require.Equal(t, i.Role, keybase1.TeamRole_WRITER) 436 typ, err := i.Type.C() 437 require.NoError(t, err) 438 require.Equal(t, typ, keybase1.TeamInviteCategory_EMAIL) 439 require.Equal(t, i.Name, keybase1.TeamInviteName("max+8114060fcef4@keyba.se")) 440 }) 441 checkInvite("6acd369ec6f5649c4ef83c8753b3aa27", func(i *keybase1.TeamInvite) { 442 require.NotNil(t, i) 443 require.Equal(t, i.Role, keybase1.TeamRole_ADMIN) 444 require.Equal(t, i.Type.Sbs(), keybase1.TeamInviteSocialNetwork("twitter")) 445 require.Equal(t, i.Name, keybase1.TeamInviteName("u_8114060fcef4")) 446 }) 447 } 448 449 func signerToX(uv *keybase1.UserVersion) *SignerX { 450 if uv == nil { 451 return nil 452 } 453 return &SignerX{signer: *uv} 454 } 455 456 func TestMemberCtime(t *testing.T) { 457 uv := NewUserVersion("foo", 1) 458 var points []keybase1.UserLogPoint 459 newTeamChainState := func() TeamSigChainState { 460 return TeamSigChainState{ 461 inner: keybase1.TeamSigChainState{ 462 UserLog: map[keybase1.UserVersion][]keybase1.UserLogPoint{ 463 uv: points, 464 }, 465 }, 466 } 467 } 468 469 // nil points 470 tcs := newTeamChainState() 471 ctime := tcs.MemberCtime(uv) 472 require.Nil(t, ctime) 473 474 // user joined as a writer 475 points = append(points, 476 keybase1.UserLogPoint{ 477 Role: keybase1.TeamRole_WRITER, 478 SigMeta: keybase1.SignatureMetadata{ 479 Time: 1, 480 }, 481 }) 482 tcs = newTeamChainState() 483 ctime = tcs.MemberCtime(uv) 484 require.NotNil(t, ctime) 485 require.EqualValues(t, 1, *ctime) 486 487 points = append(points, 488 keybase1.UserLogPoint{ 489 Role: keybase1.TeamRole_NONE, 490 SigMeta: keybase1.SignatureMetadata{ 491 Time: 2, 492 }, 493 }, 494 keybase1.UserLogPoint{ 495 Role: keybase1.TeamRole_ADMIN, 496 SigMeta: keybase1.SignatureMetadata{ 497 Time: 3, 498 }, 499 }) 500 501 // user left and joined later as an admin 502 tcs = newTeamChainState() 503 ctime = tcs.MemberCtime(uv) 504 require.NotNil(t, ctime) 505 require.EqualValues(t, 3, *ctime) 506 507 // user had a bunch of non-NONE roles, we should return the first join time 508 points = nil 509 for i := 0; i < 5; i++ { 510 points = append(points, 511 keybase1.UserLogPoint{ 512 Role: keybase1.TeamRole_WRITER, 513 SigMeta: keybase1.SignatureMetadata{ 514 Time: keybase1.Time(i), 515 }, 516 }) 517 tcs = newTeamChainState() 518 ctime = tcs.MemberCtime(uv) 519 require.NotNil(t, ctime) 520 require.EqualValues(t, 0, *ctime) 521 } 522 }