auth_test.go 75 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475
  1. package foodgroup
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "log/slog"
  9. "testing"
  10. "time"
  11. "github.com/mk6i/open-oscar-server/config"
  12. "github.com/mk6i/open-oscar-server/state"
  13. "github.com/mk6i/open-oscar-server/wire"
  14. "github.com/google/uuid"
  15. "github.com/stretchr/testify/assert"
  16. "github.com/stretchr/testify/mock"
  17. )
  18. func TestAuthService_BUCPLoginRequest(t *testing.T) {
  19. user := state.User{
  20. IdentScreenName: state.NewIdentScreenName("screenName"),
  21. DisplayScreenName: "screenName",
  22. AuthKey: "auth_key",
  23. }
  24. assert.NoError(t, user.HashPassword("the_password"))
  25. cases := []struct {
  26. // name is the unit test name
  27. name string
  28. // advertisedHost is the BOS host the client will connect to upon successful login
  29. advertisedHost string
  30. // cfg is the app configuration
  31. cfg config.Config
  32. // inputSNAC is the SNAC sent from the client to the server
  33. inputSNAC wire.SNAC_0x17_0x02_BUCPLoginRequest
  34. // mockParams is the list of params sent to mocks that satisfy this
  35. // method's dependencies
  36. mockParams mockParams
  37. // createAccount is the function that creates a new user account
  38. createAccount state.CreateAccountFunc
  39. // expectOutput is the SNAC sent from the server to client
  40. expectOutput wire.SNACMessage
  41. // wantErr is the error we expect from the method
  42. wantErr error
  43. // maxConcurrentLoginsPerUser is the maximum concurrent logins per user (only set for MultiConnFlagsRecentClient tests)
  44. maxConcurrentLoginsPerUser int
  45. }{
  46. {
  47. name: "AIM account exists, correct password, login OK, no concurrent logins",
  48. advertisedHost: "127.0.0.1:5190",
  49. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  50. TLVRestBlock: wire.TLVRestBlock{
  51. TLVList: wire.TLVList{
  52. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  53. wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
  54. wire.NewTLVBE(wire.LoginTLVTagsMultiConnFlags, wire.MultiConnFlagsRecentClient),
  55. },
  56. },
  57. },
  58. mockParams: mockParams{
  59. userManagerParams: userManagerParams{
  60. getUserParams: getUserParams{
  61. {
  62. screenName: user.IdentScreenName,
  63. result: &user,
  64. },
  65. },
  66. },
  67. cookieBakerParams: cookieBakerParams{
  68. cookieIssueParams: cookieIssueParams{
  69. {
  70. dataIn: func() []byte {
  71. loginCookie := state.ServerCookie{
  72. ScreenName: user.DisplayScreenName,
  73. MultiConnFlag: uint8(wire.MultiConnFlagsRecentClient),
  74. }
  75. buf := &bytes.Buffer{}
  76. assert.NoError(t, wire.MarshalBE(loginCookie, buf))
  77. return buf.Bytes()
  78. }(),
  79. cookieOut: []byte("the-cookie"),
  80. },
  81. },
  82. },
  83. sessionRetrieverParams: sessionRetrieverParams{
  84. retrieveSessionParams: retrieveSessionParams{
  85. {
  86. screenName: user.IdentScreenName,
  87. result: nil,
  88. },
  89. },
  90. },
  91. },
  92. expectOutput: wire.SNACMessage{
  93. Frame: wire.SNACFrame{
  94. FoodGroup: wire.BUCP,
  95. SubGroup: wire.BUCPLoginResponse,
  96. },
  97. Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
  98. TLVRestBlock: wire.TLVRestBlock{
  99. TLVList: wire.TLVList{
  100. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  101. wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
  102. wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
  103. wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
  104. },
  105. },
  106. },
  107. },
  108. maxConcurrentLoginsPerUser: 2,
  109. },
  110. {
  111. name: "AIM account exists, correct password, login OK, concurrent logins under limit",
  112. advertisedHost: "127.0.0.1:5190",
  113. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  114. TLVRestBlock: wire.TLVRestBlock{
  115. TLVList: wire.TLVList{
  116. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  117. wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
  118. wire.NewTLVBE(wire.LoginTLVTagsMultiConnFlags, wire.MultiConnFlagsRecentClient),
  119. },
  120. },
  121. },
  122. mockParams: mockParams{
  123. userManagerParams: userManagerParams{
  124. getUserParams: getUserParams{
  125. {
  126. screenName: user.IdentScreenName,
  127. result: &user,
  128. },
  129. },
  130. },
  131. cookieBakerParams: cookieBakerParams{
  132. cookieIssueParams: cookieIssueParams{
  133. {
  134. dataIn: func() []byte {
  135. loginCookie := state.ServerCookie{
  136. ScreenName: user.DisplayScreenName,
  137. MultiConnFlag: uint8(wire.MultiConnFlagsRecentClient),
  138. }
  139. buf := &bytes.Buffer{}
  140. assert.NoError(t, wire.MarshalBE(loginCookie, buf))
  141. return buf.Bytes()
  142. }(),
  143. cookieOut: []byte("the-cookie"),
  144. },
  145. },
  146. },
  147. sessionRetrieverParams: sessionRetrieverParams{
  148. retrieveSessionParams: retrieveSessionParams{
  149. {
  150. screenName: user.IdentScreenName,
  151. result: func() *state.Session {
  152. // Create a session with 1 instance, under the limit
  153. sess := state.NewSession()
  154. sess.SetIdentScreenName(user.IdentScreenName)
  155. sess.SetDisplayScreenName(user.DisplayScreenName)
  156. sess.AddInstance()
  157. return sess
  158. }(),
  159. },
  160. },
  161. },
  162. },
  163. expectOutput: wire.SNACMessage{
  164. Frame: wire.SNACFrame{
  165. FoodGroup: wire.BUCP,
  166. SubGroup: wire.BUCPLoginResponse,
  167. },
  168. Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
  169. TLVRestBlock: wire.TLVRestBlock{
  170. TLVList: wire.TLVList{
  171. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  172. wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
  173. wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
  174. wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
  175. },
  176. },
  177. },
  178. },
  179. maxConcurrentLoginsPerUser: 2,
  180. },
  181. {
  182. name: "login fails when concurrent login limit is reached",
  183. advertisedHost: "127.0.0.1:5190",
  184. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  185. TLVRestBlock: wire.TLVRestBlock{
  186. TLVList: wire.TLVList{
  187. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  188. wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
  189. wire.NewTLVBE(wire.LoginTLVTagsMultiConnFlags, wire.MultiConnFlagsRecentClient),
  190. },
  191. },
  192. },
  193. mockParams: mockParams{
  194. userManagerParams: userManagerParams{
  195. getUserParams: getUserParams{
  196. {
  197. screenName: user.IdentScreenName,
  198. result: &user,
  199. },
  200. },
  201. },
  202. sessionRetrieverParams: sessionRetrieverParams{
  203. retrieveSessionParams: retrieveSessionParams{
  204. {
  205. screenName: user.IdentScreenName,
  206. result: func() *state.Session {
  207. // Create a session with 2 instances (the max allowed)
  208. // This will cause InstanceCount() to return 2, which equals the limit of 2
  209. sess := state.NewSession()
  210. sess.SetIdentScreenName(user.IdentScreenName)
  211. sess.SetDisplayScreenName(user.DisplayScreenName)
  212. sess.AddInstance()
  213. sess.AddInstance()
  214. return sess
  215. }(),
  216. },
  217. },
  218. },
  219. },
  220. expectOutput: wire.SNACMessage{
  221. Frame: wire.SNACFrame{
  222. FoodGroup: wire.BUCP,
  223. SubGroup: wire.BUCPLoginResponse,
  224. },
  225. Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
  226. TLVRestBlock: wire.TLVRestBlock{
  227. TLVList: []wire.TLV{
  228. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  229. wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrRateLimitExceeded),
  230. },
  231. },
  232. },
  233. },
  234. maxConcurrentLoginsPerUser: 2,
  235. },
  236. {
  237. name: "ICQ account exists, correct password, login OK",
  238. advertisedHost: "127.0.0.1:5190",
  239. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  240. TLVRestBlock: wire.TLVRestBlock{
  241. TLVList: wire.TLVList{
  242. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  243. wire.NewTLVBE(wire.LoginTLVTagsClientIdentity, "ICQ 2000b"),
  244. wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
  245. },
  246. },
  247. },
  248. mockParams: mockParams{
  249. userManagerParams: userManagerParams{
  250. getUserParams: getUserParams{
  251. {
  252. screenName: user.IdentScreenName,
  253. result: &user,
  254. },
  255. },
  256. },
  257. cookieBakerParams: cookieBakerParams{
  258. cookieIssueParams: cookieIssueParams{
  259. {
  260. dataIn: func() []byte {
  261. loginCookie := state.ServerCookie{
  262. ScreenName: user.DisplayScreenName,
  263. ClientID: "ICQ 2000b",
  264. }
  265. buf := &bytes.Buffer{}
  266. assert.NoError(t, wire.MarshalBE(loginCookie, buf))
  267. return buf.Bytes()
  268. }(),
  269. cookieOut: []byte("the-cookie"),
  270. },
  271. },
  272. },
  273. },
  274. expectOutput: wire.SNACMessage{
  275. Frame: wire.SNACFrame{
  276. FoodGroup: wire.BUCP,
  277. SubGroup: wire.BUCPLoginResponse,
  278. },
  279. Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
  280. TLVRestBlock: wire.TLVRestBlock{
  281. TLVList: wire.TLVList{
  282. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  283. wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
  284. wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
  285. wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
  286. },
  287. },
  288. },
  289. },
  290. },
  291. {
  292. name: "AIM account exists, incorrect password, login fails",
  293. advertisedHost: "127.0.0.1:5190",
  294. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  295. TLVRestBlock: wire.TLVRestBlock{
  296. TLVList: wire.TLVList{
  297. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  298. wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, []byte("bad_password")),
  299. },
  300. },
  301. },
  302. mockParams: mockParams{
  303. userManagerParams: userManagerParams{
  304. getUserParams: getUserParams{
  305. {
  306. screenName: user.IdentScreenName,
  307. result: &user,
  308. },
  309. },
  310. },
  311. },
  312. expectOutput: wire.SNACMessage{
  313. Frame: wire.SNACFrame{
  314. FoodGroup: wire.BUCP,
  315. SubGroup: wire.BUCPLoginResponse,
  316. },
  317. Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
  318. TLVRestBlock: wire.TLVRestBlock{
  319. TLVList: []wire.TLV{
  320. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  321. wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidPassword),
  322. },
  323. },
  324. },
  325. },
  326. },
  327. {
  328. name: "AIM account doesn't exist, login fails",
  329. advertisedHost: "127.0.0.1:5190",
  330. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  331. TLVRestBlock: wire.TLVRestBlock{
  332. TLVList: wire.TLVList{
  333. wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, []byte("password")),
  334. wire.NewTLVBE(wire.LoginTLVTagsScreenName, []byte("non_existent_screen_name")),
  335. },
  336. },
  337. },
  338. mockParams: mockParams{
  339. userManagerParams: userManagerParams{
  340. getUserParams: getUserParams{
  341. {
  342. screenName: state.NewIdentScreenName("non_existent_screen_name"),
  343. result: nil,
  344. },
  345. },
  346. },
  347. },
  348. expectOutput: wire.SNACMessage{
  349. Frame: wire.SNACFrame{
  350. FoodGroup: wire.BUCP,
  351. SubGroup: wire.BUCPLoginResponse,
  352. },
  353. Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
  354. TLVRestBlock: wire.TLVRestBlock{
  355. TLVList: []wire.TLV{
  356. wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("non_existent_screen_name")),
  357. wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidUsernameOrPassword),
  358. },
  359. },
  360. },
  361. },
  362. },
  363. {
  364. name: "AIM account is suspended",
  365. advertisedHost: "127.0.0.1:5190",
  366. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  367. TLVRestBlock: wire.TLVRestBlock{
  368. TLVList: wire.TLVList{
  369. wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, []byte("password")),
  370. wire.NewTLVBE(wire.LoginTLVTagsScreenName, []byte("suspended_screen_name")),
  371. },
  372. },
  373. },
  374. mockParams: mockParams{
  375. userManagerParams: userManagerParams{
  376. getUserParams: getUserParams{
  377. {
  378. screenName: state.NewIdentScreenName("suspended_screen_name"),
  379. result: &state.User{
  380. SuspendedStatus: wire.LoginErrSuspendedAccount,
  381. },
  382. },
  383. },
  384. },
  385. },
  386. expectOutput: wire.SNACMessage{
  387. Frame: wire.SNACFrame{
  388. FoodGroup: wire.BUCP,
  389. SubGroup: wire.BUCPLoginResponse,
  390. },
  391. Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
  392. TLVRestBlock: wire.TLVRestBlock{
  393. TLVList: []wire.TLV{
  394. wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("suspended_screen_name")),
  395. wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrSuspendedAccount),
  396. },
  397. },
  398. },
  399. },
  400. },
  401. {
  402. name: "ICQ account doesn't exist, login fails",
  403. advertisedHost: "127.0.0.1:5190",
  404. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  405. TLVRestBlock: wire.TLVRestBlock{
  406. TLVList: wire.TLVList{
  407. wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, []byte("password")),
  408. wire.NewTLVBE(wire.LoginTLVTagsScreenName, []byte("100003")),
  409. },
  410. },
  411. },
  412. mockParams: mockParams{
  413. userManagerParams: userManagerParams{
  414. getUserParams: getUserParams{
  415. {
  416. screenName: state.NewIdentScreenName("100003"),
  417. result: nil,
  418. },
  419. },
  420. },
  421. },
  422. expectOutput: wire.SNACMessage{
  423. Frame: wire.SNACFrame{
  424. FoodGroup: wire.BUCP,
  425. SubGroup: wire.BUCPLoginResponse,
  426. },
  427. Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
  428. TLVRestBlock: wire.TLVRestBlock{
  429. TLVList: []wire.TLV{
  430. wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("100003")),
  431. wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrICQUserErr),
  432. },
  433. },
  434. },
  435. },
  436. },
  437. {
  438. name: "account doesn't exist, authentication is disabled, account is created, login succeeds",
  439. advertisedHost: "127.0.0.1:5190",
  440. cfg: config.Config{
  441. DisableAuth: true,
  442. },
  443. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  444. TLVRestBlock: wire.TLVRestBlock{
  445. TLVList: wire.TLVList{
  446. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  447. wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
  448. },
  449. },
  450. },
  451. mockParams: mockParams{
  452. userManagerParams: userManagerParams{
  453. getUserParams: getUserParams{
  454. {
  455. screenName: user.IdentScreenName,
  456. result: nil,
  457. },
  458. },
  459. },
  460. cookieBakerParams: cookieBakerParams{
  461. cookieIssueParams: cookieIssueParams{
  462. {
  463. dataIn: func() []byte {
  464. loginCookie := state.ServerCookie{
  465. ScreenName: user.DisplayScreenName,
  466. }
  467. buf := &bytes.Buffer{}
  468. assert.NoError(t, wire.MarshalBE(loginCookie, buf))
  469. return buf.Bytes()
  470. }(),
  471. cookieOut: []byte("the-cookie"),
  472. },
  473. },
  474. },
  475. },
  476. createAccount: func(ctx context.Context, screenName state.DisplayScreenName, password string) error {
  477. assert.Equal(t, user.DisplayScreenName, screenName)
  478. assert.Equal(t, "welcome1", password)
  479. return nil
  480. },
  481. expectOutput: wire.SNACMessage{
  482. Frame: wire.SNACFrame{
  483. FoodGroup: wire.BUCP,
  484. SubGroup: wire.BUCPLoginResponse,
  485. },
  486. Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
  487. TLVRestBlock: wire.TLVRestBlock{
  488. TLVList: wire.TLVList{
  489. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  490. wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
  491. wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
  492. wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
  493. },
  494. },
  495. },
  496. },
  497. },
  498. {
  499. name: "AIM account doesn't exist, authentication is disabled, screen name has bad format, login fails",
  500. advertisedHost: "127.0.0.1:5190",
  501. cfg: config.Config{
  502. DisableAuth: true,
  503. },
  504. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  505. TLVRestBlock: wire.TLVRestBlock{
  506. TLVList: wire.TLVList{
  507. wire.NewTLVBE(wire.LoginTLVTagsScreenName, "2coolforschool"),
  508. wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
  509. },
  510. },
  511. },
  512. mockParams: mockParams{
  513. userManagerParams: userManagerParams{
  514. getUserParams: getUserParams{
  515. {
  516. screenName: state.NewIdentScreenName("2coolforschool"),
  517. result: nil,
  518. },
  519. },
  520. },
  521. },
  522. createAccount: func(ctx context.Context, screenName state.DisplayScreenName, password string) error {
  523. assert.Equal(t, state.DisplayScreenName("2coolforschool"), screenName)
  524. assert.Equal(t, "welcome1", password)
  525. return state.ErrAIMHandleInvalidFormat
  526. },
  527. expectOutput: wire.SNACMessage{
  528. Frame: wire.SNACFrame{
  529. FoodGroup: wire.BUCP,
  530. SubGroup: wire.BUCPLoginResponse,
  531. },
  532. Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
  533. TLVRestBlock: wire.TLVRestBlock{
  534. TLVList: wire.TLVList{
  535. wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("2coolforschool")),
  536. wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidUsernameOrPassword),
  537. },
  538. },
  539. },
  540. },
  541. },
  542. {
  543. name: "ICQ account doesn't exist, authentication is disabled, UIN has bad format, login fails",
  544. advertisedHost: "127.0.0.1:5190",
  545. cfg: config.Config{
  546. DisableAuth: true,
  547. },
  548. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  549. TLVRestBlock: wire.TLVRestBlock{
  550. TLVList: wire.TLVList{
  551. wire.NewTLVBE(wire.LoginTLVTagsScreenName, "99"),
  552. wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
  553. },
  554. },
  555. },
  556. mockParams: mockParams{
  557. userManagerParams: userManagerParams{
  558. getUserParams: getUserParams{
  559. {
  560. screenName: state.NewIdentScreenName("99"),
  561. result: nil,
  562. },
  563. },
  564. },
  565. },
  566. createAccount: func(ctx context.Context, screenName state.DisplayScreenName, password string) error {
  567. assert.Equal(t, state.DisplayScreenName("99"), screenName)
  568. assert.Equal(t, "welcome1", password)
  569. return state.ErrICQUINInvalidFormat
  570. },
  571. expectOutput: wire.SNACMessage{
  572. Frame: wire.SNACFrame{
  573. FoodGroup: wire.BUCP,
  574. SubGroup: wire.BUCPLoginResponse,
  575. },
  576. Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
  577. TLVRestBlock: wire.TLVRestBlock{
  578. TLVList: wire.TLVList{
  579. wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("99")),
  580. wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrICQUserErr),
  581. },
  582. },
  583. },
  584. },
  585. },
  586. {
  587. name: "account exists, password is invalid, authentication is disabled, login succeeds",
  588. advertisedHost: "127.0.0.1:5190",
  589. cfg: config.Config{
  590. DisableAuth: true,
  591. },
  592. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  593. TLVRestBlock: wire.TLVRestBlock{
  594. TLVList: wire.TLVList{
  595. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  596. wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, []byte("bad-password-hash")),
  597. },
  598. },
  599. },
  600. mockParams: mockParams{
  601. userManagerParams: userManagerParams{
  602. getUserParams: getUserParams{
  603. {
  604. screenName: user.IdentScreenName,
  605. result: &user,
  606. },
  607. },
  608. },
  609. cookieBakerParams: cookieBakerParams{
  610. cookieIssueParams: cookieIssueParams{
  611. {
  612. dataIn: func() []byte {
  613. loginCookie := state.ServerCookie{
  614. ScreenName: user.DisplayScreenName,
  615. }
  616. buf := &bytes.Buffer{}
  617. assert.NoError(t, wire.MarshalBE(loginCookie, buf))
  618. return buf.Bytes()
  619. }(),
  620. cookieOut: []byte("the-cookie"),
  621. },
  622. },
  623. },
  624. },
  625. expectOutput: wire.SNACMessage{
  626. Frame: wire.SNACFrame{
  627. FoodGroup: wire.BUCP,
  628. SubGroup: wire.BUCPLoginResponse,
  629. },
  630. Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
  631. TLVRestBlock: wire.TLVRestBlock{
  632. TLVList: wire.TLVList{
  633. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  634. wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
  635. wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
  636. wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
  637. },
  638. },
  639. },
  640. },
  641. },
  642. {
  643. name: "login fails on user manager lookup",
  644. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  645. TLVRestBlock: wire.TLVRestBlock{
  646. TLVList: wire.TLVList{
  647. wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
  648. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  649. },
  650. },
  651. },
  652. mockParams: mockParams{
  653. userManagerParams: userManagerParams{
  654. getUserParams: getUserParams{
  655. {
  656. screenName: user.IdentScreenName,
  657. err: io.EOF,
  658. },
  659. },
  660. },
  661. },
  662. wantErr: io.EOF,
  663. },
  664. {
  665. name: "login with TOC client - success",
  666. advertisedHost: "127.0.0.1:5190",
  667. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  668. TLVRestBlock: wire.TLVRestBlock{
  669. TLVList: wire.TLVList{
  670. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  671. wire.NewTLVBE(wire.LoginTLVTagsRoastedTOCPassword, wire.RoastTOCPassword([]byte("the_password"))),
  672. },
  673. },
  674. },
  675. mockParams: mockParams{
  676. userManagerParams: userManagerParams{
  677. getUserParams: getUserParams{
  678. {
  679. screenName: user.IdentScreenName,
  680. result: &user,
  681. },
  682. },
  683. },
  684. cookieBakerParams: cookieBakerParams{
  685. cookieIssueParams: cookieIssueParams{
  686. {
  687. dataIn: func() []byte {
  688. loginCookie := state.ServerCookie{
  689. ScreenName: user.DisplayScreenName,
  690. }
  691. buf := &bytes.Buffer{}
  692. assert.NoError(t, wire.MarshalBE(loginCookie, buf))
  693. return buf.Bytes()
  694. }(),
  695. cookieOut: []byte("the-cookie"),
  696. },
  697. },
  698. },
  699. },
  700. expectOutput: wire.SNACMessage{
  701. Frame: wire.SNACFrame{
  702. FoodGroup: wire.BUCP,
  703. SubGroup: wire.BUCPLoginResponse,
  704. },
  705. Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
  706. TLVRestBlock: wire.TLVRestBlock{
  707. TLVList: wire.TLVList{
  708. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  709. wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
  710. wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
  711. wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
  712. },
  713. },
  714. },
  715. },
  716. },
  717. {
  718. name: "AIM account exists, correct password, linked accounts in response",
  719. advertisedHost: "127.0.0.1:5190",
  720. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  721. TLVRestBlock: wire.TLVRestBlock{
  722. TLVList: wire.TLVList{
  723. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  724. wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
  725. },
  726. },
  727. },
  728. mockParams: mockParams{
  729. userManagerParams: userManagerParams{
  730. getUserParams: getUserParams{
  731. {
  732. screenName: user.IdentScreenName,
  733. result: &user,
  734. },
  735. },
  736. },
  737. cookieBakerParams: cookieBakerParams{
  738. cookieIssueParams: cookieIssueParams{
  739. {
  740. dataIn: func() []byte {
  741. loginCookie := state.ServerCookie{
  742. ScreenName: user.DisplayScreenName,
  743. }
  744. buf := &bytes.Buffer{}
  745. assert.NoError(t, wire.MarshalBE(loginCookie, buf))
  746. return buf.Bytes()
  747. }(),
  748. cookieOut: []byte("the-cookie"),
  749. },
  750. },
  751. },
  752. feedbagManagerParams: feedbagManagerParams{
  753. feedbagParams: feedbagParams{
  754. {
  755. screenName: user.IdentScreenName,
  756. results: []wire.FeedbagItem{{ClassID: wire.FeedbagClassIdAlInfo, Name: "linked1"}},
  757. },
  758. },
  759. },
  760. },
  761. expectOutput: wire.SNACMessage{
  762. Frame: wire.SNACFrame{
  763. FoodGroup: wire.BUCP,
  764. SubGroup: wire.BUCPLoginResponse,
  765. },
  766. Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
  767. TLVRestBlock: wire.TLVRestBlock{
  768. TLVList: wire.TLVList{
  769. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  770. wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
  771. wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
  772. wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
  773. wire.NewTLVBE(wire.OServiceTLVTagsLinkedAccounts, `<SET SETID="1"><RESREC TYPE="PRIMARY-ACCOUNT" ID="1"><n>screenname</n></RESREC><RESREC TYPE="LINKED-ACCOUNT" ID="2"><n>linked1</n></RESREC></SET>`),
  774. },
  775. },
  776. },
  777. },
  778. },
  779. {
  780. name: "feedbag error during login, returns error",
  781. advertisedHost: "127.0.0.1:5190",
  782. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  783. TLVRestBlock: wire.TLVRestBlock{
  784. TLVList: wire.TLVList{
  785. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  786. wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
  787. },
  788. },
  789. },
  790. mockParams: mockParams{
  791. userManagerParams: userManagerParams{
  792. getUserParams: getUserParams{
  793. {
  794. screenName: user.IdentScreenName,
  795. result: &user,
  796. },
  797. },
  798. },
  799. cookieBakerParams: cookieBakerParams{
  800. cookieIssueParams: cookieIssueParams{
  801. {
  802. dataIn: func() []byte {
  803. loginCookie := state.ServerCookie{
  804. ScreenName: user.DisplayScreenName,
  805. }
  806. buf := &bytes.Buffer{}
  807. assert.NoError(t, wire.MarshalBE(loginCookie, buf))
  808. return buf.Bytes()
  809. }(),
  810. cookieOut: []byte("the-cookie"),
  811. },
  812. },
  813. },
  814. feedbagManagerParams: feedbagManagerParams{
  815. feedbagParams: feedbagParams{
  816. {
  817. screenName: user.IdentScreenName,
  818. err: io.EOF,
  819. },
  820. },
  821. },
  822. },
  823. wantErr: io.EOF,
  824. },
  825. {
  826. name: "login with TOC client - failed",
  827. advertisedHost: "127.0.0.1:5190",
  828. inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
  829. TLVRestBlock: wire.TLVRestBlock{
  830. TLVList: wire.TLVList{
  831. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  832. wire.NewTLVBE(wire.LoginTLVTagsRoastedTOCPassword, wire.RoastTOCPassword([]byte("the_wrong_password"))),
  833. },
  834. },
  835. },
  836. mockParams: mockParams{
  837. userManagerParams: userManagerParams{
  838. getUserParams: getUserParams{
  839. {
  840. screenName: user.IdentScreenName,
  841. result: &user,
  842. },
  843. },
  844. },
  845. },
  846. expectOutput: wire.SNACMessage{
  847. Frame: wire.SNACFrame{
  848. FoodGroup: wire.BUCP,
  849. SubGroup: wire.BUCPLoginResponse,
  850. },
  851. Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
  852. TLVRestBlock: wire.TLVRestBlock{
  853. TLVList: wire.TLVList{
  854. wire.NewTLVBE(wire.LoginTLVTagsScreenName, "screenName"),
  855. wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidPassword),
  856. },
  857. },
  858. },
  859. },
  860. },
  861. }
  862. for _, tc := range cases {
  863. t.Run(tc.name, func(t *testing.T) {
  864. userManager := newMockUserManager(t)
  865. for _, params := range tc.mockParams.userManagerParams.getUserParams {
  866. userManager.EXPECT().
  867. User(matchContext(), params.screenName).
  868. Return(params.result, params.err)
  869. }
  870. cookieBaker := newMockCookieBaker(t)
  871. for _, params := range tc.mockParams.cookieIssueParams {
  872. cookieBaker.EXPECT().
  873. Issue(params.dataIn).
  874. Return(params.cookieOut, params.err)
  875. }
  876. sessionRetriever := newMockSessionRetriever(t)
  877. for _, params := range tc.mockParams.retrieveSessionParams {
  878. sessionRetriever.EXPECT().
  879. RetrieveSession(params.screenName).
  880. Return(params.result)
  881. }
  882. feedbagManager := newMockFeedbagManager(t)
  883. for _, params := range tc.mockParams.feedbagParams {
  884. feedbagManager.EXPECT().
  885. Feedbag(matchContext(), params.screenName).
  886. Return(params.results, params.err)
  887. }
  888. feedbagManager.EXPECT().Feedbag(matchContext(), mock.Anything).Return(nil, nil).Maybe()
  889. svc := AuthService{
  890. config: tc.cfg,
  891. cookieBaker: cookieBaker,
  892. userManager: userManager,
  893. sessionRetriever: sessionRetriever,
  894. feedbagManager: feedbagManager,
  895. maxConcurrentLoginsPerUser: 2,
  896. createAccount: tc.createAccount,
  897. logger: slog.Default(),
  898. }
  899. outputSNAC, err := svc.BUCPLogin(context.Background(), tc.inputSNAC, tc.advertisedHost)
  900. assert.ErrorIs(t, err, tc.wantErr)
  901. assert.Equal(t, tc.expectOutput, outputSNAC)
  902. })
  903. }
  904. }
  905. func TestAuthService_FLAPLogin(t *testing.T) {
  906. user := state.User{
  907. AuthKey: "auth_key",
  908. DisplayScreenName: "screenName",
  909. IdentScreenName: state.NewIdentScreenName("screenName"),
  910. }
  911. assert.NoError(t, user.HashPassword("the_password"))
  912. cases := []struct {
  913. // name is the unit test name
  914. name string
  915. // advertisedHost is the BOS host the client will connect to upon successful login
  916. advertisedHost string
  917. // cfg is the app configuration
  918. cfg config.Config
  919. // inputSNAC is the authentication FLAP frame sent from the client to the server
  920. inputSNAC wire.FLAPSignonFrame
  921. // mockParams is the list of params sent to mocks that satisfy this
  922. // method's dependencies
  923. mockParams mockParams
  924. // createAccount is the function that creates a new user account
  925. createAccount state.CreateAccountFunc
  926. // expectOutput is the response sent from the server to client
  927. expectOutput wire.TLVRestBlock
  928. // wantErr is the error we expect from the method
  929. wantErr error
  930. }{
  931. {
  932. name: "AIM account exists, correct password, login OK",
  933. advertisedHost: "127.0.0.1:5190",
  934. inputSNAC: wire.FLAPSignonFrame{
  935. TLVRestBlock: wire.TLVRestBlock{
  936. TLVList: wire.TLVList{
  937. wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARPassword([]byte("the_password"))),
  938. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  939. },
  940. },
  941. },
  942. mockParams: mockParams{
  943. userManagerParams: userManagerParams{
  944. getUserParams: getUserParams{
  945. {
  946. screenName: user.IdentScreenName,
  947. result: &user,
  948. },
  949. },
  950. },
  951. cookieBakerParams: cookieBakerParams{
  952. cookieIssueParams: cookieIssueParams{
  953. {
  954. dataIn: func() []byte {
  955. loginCookie := state.ServerCookie{
  956. ScreenName: user.DisplayScreenName,
  957. }
  958. buf := &bytes.Buffer{}
  959. assert.NoError(t, wire.MarshalBE(loginCookie, buf))
  960. return buf.Bytes()
  961. }(),
  962. cookieOut: []byte("the-cookie"),
  963. },
  964. },
  965. },
  966. },
  967. expectOutput: wire.TLVRestBlock{
  968. TLVList: wire.TLVList{
  969. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  970. wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
  971. wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
  972. wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
  973. },
  974. },
  975. },
  976. {
  977. name: "ICQ account exists, correct password, login OK",
  978. advertisedHost: "127.0.0.1:5190",
  979. inputSNAC: wire.FLAPSignonFrame{
  980. TLVRestBlock: wire.TLVRestBlock{
  981. TLVList: wire.TLVList{
  982. wire.NewTLVBE(wire.LoginTLVTagsClientIdentity, "ICQ 2000b"),
  983. wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARPassword([]byte("the_password"))),
  984. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  985. },
  986. },
  987. },
  988. mockParams: mockParams{
  989. userManagerParams: userManagerParams{
  990. getUserParams: getUserParams{
  991. {
  992. screenName: user.IdentScreenName,
  993. result: &user,
  994. },
  995. },
  996. },
  997. cookieBakerParams: cookieBakerParams{
  998. cookieIssueParams: cookieIssueParams{
  999. {
  1000. dataIn: func() []byte {
  1001. loginCookie := state.ServerCookie{
  1002. ScreenName: user.DisplayScreenName,
  1003. ClientID: "ICQ 2000b",
  1004. }
  1005. buf := &bytes.Buffer{}
  1006. assert.NoError(t, wire.MarshalBE(loginCookie, buf))
  1007. return buf.Bytes()
  1008. }(),
  1009. cookieOut: []byte("the-cookie"),
  1010. },
  1011. },
  1012. },
  1013. },
  1014. expectOutput: wire.TLVRestBlock{
  1015. TLVList: wire.TLVList{
  1016. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  1017. wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
  1018. wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
  1019. wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
  1020. },
  1021. },
  1022. },
  1023. {
  1024. name: "AIM account exists, incorrect password, login fails",
  1025. advertisedHost: "127.0.0.1:5190",
  1026. inputSNAC: wire.FLAPSignonFrame{
  1027. TLVRestBlock: wire.TLVRestBlock{
  1028. TLVList: wire.TLVList{
  1029. wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, []byte("bad_roasted_password")),
  1030. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  1031. },
  1032. },
  1033. },
  1034. mockParams: mockParams{
  1035. userManagerParams: userManagerParams{
  1036. getUserParams: getUserParams{
  1037. {
  1038. screenName: user.IdentScreenName,
  1039. result: &user,
  1040. },
  1041. },
  1042. },
  1043. },
  1044. expectOutput: wire.TLVRestBlock{
  1045. TLVList: []wire.TLV{
  1046. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  1047. wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidPassword),
  1048. },
  1049. },
  1050. },
  1051. {
  1052. name: "AIM account doesn't exist, login fails",
  1053. advertisedHost: "127.0.0.1:5190",
  1054. inputSNAC: wire.FLAPSignonFrame{
  1055. TLVRestBlock: wire.TLVRestBlock{
  1056. TLVList: wire.TLVList{
  1057. wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARPassword([]byte("the_password"))),
  1058. wire.NewTLVBE(wire.LoginTLVTagsScreenName, []byte("non_existent_screen_name")),
  1059. },
  1060. },
  1061. },
  1062. mockParams: mockParams{
  1063. userManagerParams: userManagerParams{
  1064. getUserParams: getUserParams{
  1065. {
  1066. screenName: state.NewIdentScreenName("non_existent_screen_name"),
  1067. result: nil,
  1068. },
  1069. },
  1070. },
  1071. },
  1072. expectOutput: wire.TLVRestBlock{
  1073. TLVList: []wire.TLV{
  1074. wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("non_existent_screen_name")),
  1075. wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidUsernameOrPassword),
  1076. },
  1077. },
  1078. },
  1079. {
  1080. name: "ICQ account doesn't exist, login fails",
  1081. advertisedHost: "127.0.0.1:5190",
  1082. inputSNAC: wire.FLAPSignonFrame{
  1083. TLVRestBlock: wire.TLVRestBlock{
  1084. TLVList: wire.TLVList{
  1085. wire.NewTLVBE(wire.LoginTLVTagsClientIdentity, "ICQ 2000b"),
  1086. wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARPassword([]byte("the_password"))),
  1087. wire.NewTLVBE(wire.LoginTLVTagsScreenName, []byte("100003")),
  1088. },
  1089. },
  1090. },
  1091. mockParams: mockParams{
  1092. userManagerParams: userManagerParams{
  1093. getUserParams: getUserParams{
  1094. {
  1095. screenName: state.NewIdentScreenName("100003"),
  1096. result: nil,
  1097. },
  1098. },
  1099. },
  1100. },
  1101. expectOutput: wire.TLVRestBlock{
  1102. TLVList: []wire.TLV{
  1103. wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("100003")),
  1104. wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrICQUserErr),
  1105. },
  1106. },
  1107. },
  1108. {
  1109. name: "account doesn't exist, authentication is disabled, account is created, login succeeds",
  1110. advertisedHost: "127.0.0.1:5190",
  1111. cfg: config.Config{
  1112. DisableAuth: true,
  1113. },
  1114. inputSNAC: wire.FLAPSignonFrame{
  1115. TLVRestBlock: wire.TLVRestBlock{
  1116. TLVList: wire.TLVList{
  1117. wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARPassword([]byte("the_password"))),
  1118. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  1119. },
  1120. },
  1121. },
  1122. mockParams: mockParams{
  1123. userManagerParams: userManagerParams{
  1124. getUserParams: getUserParams{
  1125. {
  1126. screenName: user.IdentScreenName,
  1127. result: nil,
  1128. },
  1129. },
  1130. },
  1131. cookieBakerParams: cookieBakerParams{
  1132. cookieIssueParams: cookieIssueParams{
  1133. {
  1134. dataIn: func() []byte {
  1135. loginCookie := state.ServerCookie{
  1136. ScreenName: user.DisplayScreenName,
  1137. }
  1138. buf := &bytes.Buffer{}
  1139. assert.NoError(t, wire.MarshalBE(loginCookie, buf))
  1140. return buf.Bytes()
  1141. }(),
  1142. cookieOut: []byte("the-cookie"),
  1143. },
  1144. },
  1145. },
  1146. },
  1147. createAccount: func(ctx context.Context, screenName state.DisplayScreenName, password string) error {
  1148. assert.Equal(t, user.DisplayScreenName, screenName)
  1149. assert.Equal(t, "welcome1", password)
  1150. return nil
  1151. },
  1152. expectOutput: wire.TLVRestBlock{
  1153. TLVList: wire.TLVList{
  1154. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  1155. wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
  1156. wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
  1157. wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
  1158. },
  1159. },
  1160. },
  1161. {
  1162. name: "account exists, password is invalid, authentication is disabled, login succeeds",
  1163. advertisedHost: "127.0.0.1:5190",
  1164. cfg: config.Config{
  1165. DisableAuth: true,
  1166. },
  1167. inputSNAC: wire.FLAPSignonFrame{
  1168. TLVRestBlock: wire.TLVRestBlock{
  1169. TLVList: wire.TLVList{
  1170. wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, "bad-roasted-password"),
  1171. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  1172. },
  1173. },
  1174. },
  1175. mockParams: mockParams{
  1176. userManagerParams: userManagerParams{
  1177. getUserParams: getUserParams{
  1178. {
  1179. screenName: user.IdentScreenName,
  1180. result: &user,
  1181. },
  1182. },
  1183. },
  1184. cookieBakerParams: cookieBakerParams{
  1185. cookieIssueParams: cookieIssueParams{
  1186. {
  1187. dataIn: func() []byte {
  1188. loginCookie := state.ServerCookie{
  1189. ScreenName: user.DisplayScreenName,
  1190. }
  1191. buf := &bytes.Buffer{}
  1192. assert.NoError(t, wire.MarshalBE(loginCookie, buf))
  1193. return buf.Bytes()
  1194. }(),
  1195. cookieOut: []byte("the-cookie"),
  1196. },
  1197. },
  1198. },
  1199. },
  1200. expectOutput: wire.TLVRestBlock{
  1201. TLVList: wire.TLVList{
  1202. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  1203. wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
  1204. wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
  1205. wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
  1206. },
  1207. },
  1208. },
  1209. {
  1210. name: "feedbag error during login, returns error",
  1211. advertisedHost: "127.0.0.1:5190",
  1212. inputSNAC: wire.FLAPSignonFrame{
  1213. TLVRestBlock: wire.TLVRestBlock{
  1214. TLVList: wire.TLVList{
  1215. wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARPassword([]byte("the_password"))),
  1216. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  1217. },
  1218. },
  1219. },
  1220. mockParams: mockParams{
  1221. userManagerParams: userManagerParams{
  1222. getUserParams: getUserParams{
  1223. {
  1224. screenName: user.IdentScreenName,
  1225. result: &user,
  1226. },
  1227. },
  1228. },
  1229. cookieBakerParams: cookieBakerParams{
  1230. cookieIssueParams: cookieIssueParams{
  1231. {
  1232. dataIn: func() []byte {
  1233. loginCookie := state.ServerCookie{
  1234. ScreenName: user.DisplayScreenName,
  1235. }
  1236. buf := &bytes.Buffer{}
  1237. assert.NoError(t, wire.MarshalBE(loginCookie, buf))
  1238. return buf.Bytes()
  1239. }(),
  1240. cookieOut: []byte("the-cookie"),
  1241. },
  1242. },
  1243. },
  1244. feedbagManagerParams: feedbagManagerParams{
  1245. feedbagParams: feedbagParams{
  1246. {
  1247. screenName: user.IdentScreenName,
  1248. err: io.EOF,
  1249. },
  1250. },
  1251. },
  1252. },
  1253. wantErr: io.EOF,
  1254. },
  1255. {
  1256. name: "login fails on user manager lookup",
  1257. inputSNAC: wire.FLAPSignonFrame{
  1258. TLVRestBlock: wire.TLVRestBlock{
  1259. TLVList: wire.TLVList{
  1260. wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARPassword([]byte("the_password"))),
  1261. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  1262. },
  1263. },
  1264. },
  1265. mockParams: mockParams{
  1266. userManagerParams: userManagerParams{
  1267. getUserParams: getUserParams{
  1268. {
  1269. screenName: user.IdentScreenName,
  1270. err: io.EOF,
  1271. },
  1272. },
  1273. },
  1274. },
  1275. wantErr: io.EOF,
  1276. },
  1277. {
  1278. name: "login with AIM 1.1.19 for Java - success",
  1279. advertisedHost: "127.0.0.1:5190",
  1280. inputSNAC: wire.FLAPSignonFrame{
  1281. TLVRestBlock: wire.TLVRestBlock{
  1282. TLVList: wire.TLVList{
  1283. wire.NewTLVBE(wire.LoginTLVTagsClientIdentity, "AOL Instant Messenger (TM) version 1.1.19 for Java"),
  1284. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  1285. wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARJavaPassword([]byte("the_password"))),
  1286. },
  1287. },
  1288. },
  1289. mockParams: mockParams{
  1290. userManagerParams: userManagerParams{
  1291. getUserParams: getUserParams{
  1292. {
  1293. screenName: user.IdentScreenName,
  1294. result: &user,
  1295. },
  1296. },
  1297. },
  1298. cookieBakerParams: cookieBakerParams{
  1299. cookieIssueParams: cookieIssueParams{
  1300. {
  1301. dataIn: func() []byte {
  1302. loginCookie := state.ServerCookie{
  1303. ScreenName: user.DisplayScreenName,
  1304. ClientID: "AOL Instant Messenger (TM) version 1.1.19 for Java",
  1305. }
  1306. buf := &bytes.Buffer{}
  1307. assert.NoError(t, wire.MarshalBE(loginCookie, buf))
  1308. return buf.Bytes()
  1309. }(),
  1310. cookieOut: []byte("the-cookie"),
  1311. },
  1312. },
  1313. },
  1314. },
  1315. expectOutput: wire.TLVRestBlock{
  1316. TLVList: wire.TLVList{
  1317. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  1318. wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
  1319. wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
  1320. wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
  1321. },
  1322. },
  1323. },
  1324. {
  1325. name: "login with AIM 1.1.19 for Java - failed",
  1326. advertisedHost: "127.0.0.1:5190",
  1327. inputSNAC: wire.FLAPSignonFrame{
  1328. TLVRestBlock: wire.TLVRestBlock{
  1329. TLVList: wire.TLVList{
  1330. wire.NewTLVBE(wire.LoginTLVTagsClientIdentity, "AOL Instant Messenger (TM) version 1.1.19 for Java"),
  1331. wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
  1332. wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARJavaPassword([]byte("the_wrong_password"))),
  1333. },
  1334. },
  1335. },
  1336. mockParams: mockParams{
  1337. userManagerParams: userManagerParams{
  1338. getUserParams: getUserParams{
  1339. {
  1340. screenName: user.IdentScreenName,
  1341. result: &user,
  1342. },
  1343. },
  1344. },
  1345. },
  1346. expectOutput: wire.TLVRestBlock{
  1347. TLVList: wire.TLVList{
  1348. wire.NewTLVBE(wire.LoginTLVTagsScreenName, "screenName"),
  1349. wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidPassword),
  1350. },
  1351. },
  1352. },
  1353. }
  1354. for _, tc := range cases {
  1355. t.Run(tc.name, func(t *testing.T) {
  1356. userManager := newMockUserManager(t)
  1357. for _, params := range tc.mockParams.userManagerParams.getUserParams {
  1358. userManager.EXPECT().
  1359. User(matchContext(), params.screenName).
  1360. Return(params.result, params.err)
  1361. }
  1362. cookieBaker := newMockCookieBaker(t)
  1363. for _, params := range tc.mockParams.cookieIssueParams {
  1364. cookieBaker.EXPECT().
  1365. Issue(params.dataIn).
  1366. Return(params.cookieOut, params.err)
  1367. }
  1368. feedbagManager := newMockFeedbagManager(t)
  1369. for _, params := range tc.mockParams.feedbagParams {
  1370. feedbagManager.EXPECT().
  1371. Feedbag(matchContext(), params.screenName).
  1372. Return(params.results, params.err)
  1373. }
  1374. feedbagManager.EXPECT().Feedbag(matchContext(), mock.Anything).Return(nil, nil).Maybe()
  1375. svc := AuthService{
  1376. config: tc.cfg,
  1377. cookieBaker: cookieBaker,
  1378. userManager: userManager,
  1379. feedbagManager: feedbagManager,
  1380. createAccount: tc.createAccount,
  1381. logger: slog.Default(),
  1382. }
  1383. outputSNAC, err := svc.FLAPLogin(context.Background(), tc.inputSNAC, tc.advertisedHost)
  1384. assert.ErrorIs(t, err, tc.wantErr)
  1385. assert.Equal(t, tc.expectOutput, outputSNAC)
  1386. })
  1387. }
  1388. }
  1389. func TestAuthService_KerberosLogin(t *testing.T) {
  1390. user := state.User{
  1391. AuthKey: "auth_key",
  1392. DisplayScreenName: "screenName",
  1393. IdentScreenName: state.NewIdentScreenName("screenName"),
  1394. }
  1395. assert.NoError(t, user.HashPassword("the_password"))
  1396. cases := []struct {
  1397. // name is the unit test name
  1398. name string
  1399. // advertisedHost is the BOS host the client will connect to upon successful login
  1400. advertisedHost string
  1401. // cfg is the app configuration
  1402. cfg config.Config
  1403. // inputSNAC is the kerberos SNAC sent from the client to the server
  1404. inputSNAC wire.SNAC_0x050C_0x0002_KerberosLoginRequest
  1405. // mockParams is the list of params sent to mocks that satisfy this
  1406. // method's dependencies
  1407. mockParams mockParams
  1408. // createAccount is the function that creates a new user account
  1409. createAccount state.CreateAccountFunc
  1410. // expectOutput is the response sent from the server to client
  1411. expectOutput wire.SNACMessage
  1412. // wantErr is the error we expect from the method
  1413. wantErr error
  1414. // timeNow returns a canned time value
  1415. timeNow func() time.Time
  1416. }{
  1417. {
  1418. name: "AIM account exists, correct password, login OK",
  1419. advertisedHost: "127.0.0.1:5190",
  1420. timeNow: func() time.Time {
  1421. return time.Unix(1000, 0)
  1422. },
  1423. inputSNAC: wire.SNAC_0x050C_0x0002_KerberosLoginRequest{
  1424. RequestID: 54321,
  1425. ClientPrincipal: user.DisplayScreenName.String(),
  1426. TicketRequestMetadata: wire.TLVBlock{
  1427. TLVList: wire.TLVList{
  1428. wire.NewTLVBE(wire.KerberosTLVTicketRequest, wire.KerberosLoginRequestTicket{
  1429. Password: []byte("the_password"),
  1430. }),
  1431. },
  1432. },
  1433. },
  1434. mockParams: mockParams{
  1435. userManagerParams: userManagerParams{
  1436. getUserParams: getUserParams{
  1437. {
  1438. screenName: user.IdentScreenName,
  1439. result: &user,
  1440. },
  1441. },
  1442. },
  1443. cookieBakerParams: cookieBakerParams{
  1444. cookieIssueParams: cookieIssueParams{
  1445. {
  1446. dataIn: func() []byte {
  1447. loginCookie := state.ServerCookie{
  1448. Service: wire.BOS,
  1449. ScreenName: user.DisplayScreenName,
  1450. ClientID: "",
  1451. MultiConnFlag: uint8(wire.MultiConnFlagsRecentClient),
  1452. KerberosAuth: 1,
  1453. }
  1454. buf := &bytes.Buffer{}
  1455. assert.NoError(t, wire.MarshalBE(loginCookie, buf))
  1456. return buf.Bytes()
  1457. }(),
  1458. cookieOut: []byte("the-cookie"),
  1459. },
  1460. },
  1461. },
  1462. sessionRetrieverParams: sessionRetrieverParams{
  1463. retrieveSessionParams: retrieveSessionParams{
  1464. {
  1465. screenName: user.IdentScreenName,
  1466. result: nil,
  1467. },
  1468. },
  1469. },
  1470. },
  1471. expectOutput: wire.SNACMessage{
  1472. Frame: wire.SNACFrame{
  1473. FoodGroup: wire.Kerberos,
  1474. SubGroup: wire.KerberosLoginSuccessResponse,
  1475. },
  1476. Body: wire.SNAC_0x050C_0x0003_KerberosLoginSuccessResponse{
  1477. RequestID: 54321,
  1478. Epoch: 1000,
  1479. ClientPrincipal: user.DisplayScreenName.String(),
  1480. ClientRealm: "AOL",
  1481. Tickets: []wire.KerberosTicket{
  1482. {
  1483. PVNO: 0x5,
  1484. EncTicket: []uint8{},
  1485. TicketRealm: "AOL",
  1486. ServicePrincipal: "im/boss",
  1487. ClientRealm: "AOL",
  1488. ClientPrincipal: user.DisplayScreenName.String(),
  1489. AuthTime: 1000,
  1490. StartTime: 1000,
  1491. EndTime: 87400,
  1492. Unknown4: 0x60000000,
  1493. Unknown5: 0x40000000,
  1494. ConnectionMetadata: wire.TLVBlock{
  1495. TLVList: wire.TLVList{
  1496. wire.NewTLVBE(wire.KerberosTLVBOSServerInfo, wire.KerberosBOSServerInfo{
  1497. Unknown: 1,
  1498. ConnectionInfo: wire.TLVBlock{
  1499. TLVList: wire.TLVList{
  1500. wire.NewTLVBE(wire.KerberosTLVHostname, "127.0.0.1:5190"),
  1501. wire.NewTLVBE(wire.KerberosTLVCookie, []byte("the-cookie")),
  1502. wire.NewTLVBE(wire.KerberosTLVConnSettings, wire.KerberosConnUseSSL),
  1503. },
  1504. },
  1505. }),
  1506. },
  1507. },
  1508. },
  1509. },
  1510. },
  1511. },
  1512. },
  1513. {
  1514. name: "AIM account exists, incorrect password, login failed",
  1515. advertisedHost: "127.0.0.1:5190",
  1516. timeNow: func() time.Time {
  1517. return time.Unix(1000, 0)
  1518. },
  1519. inputSNAC: wire.SNAC_0x050C_0x0002_KerberosLoginRequest{
  1520. RequestID: 54321,
  1521. ClientPrincipal: user.DisplayScreenName.String(),
  1522. TicketRequestMetadata: wire.TLVBlock{
  1523. TLVList: wire.TLVList{
  1524. wire.NewTLVBE(wire.KerberosTLVTicketRequest, wire.KerberosLoginRequestTicket{
  1525. Password: []byte("the_WRONG_password"),
  1526. }),
  1527. },
  1528. },
  1529. },
  1530. mockParams: mockParams{
  1531. userManagerParams: userManagerParams{
  1532. getUserParams: getUserParams{
  1533. {
  1534. screenName: user.IdentScreenName,
  1535. result: nil,
  1536. },
  1537. },
  1538. },
  1539. },
  1540. expectOutput: wire.SNACMessage{
  1541. Frame: wire.SNACFrame{
  1542. FoodGroup: wire.Kerberos,
  1543. SubGroup: wire.KerberosKerberosLoginErrResponse,
  1544. },
  1545. Body: wire.SNAC_0x050C_0x0004_KerberosLoginErrResponse{
  1546. KerbRequestID: 54321,
  1547. ScreenName: user.DisplayScreenName.String(),
  1548. ErrCode: wire.KerberosErrAuthFailure,
  1549. Message: "Auth failure",
  1550. },
  1551. },
  1552. },
  1553. {
  1554. name: "AIM account exists, correct roasted password, login OK",
  1555. advertisedHost: "127.0.0.1:5190",
  1556. timeNow: func() time.Time {
  1557. return time.Unix(1000, 0)
  1558. },
  1559. inputSNAC: wire.SNAC_0x050C_0x0002_KerberosLoginRequest{
  1560. RequestID: 54321,
  1561. ClientPrincipal: user.DisplayScreenName.String(),
  1562. TicketRequestMetadata: wire.TLVBlock{
  1563. TLVList: wire.TLVList{
  1564. wire.NewTLVBE(wire.KerberosTLVTicketRequest, wire.KerberosLoginRequestTicket{
  1565. Version: 4,
  1566. Password: wire.RoastKerberosPassword([]byte("the_password")),
  1567. }),
  1568. },
  1569. },
  1570. },
  1571. mockParams: mockParams{
  1572. userManagerParams: userManagerParams{
  1573. getUserParams: getUserParams{
  1574. {
  1575. screenName: user.IdentScreenName,
  1576. result: &user,
  1577. },
  1578. },
  1579. },
  1580. cookieBakerParams: cookieBakerParams{
  1581. cookieIssueParams: cookieIssueParams{
  1582. {
  1583. dataIn: func() []byte {
  1584. loginCookie := state.ServerCookie{
  1585. Service: wire.BOS,
  1586. ScreenName: user.DisplayScreenName,
  1587. ClientID: "",
  1588. MultiConnFlag: uint8(wire.MultiConnFlagsRecentClient),
  1589. KerberosAuth: 1,
  1590. }
  1591. buf := &bytes.Buffer{}
  1592. assert.NoError(t, wire.MarshalBE(loginCookie, buf))
  1593. return buf.Bytes()
  1594. }(),
  1595. cookieOut: []byte("the-cookie"),
  1596. },
  1597. },
  1598. },
  1599. sessionRetrieverParams: sessionRetrieverParams{
  1600. retrieveSessionParams: retrieveSessionParams{
  1601. {
  1602. screenName: user.IdentScreenName,
  1603. result: nil,
  1604. },
  1605. },
  1606. },
  1607. },
  1608. expectOutput: wire.SNACMessage{
  1609. Frame: wire.SNACFrame{
  1610. FoodGroup: wire.Kerberos,
  1611. SubGroup: wire.KerberosLoginSuccessResponse,
  1612. },
  1613. Body: wire.SNAC_0x050C_0x0003_KerberosLoginSuccessResponse{
  1614. RequestID: 54321,
  1615. Epoch: 1000,
  1616. ClientPrincipal: user.DisplayScreenName.String(),
  1617. ClientRealm: "AOL",
  1618. Tickets: []wire.KerberosTicket{
  1619. {
  1620. PVNO: 0x5,
  1621. EncTicket: []uint8{},
  1622. TicketRealm: "AOL",
  1623. ServicePrincipal: "im/boss",
  1624. ClientRealm: "AOL",
  1625. ClientPrincipal: user.DisplayScreenName.String(),
  1626. AuthTime: 1000,
  1627. StartTime: 1000,
  1628. EndTime: 87400,
  1629. Unknown4: 0x60000000,
  1630. Unknown5: 0x40000000,
  1631. ConnectionMetadata: wire.TLVBlock{
  1632. TLVList: wire.TLVList{
  1633. wire.NewTLVBE(wire.KerberosTLVBOSServerInfo, wire.KerberosBOSServerInfo{
  1634. Unknown: 1,
  1635. ConnectionInfo: wire.TLVBlock{
  1636. TLVList: wire.TLVList{
  1637. wire.NewTLVBE(wire.KerberosTLVHostname, "127.0.0.1:5190"),
  1638. wire.NewTLVBE(wire.KerberosTLVCookie, []byte("the-cookie")),
  1639. wire.NewTLVBE(wire.KerberosTLVConnSettings, wire.KerberosConnUseSSL),
  1640. },
  1641. },
  1642. }),
  1643. },
  1644. },
  1645. },
  1646. },
  1647. },
  1648. },
  1649. },
  1650. {
  1651. name: "AIM account exists, incorrect roasted password, login failed",
  1652. advertisedHost: "127.0.0.1:5190",
  1653. timeNow: func() time.Time {
  1654. return time.Unix(1000, 0)
  1655. },
  1656. inputSNAC: wire.SNAC_0x050C_0x0002_KerberosLoginRequest{
  1657. RequestID: 54321,
  1658. ClientPrincipal: user.DisplayScreenName.String(),
  1659. TicketRequestMetadata: wire.TLVBlock{
  1660. TLVList: wire.TLVList{
  1661. wire.NewTLVBE(wire.KerberosTLVTicketRequest, wire.KerberosLoginRequestTicket{
  1662. Version: 4,
  1663. Password: wire.RoastKerberosPassword([]byte("the_WRONG_password")),
  1664. }),
  1665. },
  1666. },
  1667. },
  1668. mockParams: mockParams{
  1669. userManagerParams: userManagerParams{
  1670. getUserParams: getUserParams{
  1671. {
  1672. screenName: user.IdentScreenName,
  1673. result: &user,
  1674. },
  1675. },
  1676. },
  1677. },
  1678. expectOutput: wire.SNACMessage{
  1679. Frame: wire.SNACFrame{
  1680. FoodGroup: wire.Kerberos,
  1681. SubGroup: wire.KerberosKerberosLoginErrResponse,
  1682. },
  1683. Body: wire.SNAC_0x050C_0x0004_KerberosLoginErrResponse{
  1684. KerbRequestID: 54321,
  1685. ScreenName: user.DisplayScreenName.String(),
  1686. ErrCode: wire.KerberosErrAuthFailure,
  1687. Message: "Auth failure",
  1688. },
  1689. },
  1690. },
  1691. }
  1692. for _, tc := range cases {
  1693. t.Run(tc.name, func(t *testing.T) {
  1694. userManager := newMockUserManager(t)
  1695. for _, params := range tc.mockParams.userManagerParams.getUserParams {
  1696. userManager.EXPECT().
  1697. User(matchContext(), params.screenName).
  1698. Return(params.result, params.err)
  1699. }
  1700. cookieBaker := newMockCookieBaker(t)
  1701. for _, params := range tc.mockParams.cookieIssueParams {
  1702. cookieBaker.EXPECT().
  1703. Issue(params.dataIn).
  1704. Return(params.cookieOut, params.err)
  1705. }
  1706. sessionRetriever := newMockSessionRetriever(t)
  1707. for _, params := range tc.mockParams.retrieveSessionParams {
  1708. sessionRetriever.EXPECT().
  1709. RetrieveSession(params.screenName).
  1710. Return(params.result)
  1711. }
  1712. feedbagManager := newMockFeedbagManager(t)
  1713. feedbagManager.EXPECT().Feedbag(matchContext(), mock.Anything).Return(nil, nil).Maybe()
  1714. svc := AuthService{
  1715. config: tc.cfg,
  1716. cookieBaker: cookieBaker,
  1717. userManager: userManager,
  1718. sessionRetriever: sessionRetriever,
  1719. feedbagManager: feedbagManager,
  1720. timeNow: tc.timeNow,
  1721. maxConcurrentLoginsPerUser: 2,
  1722. createAccount: tc.createAccount,
  1723. logger: slog.Default(),
  1724. }
  1725. outputSNAC, err := svc.KerberosLogin(context.Background(), tc.inputSNAC, tc.advertisedHost)
  1726. assert.ErrorIs(t, err, tc.wantErr)
  1727. assert.Equal(t, tc.expectOutput, outputSNAC)
  1728. })
  1729. }
  1730. }
  1731. func TestAuthService_BUCPChallengeRequest(t *testing.T) {
  1732. sessUUID := uuid.UUID{1, 2, 3}
  1733. cases := []struct {
  1734. // name is the unit test name
  1735. name string
  1736. // advertisedHost is the BOS host the client will connect to upon successful login
  1737. advertisedHost string
  1738. // cfg is the app configuration
  1739. cfg config.Config
  1740. // inputSNAC is the SNAC sent from the client to the server
  1741. inputSNAC wire.SNAC_0x17_0x06_BUCPChallengeRequest
  1742. // mockParams is the list of params sent to mocks that satisfy this
  1743. // method's dependencies
  1744. mockParams mockParams
  1745. // expectOutput is the SNAC sent from the server to client
  1746. expectOutput wire.SNACMessage
  1747. // wantErr is the error we expect from the method
  1748. wantErr error
  1749. }{
  1750. {
  1751. name: "login with valid username, expect OK login response",
  1752. advertisedHost: "127.0.0.1:5190",
  1753. inputSNAC: wire.SNAC_0x17_0x06_BUCPChallengeRequest{
  1754. TLVRestBlock: wire.TLVRestBlock{
  1755. TLVList: wire.TLVList{
  1756. wire.NewTLVBE(wire.LoginTLVTagsScreenName, "sn_user_a"),
  1757. },
  1758. },
  1759. },
  1760. mockParams: mockParams{
  1761. userManagerParams: userManagerParams{
  1762. getUserParams: getUserParams{
  1763. {
  1764. screenName: state.NewIdentScreenName("sn_user_a"),
  1765. result: &state.User{
  1766. IdentScreenName: state.NewIdentScreenName("sn_user_a"),
  1767. AuthKey: "auth_key_user_a",
  1768. },
  1769. },
  1770. },
  1771. },
  1772. },
  1773. expectOutput: wire.SNACMessage{
  1774. Frame: wire.SNACFrame{
  1775. FoodGroup: wire.BUCP,
  1776. SubGroup: wire.BUCPChallengeResponse,
  1777. },
  1778. Body: wire.SNAC_0x17_0x07_BUCPChallengeResponse{
  1779. AuthKey: "auth_key_user_a",
  1780. },
  1781. },
  1782. },
  1783. {
  1784. name: "login with invalid username, expect OK login response (Cfg.DisableAuth=true)",
  1785. advertisedHost: "127.0.0.1:5190",
  1786. cfg: config.Config{
  1787. DisableAuth: true,
  1788. },
  1789. inputSNAC: wire.SNAC_0x17_0x06_BUCPChallengeRequest{
  1790. TLVRestBlock: wire.TLVRestBlock{
  1791. TLVList: wire.TLVList{
  1792. wire.NewTLVBE(wire.LoginTLVTagsScreenName, "sn_user_b"),
  1793. },
  1794. },
  1795. },
  1796. mockParams: mockParams{
  1797. userManagerParams: userManagerParams{
  1798. getUserParams: getUserParams{
  1799. {
  1800. screenName: state.NewIdentScreenName("sn_user_b"),
  1801. result: nil,
  1802. },
  1803. },
  1804. },
  1805. },
  1806. expectOutput: wire.SNACMessage{
  1807. Frame: wire.SNACFrame{
  1808. FoodGroup: wire.BUCP,
  1809. SubGroup: wire.BUCPChallengeResponse,
  1810. },
  1811. Body: wire.SNAC_0x17_0x07_BUCPChallengeResponse{
  1812. AuthKey: sessUUID.String(),
  1813. },
  1814. },
  1815. },
  1816. {
  1817. name: "login with invalid username, expect failed login response (Cfg.DisableAuth=false)",
  1818. advertisedHost: "127.0.0.1:5190",
  1819. inputSNAC: wire.SNAC_0x17_0x06_BUCPChallengeRequest{
  1820. TLVRestBlock: wire.TLVRestBlock{
  1821. TLVList: wire.TLVList{
  1822. wire.NewTLVBE(wire.LoginTLVTagsScreenName, "sn_user_b"),
  1823. },
  1824. },
  1825. },
  1826. mockParams: mockParams{
  1827. userManagerParams: userManagerParams{
  1828. getUserParams: getUserParams{
  1829. {
  1830. screenName: state.NewIdentScreenName("sn_user_b"),
  1831. result: nil,
  1832. },
  1833. },
  1834. },
  1835. },
  1836. expectOutput: wire.SNACMessage{
  1837. Frame: wire.SNACFrame{
  1838. FoodGroup: wire.BUCP,
  1839. SubGroup: wire.BUCPLoginResponse,
  1840. },
  1841. Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
  1842. TLVRestBlock: wire.TLVRestBlock{
  1843. TLVList: wire.TLVList{
  1844. wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, uint16(0x01)),
  1845. },
  1846. },
  1847. },
  1848. },
  1849. },
  1850. {
  1851. name: "login fails on user manager lookup",
  1852. inputSNAC: wire.SNAC_0x17_0x06_BUCPChallengeRequest{
  1853. TLVRestBlock: wire.TLVRestBlock{
  1854. TLVList: wire.TLVList{
  1855. wire.NewTLVBE(wire.LoginTLVTagsScreenName, "sn_user_b"),
  1856. },
  1857. },
  1858. },
  1859. mockParams: mockParams{
  1860. userManagerParams: userManagerParams{
  1861. getUserParams: getUserParams{
  1862. {
  1863. screenName: state.NewIdentScreenName("sn_user_b"),
  1864. err: io.EOF,
  1865. },
  1866. },
  1867. },
  1868. },
  1869. wantErr: io.EOF,
  1870. },
  1871. }
  1872. for _, tc := range cases {
  1873. t.Run(tc.name, func(t *testing.T) {
  1874. userManager := newMockUserManager(t)
  1875. for _, params := range tc.mockParams.userManagerParams.getUserParams {
  1876. userManager.EXPECT().
  1877. User(matchContext(), params.screenName).
  1878. Return(params.result, params.err)
  1879. }
  1880. svc := AuthService{
  1881. config: tc.cfg,
  1882. userManager: userManager,
  1883. logger: slog.Default(),
  1884. }
  1885. fnNewUUID := func() uuid.UUID {
  1886. return sessUUID
  1887. }
  1888. outputSNAC, err := svc.BUCPChallenge(context.Background(), tc.inputSNAC, fnNewUUID)
  1889. assert.ErrorIs(t, err, tc.wantErr)
  1890. assert.Equal(t, tc.expectOutput, outputSNAC)
  1891. })
  1892. }
  1893. }
  1894. func TestAuthService_RegisterChatSession_HappyPath(t *testing.T) {
  1895. instance := newTestInstance("ScreenName")
  1896. serverCookie := state.ServerCookie{
  1897. ChatCookie: "the-chat-cookie",
  1898. ScreenName: instance.DisplayScreenName(),
  1899. }
  1900. chatSessionRegistry := newMockChatSessionRegistry(t)
  1901. chatSessionRegistry.EXPECT().
  1902. AddSession(mock.Anything, serverCookie.ChatCookie, instance.DisplayScreenName(), mock.Anything).
  1903. Return(instance, nil)
  1904. chatCookieBuf := &bytes.Buffer{}
  1905. assert.NoError(t, wire.MarshalBE(serverCookie, chatCookieBuf))
  1906. svc := NewAuthService(config.Config{}, nil, nil, chatSessionRegistry, nil, nil, nil, nil, nil, nil, wire.DefaultRateLimitClasses(), nil, slog.Default())
  1907. have, err := svc.RegisterChatSession(context.Background(), serverCookie, nil)
  1908. assert.NoError(t, err)
  1909. assert.Equal(t, instance, have)
  1910. }
  1911. func TestAuthService_RegisterBOSSession(t *testing.T) {
  1912. screenName := state.DisplayScreenName("UserScreenName")
  1913. aimAuthCookie := state.ServerCookie{
  1914. ScreenName: screenName,
  1915. }
  1916. uin := state.DisplayScreenName("100003")
  1917. icqAuthCookie := state.ServerCookie{
  1918. ScreenName: uin,
  1919. }
  1920. cases := []struct {
  1921. // name is the unit test name
  1922. name string
  1923. // cookieOut is the auth cookieOut that contains session information
  1924. cookie state.ServerCookie
  1925. // cfg is the server config
  1926. cfg config.Config
  1927. // createAccount is called to auto-create a missing user when DisableAuth is set
  1928. createAccount state.CreateAccountFunc
  1929. // mockParams is the list of params sent to mocks that satisfy this
  1930. // method's dependencies
  1931. mockParams mockParams
  1932. // wantSess asserts the values of one or more session properties
  1933. wantSess func(*state.SessionInstance) bool
  1934. // wantErr is the error we expect from the method
  1935. wantErr error
  1936. }{
  1937. {
  1938. name: "successfully register an AIM session",
  1939. cookie: aimAuthCookie,
  1940. mockParams: mockParams{
  1941. sessionRegistryParams: sessionRegistryParams{
  1942. addSessionParams: addSessionParams{
  1943. {
  1944. screenName: screenName,
  1945. doMultiSess: false,
  1946. result: newTestInstance(screenName),
  1947. },
  1948. },
  1949. },
  1950. userManagerParams: userManagerParams{
  1951. getUserParams: getUserParams{
  1952. {
  1953. screenName: screenName.IdentScreenName(),
  1954. result: &state.User{
  1955. IdentScreenName: screenName.IdentScreenName(),
  1956. DisplayScreenName: screenName,
  1957. },
  1958. },
  1959. },
  1960. },
  1961. accountManagerParams: accountManagerParams{
  1962. accountManagerConfirmStatusParams: accountManagerConfirmStatusParams{
  1963. {
  1964. screenName: screenName.IdentScreenName(),
  1965. confirmStatus: true,
  1966. },
  1967. },
  1968. },
  1969. bartItemManagerParams: bartItemManagerParams{
  1970. buddyIconMetadataParams: buddyIconMetadataParams{
  1971. {
  1972. screenName: screenName.IdentScreenName(),
  1973. result: &wire.BARTID{
  1974. Type: wire.BARTTypesBuddyIcon,
  1975. BARTInfo: wire.BARTInfo{
  1976. Flags: wire.BARTFlagsKnown,
  1977. Hash: []byte{'m', 'y', 'i', 'c', 'o', 'n'},
  1978. },
  1979. },
  1980. },
  1981. },
  1982. },
  1983. },
  1984. wantSess: func(instance *state.SessionInstance) bool {
  1985. want := wire.BARTID{
  1986. Type: wire.BARTTypesBuddyIcon,
  1987. BARTInfo: wire.BARTInfo{
  1988. Flags: wire.BARTFlagsKnown,
  1989. Hash: []byte{'m', 'y', 'i', 'c', 'o', 'n'},
  1990. },
  1991. }
  1992. has, hasIcon := instance.Session().BuddyIcon()
  1993. return assert.True(t, hasIcon) && assert.Equal(t, want, has)
  1994. },
  1995. },
  1996. {
  1997. name: "successfully register an AIM bot session",
  1998. cookie: aimAuthCookie,
  1999. mockParams: mockParams{
  2000. sessionRegistryParams: sessionRegistryParams{
  2001. addSessionParams: addSessionParams{
  2002. {
  2003. screenName: screenName,
  2004. doMultiSess: false,
  2005. result: newTestInstance(screenName),
  2006. },
  2007. },
  2008. },
  2009. userManagerParams: userManagerParams{
  2010. getUserParams: getUserParams{
  2011. {
  2012. screenName: screenName.IdentScreenName(),
  2013. result: &state.User{
  2014. IdentScreenName: screenName.IdentScreenName(),
  2015. DisplayScreenName: screenName,
  2016. IsBot: true,
  2017. },
  2018. },
  2019. },
  2020. },
  2021. accountManagerParams: accountManagerParams{
  2022. accountManagerConfirmStatusParams: accountManagerConfirmStatusParams{
  2023. {
  2024. screenName: screenName.IdentScreenName(),
  2025. confirmStatus: true,
  2026. },
  2027. },
  2028. },
  2029. bartItemManagerParams: bartItemManagerParams{
  2030. buddyIconMetadataParams: buddyIconMetadataParams{
  2031. {
  2032. screenName: screenName.IdentScreenName(),
  2033. result: nil,
  2034. },
  2035. },
  2036. },
  2037. },
  2038. wantSess: func(instance *state.SessionInstance) bool {
  2039. return instance.Session().AllUserInfoBitmask(wire.OServiceUserFlagBot)
  2040. },
  2041. },
  2042. {
  2043. name: "successfully register an ICQ session",
  2044. cookie: icqAuthCookie,
  2045. mockParams: mockParams{
  2046. sessionRegistryParams: sessionRegistryParams{
  2047. addSessionParams: addSessionParams{
  2048. {
  2049. screenName: uin,
  2050. result: newTestInstance(uin),
  2051. },
  2052. },
  2053. },
  2054. userManagerParams: userManagerParams{
  2055. getUserParams: getUserParams{
  2056. {
  2057. screenName: uin.IdentScreenName(),
  2058. result: &state.User{
  2059. IdentScreenName: uin.IdentScreenName(),
  2060. DisplayScreenName: uin,
  2061. },
  2062. },
  2063. },
  2064. },
  2065. accountManagerParams: accountManagerParams{
  2066. accountManagerConfirmStatusParams: accountManagerConfirmStatusParams{
  2067. {
  2068. screenName: uin.IdentScreenName(),
  2069. confirmStatus: true,
  2070. },
  2071. },
  2072. },
  2073. bartItemManagerParams: bartItemManagerParams{
  2074. buddyIconMetadataParams: buddyIconMetadataParams{
  2075. {
  2076. screenName: uin.IdentScreenName(),
  2077. result: nil,
  2078. },
  2079. },
  2080. },
  2081. },
  2082. wantSess: func(instance *state.SessionInstance) bool {
  2083. uinMatches := fmt.Sprintf("%d", instance.UIN()) == uin.String()
  2084. flagsMatch := instance.Session().AllUserInfoBitmask(wire.OServiceUserFlagICQ)
  2085. return uinMatches && flagsMatch
  2086. },
  2087. },
  2088. {
  2089. name: "user not found, DisableAuth false, return error",
  2090. cookie: aimAuthCookie,
  2091. cfg: config.Config{DisableAuth: false},
  2092. wantErr: errors.New("user not found"),
  2093. mockParams: mockParams{
  2094. userManagerParams: userManagerParams{
  2095. getUserParams: getUserParams{
  2096. {
  2097. screenName: screenName.IdentScreenName(),
  2098. result: nil,
  2099. },
  2100. },
  2101. },
  2102. },
  2103. },
  2104. }
  2105. for _, tc := range cases {
  2106. t.Run(tc.name, func(t *testing.T) {
  2107. sessionRegistry := newMockSessionRegistry(t)
  2108. for _, params := range tc.mockParams.addSessionParams {
  2109. sessionRegistry.EXPECT().
  2110. AddSession(mock.Anything, params.screenName, params.doMultiSess, mock.Anything, mock.Anything).
  2111. Return(params.result, params.err)
  2112. }
  2113. userManager := newMockUserManager(t)
  2114. for _, params := range tc.mockParams.userManagerParams.getUserParams {
  2115. userManager.EXPECT().
  2116. User(matchContext(), params.screenName).
  2117. Return(params.result, nil).Once()
  2118. }
  2119. accountManager := newMockAccountManager(t)
  2120. for _, params := range tc.mockParams.accountManagerConfirmStatusParams {
  2121. accountManager.EXPECT().
  2122. ConfirmStatus(matchContext(), params.screenName).
  2123. Return(params.confirmStatus, nil)
  2124. }
  2125. bartItemManager := newMockBARTItemManager(t)
  2126. for _, params := range tc.mockParams.buddyIconMetadataParams {
  2127. bartItemManager.EXPECT().
  2128. BuddyIconMetadata(matchContext(), params.screenName).
  2129. Return(params.result, params.err)
  2130. }
  2131. svc := NewAuthService(tc.cfg, sessionRegistry, nil, nil, userManager, nil, nil, accountManager, bartItemManager, nil, wire.DefaultRateLimitClasses(), tc.createAccount, slog.Default())
  2132. have, err := svc.RegisterBOSSession(context.Background(), tc.cookie, nil)
  2133. if tc.wantErr != nil {
  2134. assert.ErrorContains(t, err, tc.wantErr.Error())
  2135. return
  2136. }
  2137. assert.NoError(t, err)
  2138. if tc.wantSess != nil {
  2139. assert.True(t, tc.wantSess(have))
  2140. }
  2141. })
  2142. }
  2143. }
  2144. func TestAuthService_RetrieveBOSSession_HappyPath(t *testing.T) {
  2145. instance := newTestInstance("screenName", sessOptSignonComplete)
  2146. aimAuthCookie := state.ServerCookie{
  2147. ScreenName: instance.DisplayScreenName(),
  2148. SessionNum: instance.Num(),
  2149. }
  2150. sessionRetriever := newMockSessionRetriever(t)
  2151. sessionRetriever.EXPECT().
  2152. RetrieveSession(instance.IdentScreenName()).
  2153. Return(instance.Session())
  2154. userManager := newMockUserManager(t)
  2155. userManager.EXPECT().
  2156. User(matchContext(), instance.IdentScreenName()).
  2157. Return(&state.User{IdentScreenName: instance.IdentScreenName()}, nil)
  2158. svc := NewAuthService(config.Config{}, nil, sessionRetriever, nil, userManager, nil, nil, nil, nil, nil, wire.DefaultRateLimitClasses(), nil, slog.Default())
  2159. have, err := svc.RetrieveBOSSession(context.Background(), aimAuthCookie)
  2160. assert.NoError(t, err)
  2161. assert.Equal(t, instance, have)
  2162. }
  2163. func TestAuthService_RetrieveBOSSession_SessionNotFound(t *testing.T) {
  2164. instance := newTestInstance("screenName")
  2165. aimAuthCookie := state.ServerCookie{
  2166. ScreenName: instance.DisplayScreenName(),
  2167. SessionNum: instance.Num(),
  2168. }
  2169. sessionRetriever := newMockSessionRetriever(t)
  2170. sessionRetriever.EXPECT().
  2171. RetrieveSession(instance.IdentScreenName()).
  2172. Return(nil)
  2173. userManager := newMockUserManager(t)
  2174. userManager.EXPECT().
  2175. User(matchContext(), instance.IdentScreenName()).
  2176. Return(&state.User{IdentScreenName: instance.IdentScreenName()}, nil)
  2177. svc := NewAuthService(config.Config{}, nil, sessionRetriever, nil, userManager, nil, nil, nil, nil, nil, wire.DefaultRateLimitClasses(), nil, slog.Default())
  2178. have, err := svc.RetrieveBOSSession(context.Background(), aimAuthCookie)
  2179. assert.NoError(t, err)
  2180. assert.Nil(t, have)
  2181. }
  2182. func TestAuthService_SignoutChat(t *testing.T) {
  2183. tests := []struct {
  2184. // name is the unit test name
  2185. name string
  2186. // instance is the session of the user signing out
  2187. instance *state.SessionInstance
  2188. // mockParams is the list of params sent to mocks that satisfy this
  2189. // method's dependencies
  2190. mockParams mockParams
  2191. }{
  2192. {
  2193. name: "user signs out of chat room, room is empty after user leaves",
  2194. instance: newTestInstance("me", sessOptCannedSignonTime, sessOptChatRoomCookie("the-chat-cookie")),
  2195. mockParams: mockParams{
  2196. chatMessageRelayerParams: chatMessageRelayerParams{
  2197. chatRelayToAllExceptParams: chatRelayToAllExceptParams{
  2198. {
  2199. screenName: state.NewIdentScreenName("me"),
  2200. message: wire.SNACMessage{
  2201. Frame: wire.SNACFrame{
  2202. FoodGroup: wire.Chat,
  2203. SubGroup: wire.ChatUsersLeft,
  2204. },
  2205. Body: wire.SNAC_0x0E_0x04_ChatUsersLeft{
  2206. Users: []wire.TLVUserInfo{
  2207. newTestInstance("me", sessOptCannedSignonTime, sessOptChatRoomCookie("the-chat-cookie")).Session().TLVUserInfo(),
  2208. },
  2209. },
  2210. },
  2211. },
  2212. },
  2213. },
  2214. sessionRegistryParams: sessionRegistryParams{
  2215. removeSessionParams: removeSessionParams{
  2216. {
  2217. screenName: state.NewIdentScreenName("me"),
  2218. },
  2219. },
  2220. },
  2221. },
  2222. },
  2223. {
  2224. name: "user signs out of chat room, room is not empty after user leaves",
  2225. instance: newTestInstance("me", sessOptCannedSignonTime, sessOptChatRoomCookie("the-chat-cookie")),
  2226. mockParams: mockParams{
  2227. chatMessageRelayerParams: chatMessageRelayerParams{
  2228. chatRelayToAllExceptParams: chatRelayToAllExceptParams{
  2229. {
  2230. screenName: state.NewIdentScreenName("me"),
  2231. message: wire.SNACMessage{
  2232. Frame: wire.SNACFrame{
  2233. FoodGroup: wire.Chat,
  2234. SubGroup: wire.ChatUsersLeft,
  2235. },
  2236. Body: wire.SNAC_0x0E_0x04_ChatUsersLeft{
  2237. Users: []wire.TLVUserInfo{
  2238. newTestInstance("me", sessOptCannedSignonTime, sessOptChatRoomCookie("the-chat-cookie")).Session().TLVUserInfo(),
  2239. },
  2240. },
  2241. },
  2242. },
  2243. },
  2244. },
  2245. sessionRegistryParams: sessionRegistryParams{
  2246. removeSessionParams: removeSessionParams{
  2247. {
  2248. screenName: state.NewIdentScreenName("me"),
  2249. },
  2250. },
  2251. },
  2252. },
  2253. },
  2254. }
  2255. for _, tt := range tests {
  2256. t.Run(tt.name, func(t *testing.T) {
  2257. chatMessageRelayer := newMockChatMessageRelayer(t)
  2258. for _, params := range tt.mockParams.chatRelayToAllExceptParams {
  2259. chatMessageRelayer.EXPECT().
  2260. RelayToAllExcept(matchContext(), tt.instance.ChatRoomCookie(), params.screenName, params.message)
  2261. }
  2262. sessionManager := newMockChatSessionRegistry(t)
  2263. for _, params := range tt.mockParams.removeSessionParams {
  2264. sessionManager.EXPECT().
  2265. RemoveSession(matchUserSession(params.screenName))
  2266. }
  2267. svc := NewAuthService(config.Config{}, nil, nil, sessionManager, nil, nil, chatMessageRelayer, nil, nil, nil, wire.DefaultRateLimitClasses(), nil, slog.Default())
  2268. svc.SignoutChat(context.Background(), tt.instance.Session())
  2269. })
  2270. }
  2271. }
  2272. func TestAuthService_Signout(t *testing.T) {
  2273. tests := []struct {
  2274. // name is the unit test name
  2275. name string
  2276. // instance is the session of the user signing out
  2277. instance *state.SessionInstance
  2278. // wantErr is the error we expect from the method
  2279. wantErr error
  2280. // mockParams is the list of params sent to mocks that satisfy this
  2281. // method's dependencies
  2282. mockParams mockParams
  2283. }{
  2284. {
  2285. name: "user signs out of chat room, room is empty after user leaves",
  2286. instance: newTestInstance("me", sessOptCannedSignonTime),
  2287. mockParams: mockParams{
  2288. buddyBroadcasterParams: buddyBroadcasterParams{
  2289. broadcastBuddyDepartedParams: broadcastBuddyDepartedParams{
  2290. {
  2291. screenName: state.NewIdentScreenName("me"),
  2292. },
  2293. },
  2294. },
  2295. sessionRegistryParams: sessionRegistryParams{
  2296. removeSessionParams: removeSessionParams{
  2297. {
  2298. screenName: state.NewIdentScreenName("me"),
  2299. },
  2300. },
  2301. },
  2302. },
  2303. },
  2304. }
  2305. for _, tt := range tests {
  2306. t.Run(tt.name, func(t *testing.T) {
  2307. sessionManager := newMockSessionRegistry(t)
  2308. for _, params := range tt.mockParams.removeSessionParams {
  2309. sessionManager.EXPECT().RemoveSession(matchUserSession(params.screenName))
  2310. }
  2311. svc := NewAuthService(config.Config{}, sessionManager, nil, nil, nil, nil, nil, nil, nil, nil, wire.DefaultRateLimitClasses(), nil, slog.Default())
  2312. svc.Signout(context.Background(), tt.instance.Session())
  2313. })
  2314. }
  2315. }
  2316. func TestBuildLinkedAccountsXML(t *testing.T) {
  2317. cases := []struct {
  2318. name string
  2319. screenName state.IdentScreenName
  2320. linkedNames []state.IdentScreenName
  2321. wantXML string
  2322. wantErr bool
  2323. }{
  2324. {
  2325. name: "primary account with no linked accounts",
  2326. screenName: state.NewIdentScreenName("PrimaryUser"),
  2327. linkedNames: []state.IdentScreenName{},
  2328. wantXML: `<SET SETID="1"><RESREC TYPE="PRIMARY-ACCOUNT" ID="1"><n>primaryuser</n></RESREC></SET>`,
  2329. },
  2330. {
  2331. name: "primary account with one linked account",
  2332. screenName: state.NewIdentScreenName("PrimaryUser"),
  2333. linkedNames: []state.IdentScreenName{
  2334. state.NewIdentScreenName("LinkedUser1"),
  2335. },
  2336. wantXML: `<SET SETID="1"><RESREC TYPE="PRIMARY-ACCOUNT" ID="1"><n>primaryuser</n></RESREC><RESREC TYPE="LINKED-ACCOUNT" ID="2"><n>linkeduser1</n></RESREC></SET>`,
  2337. },
  2338. {
  2339. name: "primary account with multiple linked accounts",
  2340. screenName: state.NewIdentScreenName("PrimaryUser"),
  2341. linkedNames: []state.IdentScreenName{
  2342. state.NewIdentScreenName("LinkedUser1"),
  2343. state.NewIdentScreenName("LinkedUser2"),
  2344. state.NewIdentScreenName("LinkedUser3"),
  2345. },
  2346. wantXML: `<SET SETID="1"><RESREC TYPE="PRIMARY-ACCOUNT" ID="1"><n>primaryuser</n></RESREC><RESREC TYPE="LINKED-ACCOUNT" ID="2"><n>linkeduser1</n></RESREC><RESREC TYPE="LINKED-ACCOUNT" ID="3"><n>linkeduser2</n></RESREC><RESREC TYPE="LINKED-ACCOUNT" ID="4"><n>linkeduser3</n></RESREC></SET>`,
  2347. },
  2348. }
  2349. for _, tc := range cases {
  2350. t.Run(tc.name, func(t *testing.T) {
  2351. got, err := buildLinkedAccountsXML(tc.screenName, tc.linkedNames)
  2352. if tc.wantErr {
  2353. assert.Error(t, err)
  2354. return
  2355. }
  2356. assert.NoError(t, err)
  2357. assert.Equal(t, tc.wantXML, got)
  2358. })
  2359. }
  2360. }
  2361. func TestAuthService_addLinkedAccountsTLV(t *testing.T) {
  2362. cases := []struct {
  2363. name string
  2364. screenName state.DisplayScreenName
  2365. // feedbagItems is what feedbagManager.Feedbag returns
  2366. feedbagItems []wire.FeedbagItem
  2367. feedbagErr error
  2368. // wantTLVCount is the expected number of TLVs after the call
  2369. wantTLVCount int
  2370. wantErr bool
  2371. }{
  2372. {
  2373. name: "no linked accounts, TLV list unchanged",
  2374. screenName: "PrimaryUser",
  2375. feedbagItems: nil,
  2376. wantTLVCount: 0,
  2377. },
  2378. {
  2379. name: "one linked account, TLV appended",
  2380. screenName: "PrimaryUser",
  2381. feedbagItems: []wire.FeedbagItem{
  2382. {ClassID: wire.FeedbagClassIdAlInfo, Name: "linkeduser1"},
  2383. },
  2384. wantTLVCount: 1,
  2385. },
  2386. {
  2387. name: "multiple linked accounts, single TLV appended",
  2388. screenName: "PrimaryUser",
  2389. feedbagItems: []wire.FeedbagItem{
  2390. {ClassID: wire.FeedbagClassIdAlInfo, Name: "linkeduser1"},
  2391. {ClassID: wire.FeedbagClassIdAlInfo, Name: "linkeduser2"},
  2392. },
  2393. wantTLVCount: 1,
  2394. },
  2395. {
  2396. name: "feedbagManager returns error, error propagated",
  2397. screenName: "PrimaryUser",
  2398. feedbagErr: io.EOF,
  2399. wantErr: true,
  2400. },
  2401. }
  2402. for _, tc := range cases {
  2403. t.Run(tc.name, func(t *testing.T) {
  2404. feedbagManager := newMockFeedbagManager(t)
  2405. feedbagManager.EXPECT().
  2406. Feedbag(matchContext(), state.NewIdentScreenName(string(tc.screenName))).
  2407. Return(tc.feedbagItems, tc.feedbagErr)
  2408. svc := AuthService{feedbagManager: feedbagManager}
  2409. tlvs := wire.TLVList{}
  2410. err := svc.addLinkedAccountsTLV(context.Background(), tc.screenName, &tlvs)
  2411. if tc.wantErr {
  2412. assert.Error(t, err)
  2413. return
  2414. }
  2415. assert.NoError(t, err)
  2416. assert.Len(t, tlvs, tc.wantTLVCount)
  2417. if tc.wantTLVCount > 0 {
  2418. assert.Equal(t, wire.OServiceTLVTagsLinkedAccounts, tlvs[0].Tag)
  2419. }
  2420. })
  2421. }
  2422. }