| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475 |
- package foodgroup
- import (
- "bytes"
- "context"
- "errors"
- "fmt"
- "io"
- "log/slog"
- "testing"
- "time"
- "github.com/mk6i/open-oscar-server/config"
- "github.com/mk6i/open-oscar-server/state"
- "github.com/mk6i/open-oscar-server/wire"
- "github.com/google/uuid"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/mock"
- )
- func TestAuthService_BUCPLoginRequest(t *testing.T) {
- user := state.User{
- IdentScreenName: state.NewIdentScreenName("screenName"),
- DisplayScreenName: "screenName",
- AuthKey: "auth_key",
- }
- assert.NoError(t, user.HashPassword("the_password"))
- cases := []struct {
- // name is the unit test name
- name string
- // advertisedHost is the BOS host the client will connect to upon successful login
- advertisedHost string
- // cfg is the app configuration
- cfg config.Config
- // inputSNAC is the SNAC sent from the client to the server
- inputSNAC wire.SNAC_0x17_0x02_BUCPLoginRequest
- // mockParams is the list of params sent to mocks that satisfy this
- // method's dependencies
- mockParams mockParams
- // createAccount is the function that creates a new user account
- createAccount state.CreateAccountFunc
- // expectOutput is the SNAC sent from the server to client
- expectOutput wire.SNACMessage
- // wantErr is the error we expect from the method
- wantErr error
- // maxConcurrentLoginsPerUser is the maximum concurrent logins per user (only set for MultiConnFlagsRecentClient tests)
- maxConcurrentLoginsPerUser int
- }{
- {
- name: "AIM account exists, correct password, login OK, no concurrent logins",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
- wire.NewTLVBE(wire.LoginTLVTagsMultiConnFlags, wire.MultiConnFlagsRecentClient),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- cookieBakerParams: cookieBakerParams{
- cookieIssueParams: cookieIssueParams{
- {
- dataIn: func() []byte {
- loginCookie := state.ServerCookie{
- ScreenName: user.DisplayScreenName,
- MultiConnFlag: uint8(wire.MultiConnFlagsRecentClient),
- }
- buf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(loginCookie, buf))
- return buf.Bytes()
- }(),
- cookieOut: []byte("the-cookie"),
- },
- },
- },
- sessionRetrieverParams: sessionRetrieverParams{
- retrieveSessionParams: retrieveSessionParams{
- {
- screenName: user.IdentScreenName,
- result: nil,
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPLoginResponse,
- },
- Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
- wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
- wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
- },
- },
- },
- },
- maxConcurrentLoginsPerUser: 2,
- },
- {
- name: "AIM account exists, correct password, login OK, concurrent logins under limit",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
- wire.NewTLVBE(wire.LoginTLVTagsMultiConnFlags, wire.MultiConnFlagsRecentClient),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- cookieBakerParams: cookieBakerParams{
- cookieIssueParams: cookieIssueParams{
- {
- dataIn: func() []byte {
- loginCookie := state.ServerCookie{
- ScreenName: user.DisplayScreenName,
- MultiConnFlag: uint8(wire.MultiConnFlagsRecentClient),
- }
- buf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(loginCookie, buf))
- return buf.Bytes()
- }(),
- cookieOut: []byte("the-cookie"),
- },
- },
- },
- sessionRetrieverParams: sessionRetrieverParams{
- retrieveSessionParams: retrieveSessionParams{
- {
- screenName: user.IdentScreenName,
- result: func() *state.Session {
- // Create a session with 1 instance, under the limit
- sess := state.NewSession()
- sess.SetIdentScreenName(user.IdentScreenName)
- sess.SetDisplayScreenName(user.DisplayScreenName)
- sess.AddInstance()
- return sess
- }(),
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPLoginResponse,
- },
- Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
- wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
- wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
- },
- },
- },
- },
- maxConcurrentLoginsPerUser: 2,
- },
- {
- name: "login fails when concurrent login limit is reached",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
- wire.NewTLVBE(wire.LoginTLVTagsMultiConnFlags, wire.MultiConnFlagsRecentClient),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- sessionRetrieverParams: sessionRetrieverParams{
- retrieveSessionParams: retrieveSessionParams{
- {
- screenName: user.IdentScreenName,
- result: func() *state.Session {
- // Create a session with 2 instances (the max allowed)
- // This will cause InstanceCount() to return 2, which equals the limit of 2
- sess := state.NewSession()
- sess.SetIdentScreenName(user.IdentScreenName)
- sess.SetDisplayScreenName(user.DisplayScreenName)
- sess.AddInstance()
- sess.AddInstance()
- return sess
- }(),
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPLoginResponse,
- },
- Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: []wire.TLV{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrRateLimitExceeded),
- },
- },
- },
- },
- maxConcurrentLoginsPerUser: 2,
- },
- {
- name: "ICQ account exists, correct password, login OK",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsClientIdentity, "ICQ 2000b"),
- wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- cookieBakerParams: cookieBakerParams{
- cookieIssueParams: cookieIssueParams{
- {
- dataIn: func() []byte {
- loginCookie := state.ServerCookie{
- ScreenName: user.DisplayScreenName,
- ClientID: "ICQ 2000b",
- }
- buf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(loginCookie, buf))
- return buf.Bytes()
- }(),
- cookieOut: []byte("the-cookie"),
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPLoginResponse,
- },
- Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
- wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
- wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
- },
- },
- },
- },
- },
- {
- name: "AIM account exists, incorrect password, login fails",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, []byte("bad_password")),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPLoginResponse,
- },
- Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: []wire.TLV{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidPassword),
- },
- },
- },
- },
- },
- {
- name: "AIM account doesn't exist, login fails",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, []byte("password")),
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, []byte("non_existent_screen_name")),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: state.NewIdentScreenName("non_existent_screen_name"),
- result: nil,
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPLoginResponse,
- },
- Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: []wire.TLV{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("non_existent_screen_name")),
- wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidUsernameOrPassword),
- },
- },
- },
- },
- },
- {
- name: "AIM account is suspended",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, []byte("password")),
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, []byte("suspended_screen_name")),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: state.NewIdentScreenName("suspended_screen_name"),
- result: &state.User{
- SuspendedStatus: wire.LoginErrSuspendedAccount,
- },
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPLoginResponse,
- },
- Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: []wire.TLV{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("suspended_screen_name")),
- wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrSuspendedAccount),
- },
- },
- },
- },
- },
- {
- name: "ICQ account doesn't exist, login fails",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, []byte("password")),
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, []byte("100003")),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: state.NewIdentScreenName("100003"),
- result: nil,
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPLoginResponse,
- },
- Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: []wire.TLV{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("100003")),
- wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrICQUserErr),
- },
- },
- },
- },
- },
- {
- name: "account doesn't exist, authentication is disabled, account is created, login succeeds",
- advertisedHost: "127.0.0.1:5190",
- cfg: config.Config{
- DisableAuth: true,
- },
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: nil,
- },
- },
- },
- cookieBakerParams: cookieBakerParams{
- cookieIssueParams: cookieIssueParams{
- {
- dataIn: func() []byte {
- loginCookie := state.ServerCookie{
- ScreenName: user.DisplayScreenName,
- }
- buf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(loginCookie, buf))
- return buf.Bytes()
- }(),
- cookieOut: []byte("the-cookie"),
- },
- },
- },
- },
- createAccount: func(ctx context.Context, screenName state.DisplayScreenName, password string) error {
- assert.Equal(t, user.DisplayScreenName, screenName)
- assert.Equal(t, "welcome1", password)
- return nil
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPLoginResponse,
- },
- Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
- wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
- wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
- },
- },
- },
- },
- },
- {
- name: "AIM account doesn't exist, authentication is disabled, screen name has bad format, login fails",
- advertisedHost: "127.0.0.1:5190",
- cfg: config.Config{
- DisableAuth: true,
- },
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, "2coolforschool"),
- wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: state.NewIdentScreenName("2coolforschool"),
- result: nil,
- },
- },
- },
- },
- createAccount: func(ctx context.Context, screenName state.DisplayScreenName, password string) error {
- assert.Equal(t, state.DisplayScreenName("2coolforschool"), screenName)
- assert.Equal(t, "welcome1", password)
- return state.ErrAIMHandleInvalidFormat
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPLoginResponse,
- },
- Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("2coolforschool")),
- wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidUsernameOrPassword),
- },
- },
- },
- },
- },
- {
- name: "ICQ account doesn't exist, authentication is disabled, UIN has bad format, login fails",
- advertisedHost: "127.0.0.1:5190",
- cfg: config.Config{
- DisableAuth: true,
- },
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, "99"),
- wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: state.NewIdentScreenName("99"),
- result: nil,
- },
- },
- },
- },
- createAccount: func(ctx context.Context, screenName state.DisplayScreenName, password string) error {
- assert.Equal(t, state.DisplayScreenName("99"), screenName)
- assert.Equal(t, "welcome1", password)
- return state.ErrICQUINInvalidFormat
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPLoginResponse,
- },
- Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("99")),
- wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrICQUserErr),
- },
- },
- },
- },
- },
- {
- name: "account exists, password is invalid, authentication is disabled, login succeeds",
- advertisedHost: "127.0.0.1:5190",
- cfg: config.Config{
- DisableAuth: true,
- },
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, []byte("bad-password-hash")),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- cookieBakerParams: cookieBakerParams{
- cookieIssueParams: cookieIssueParams{
- {
- dataIn: func() []byte {
- loginCookie := state.ServerCookie{
- ScreenName: user.DisplayScreenName,
- }
- buf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(loginCookie, buf))
- return buf.Bytes()
- }(),
- cookieOut: []byte("the-cookie"),
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPLoginResponse,
- },
- Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
- wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
- wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
- },
- },
- },
- },
- },
- {
- name: "login fails on user manager lookup",
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- err: io.EOF,
- },
- },
- },
- },
- wantErr: io.EOF,
- },
- {
- name: "login with TOC client - success",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsRoastedTOCPassword, wire.RoastTOCPassword([]byte("the_password"))),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- cookieBakerParams: cookieBakerParams{
- cookieIssueParams: cookieIssueParams{
- {
- dataIn: func() []byte {
- loginCookie := state.ServerCookie{
- ScreenName: user.DisplayScreenName,
- }
- buf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(loginCookie, buf))
- return buf.Bytes()
- }(),
- cookieOut: []byte("the-cookie"),
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPLoginResponse,
- },
- Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
- wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
- wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
- },
- },
- },
- },
- },
- {
- name: "AIM account exists, correct password, linked accounts in response",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- cookieBakerParams: cookieBakerParams{
- cookieIssueParams: cookieIssueParams{
- {
- dataIn: func() []byte {
- loginCookie := state.ServerCookie{
- ScreenName: user.DisplayScreenName,
- }
- buf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(loginCookie, buf))
- return buf.Bytes()
- }(),
- cookieOut: []byte("the-cookie"),
- },
- },
- },
- feedbagManagerParams: feedbagManagerParams{
- feedbagParams: feedbagParams{
- {
- screenName: user.IdentScreenName,
- results: []wire.FeedbagItem{{ClassID: wire.FeedbagClassIdAlInfo, Name: "linked1"}},
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPLoginResponse,
- },
- Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
- wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
- wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
- 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>`),
- },
- },
- },
- },
- },
- {
- name: "feedbag error during login, returns error",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- cookieBakerParams: cookieBakerParams{
- cookieIssueParams: cookieIssueParams{
- {
- dataIn: func() []byte {
- loginCookie := state.ServerCookie{
- ScreenName: user.DisplayScreenName,
- }
- buf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(loginCookie, buf))
- return buf.Bytes()
- }(),
- cookieOut: []byte("the-cookie"),
- },
- },
- },
- feedbagManagerParams: feedbagManagerParams{
- feedbagParams: feedbagParams{
- {
- screenName: user.IdentScreenName,
- err: io.EOF,
- },
- },
- },
- },
- wantErr: io.EOF,
- },
- {
- name: "login with TOC client - failed",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsRoastedTOCPassword, wire.RoastTOCPassword([]byte("the_wrong_password"))),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPLoginResponse,
- },
- Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, "screenName"),
- wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidPassword),
- },
- },
- },
- },
- },
- }
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- userManager := newMockUserManager(t)
- for _, params := range tc.mockParams.userManagerParams.getUserParams {
- userManager.EXPECT().
- User(matchContext(), params.screenName).
- Return(params.result, params.err)
- }
- cookieBaker := newMockCookieBaker(t)
- for _, params := range tc.mockParams.cookieIssueParams {
- cookieBaker.EXPECT().
- Issue(params.dataIn).
- Return(params.cookieOut, params.err)
- }
- sessionRetriever := newMockSessionRetriever(t)
- for _, params := range tc.mockParams.retrieveSessionParams {
- sessionRetriever.EXPECT().
- RetrieveSession(params.screenName).
- Return(params.result)
- }
- feedbagManager := newMockFeedbagManager(t)
- for _, params := range tc.mockParams.feedbagParams {
- feedbagManager.EXPECT().
- Feedbag(matchContext(), params.screenName).
- Return(params.results, params.err)
- }
- feedbagManager.EXPECT().Feedbag(matchContext(), mock.Anything).Return(nil, nil).Maybe()
- svc := AuthService{
- config: tc.cfg,
- cookieBaker: cookieBaker,
- userManager: userManager,
- sessionRetriever: sessionRetriever,
- feedbagManager: feedbagManager,
- maxConcurrentLoginsPerUser: 2,
- createAccount: tc.createAccount,
- logger: slog.Default(),
- }
- outputSNAC, err := svc.BUCPLogin(context.Background(), tc.inputSNAC, tc.advertisedHost)
- assert.ErrorIs(t, err, tc.wantErr)
- assert.Equal(t, tc.expectOutput, outputSNAC)
- })
- }
- }
- func TestAuthService_FLAPLogin(t *testing.T) {
- user := state.User{
- AuthKey: "auth_key",
- DisplayScreenName: "screenName",
- IdentScreenName: state.NewIdentScreenName("screenName"),
- }
- assert.NoError(t, user.HashPassword("the_password"))
- cases := []struct {
- // name is the unit test name
- name string
- // advertisedHost is the BOS host the client will connect to upon successful login
- advertisedHost string
- // cfg is the app configuration
- cfg config.Config
- // inputSNAC is the authentication FLAP frame sent from the client to the server
- inputSNAC wire.FLAPSignonFrame
- // mockParams is the list of params sent to mocks that satisfy this
- // method's dependencies
- mockParams mockParams
- // createAccount is the function that creates a new user account
- createAccount state.CreateAccountFunc
- // expectOutput is the response sent from the server to client
- expectOutput wire.TLVRestBlock
- // wantErr is the error we expect from the method
- wantErr error
- }{
- {
- name: "AIM account exists, correct password, login OK",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.FLAPSignonFrame{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARPassword([]byte("the_password"))),
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- cookieBakerParams: cookieBakerParams{
- cookieIssueParams: cookieIssueParams{
- {
- dataIn: func() []byte {
- loginCookie := state.ServerCookie{
- ScreenName: user.DisplayScreenName,
- }
- buf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(loginCookie, buf))
- return buf.Bytes()
- }(),
- cookieOut: []byte("the-cookie"),
- },
- },
- },
- },
- expectOutput: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
- wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
- wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
- },
- },
- },
- {
- name: "ICQ account exists, correct password, login OK",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.FLAPSignonFrame{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsClientIdentity, "ICQ 2000b"),
- wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARPassword([]byte("the_password"))),
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- cookieBakerParams: cookieBakerParams{
- cookieIssueParams: cookieIssueParams{
- {
- dataIn: func() []byte {
- loginCookie := state.ServerCookie{
- ScreenName: user.DisplayScreenName,
- ClientID: "ICQ 2000b",
- }
- buf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(loginCookie, buf))
- return buf.Bytes()
- }(),
- cookieOut: []byte("the-cookie"),
- },
- },
- },
- },
- expectOutput: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
- wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
- wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
- },
- },
- },
- {
- name: "AIM account exists, incorrect password, login fails",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.FLAPSignonFrame{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, []byte("bad_roasted_password")),
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- },
- expectOutput: wire.TLVRestBlock{
- TLVList: []wire.TLV{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidPassword),
- },
- },
- },
- {
- name: "AIM account doesn't exist, login fails",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.FLAPSignonFrame{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARPassword([]byte("the_password"))),
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, []byte("non_existent_screen_name")),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: state.NewIdentScreenName("non_existent_screen_name"),
- result: nil,
- },
- },
- },
- },
- expectOutput: wire.TLVRestBlock{
- TLVList: []wire.TLV{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("non_existent_screen_name")),
- wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidUsernameOrPassword),
- },
- },
- },
- {
- name: "ICQ account doesn't exist, login fails",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.FLAPSignonFrame{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsClientIdentity, "ICQ 2000b"),
- wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARPassword([]byte("the_password"))),
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, []byte("100003")),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: state.NewIdentScreenName("100003"),
- result: nil,
- },
- },
- },
- },
- expectOutput: wire.TLVRestBlock{
- TLVList: []wire.TLV{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("100003")),
- wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrICQUserErr),
- },
- },
- },
- {
- name: "account doesn't exist, authentication is disabled, account is created, login succeeds",
- advertisedHost: "127.0.0.1:5190",
- cfg: config.Config{
- DisableAuth: true,
- },
- inputSNAC: wire.FLAPSignonFrame{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARPassword([]byte("the_password"))),
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: nil,
- },
- },
- },
- cookieBakerParams: cookieBakerParams{
- cookieIssueParams: cookieIssueParams{
- {
- dataIn: func() []byte {
- loginCookie := state.ServerCookie{
- ScreenName: user.DisplayScreenName,
- }
- buf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(loginCookie, buf))
- return buf.Bytes()
- }(),
- cookieOut: []byte("the-cookie"),
- },
- },
- },
- },
- createAccount: func(ctx context.Context, screenName state.DisplayScreenName, password string) error {
- assert.Equal(t, user.DisplayScreenName, screenName)
- assert.Equal(t, "welcome1", password)
- return nil
- },
- expectOutput: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
- wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
- wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
- },
- },
- },
- {
- name: "account exists, password is invalid, authentication is disabled, login succeeds",
- advertisedHost: "127.0.0.1:5190",
- cfg: config.Config{
- DisableAuth: true,
- },
- inputSNAC: wire.FLAPSignonFrame{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, "bad-roasted-password"),
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- cookieBakerParams: cookieBakerParams{
- cookieIssueParams: cookieIssueParams{
- {
- dataIn: func() []byte {
- loginCookie := state.ServerCookie{
- ScreenName: user.DisplayScreenName,
- }
- buf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(loginCookie, buf))
- return buf.Bytes()
- }(),
- cookieOut: []byte("the-cookie"),
- },
- },
- },
- },
- expectOutput: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
- wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
- wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
- },
- },
- },
- {
- name: "feedbag error during login, returns error",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.FLAPSignonFrame{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARPassword([]byte("the_password"))),
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- cookieBakerParams: cookieBakerParams{
- cookieIssueParams: cookieIssueParams{
- {
- dataIn: func() []byte {
- loginCookie := state.ServerCookie{
- ScreenName: user.DisplayScreenName,
- }
- buf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(loginCookie, buf))
- return buf.Bytes()
- }(),
- cookieOut: []byte("the-cookie"),
- },
- },
- },
- feedbagManagerParams: feedbagManagerParams{
- feedbagParams: feedbagParams{
- {
- screenName: user.IdentScreenName,
- err: io.EOF,
- },
- },
- },
- },
- wantErr: io.EOF,
- },
- {
- name: "login fails on user manager lookup",
- inputSNAC: wire.FLAPSignonFrame{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARPassword([]byte("the_password"))),
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- err: io.EOF,
- },
- },
- },
- },
- wantErr: io.EOF,
- },
- {
- name: "login with AIM 1.1.19 for Java - success",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.FLAPSignonFrame{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsClientIdentity, "AOL Instant Messenger (TM) version 1.1.19 for Java"),
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARJavaPassword([]byte("the_password"))),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- cookieBakerParams: cookieBakerParams{
- cookieIssueParams: cookieIssueParams{
- {
- dataIn: func() []byte {
- loginCookie := state.ServerCookie{
- ScreenName: user.DisplayScreenName,
- ClientID: "AOL Instant Messenger (TM) version 1.1.19 for Java",
- }
- buf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(loginCookie, buf))
- return buf.Bytes()
- }(),
- cookieOut: []byte("the-cookie"),
- },
- },
- },
- },
- expectOutput: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:5190"),
- wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
- wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
- },
- },
- },
- {
- name: "login with AIM 1.1.19 for Java - failed",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.FLAPSignonFrame{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsClientIdentity, "AOL Instant Messenger (TM) version 1.1.19 for Java"),
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
- wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, wire.RoastOSCARJavaPassword([]byte("the_wrong_password"))),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- },
- expectOutput: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, "screenName"),
- wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidPassword),
- },
- },
- },
- }
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- userManager := newMockUserManager(t)
- for _, params := range tc.mockParams.userManagerParams.getUserParams {
- userManager.EXPECT().
- User(matchContext(), params.screenName).
- Return(params.result, params.err)
- }
- cookieBaker := newMockCookieBaker(t)
- for _, params := range tc.mockParams.cookieIssueParams {
- cookieBaker.EXPECT().
- Issue(params.dataIn).
- Return(params.cookieOut, params.err)
- }
- feedbagManager := newMockFeedbagManager(t)
- for _, params := range tc.mockParams.feedbagParams {
- feedbagManager.EXPECT().
- Feedbag(matchContext(), params.screenName).
- Return(params.results, params.err)
- }
- feedbagManager.EXPECT().Feedbag(matchContext(), mock.Anything).Return(nil, nil).Maybe()
- svc := AuthService{
- config: tc.cfg,
- cookieBaker: cookieBaker,
- userManager: userManager,
- feedbagManager: feedbagManager,
- createAccount: tc.createAccount,
- logger: slog.Default(),
- }
- outputSNAC, err := svc.FLAPLogin(context.Background(), tc.inputSNAC, tc.advertisedHost)
- assert.ErrorIs(t, err, tc.wantErr)
- assert.Equal(t, tc.expectOutput, outputSNAC)
- })
- }
- }
- func TestAuthService_KerberosLogin(t *testing.T) {
- user := state.User{
- AuthKey: "auth_key",
- DisplayScreenName: "screenName",
- IdentScreenName: state.NewIdentScreenName("screenName"),
- }
- assert.NoError(t, user.HashPassword("the_password"))
- cases := []struct {
- // name is the unit test name
- name string
- // advertisedHost is the BOS host the client will connect to upon successful login
- advertisedHost string
- // cfg is the app configuration
- cfg config.Config
- // inputSNAC is the kerberos SNAC sent from the client to the server
- inputSNAC wire.SNAC_0x050C_0x0002_KerberosLoginRequest
- // mockParams is the list of params sent to mocks that satisfy this
- // method's dependencies
- mockParams mockParams
- // createAccount is the function that creates a new user account
- createAccount state.CreateAccountFunc
- // expectOutput is the response sent from the server to client
- expectOutput wire.SNACMessage
- // wantErr is the error we expect from the method
- wantErr error
- // timeNow returns a canned time value
- timeNow func() time.Time
- }{
- {
- name: "AIM account exists, correct password, login OK",
- advertisedHost: "127.0.0.1:5190",
- timeNow: func() time.Time {
- return time.Unix(1000, 0)
- },
- inputSNAC: wire.SNAC_0x050C_0x0002_KerberosLoginRequest{
- RequestID: 54321,
- ClientPrincipal: user.DisplayScreenName.String(),
- TicketRequestMetadata: wire.TLVBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.KerberosTLVTicketRequest, wire.KerberosLoginRequestTicket{
- Password: []byte("the_password"),
- }),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- cookieBakerParams: cookieBakerParams{
- cookieIssueParams: cookieIssueParams{
- {
- dataIn: func() []byte {
- loginCookie := state.ServerCookie{
- Service: wire.BOS,
- ScreenName: user.DisplayScreenName,
- ClientID: "",
- MultiConnFlag: uint8(wire.MultiConnFlagsRecentClient),
- KerberosAuth: 1,
- }
- buf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(loginCookie, buf))
- return buf.Bytes()
- }(),
- cookieOut: []byte("the-cookie"),
- },
- },
- },
- sessionRetrieverParams: sessionRetrieverParams{
- retrieveSessionParams: retrieveSessionParams{
- {
- screenName: user.IdentScreenName,
- result: nil,
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.Kerberos,
- SubGroup: wire.KerberosLoginSuccessResponse,
- },
- Body: wire.SNAC_0x050C_0x0003_KerberosLoginSuccessResponse{
- RequestID: 54321,
- Epoch: 1000,
- ClientPrincipal: user.DisplayScreenName.String(),
- ClientRealm: "AOL",
- Tickets: []wire.KerberosTicket{
- {
- PVNO: 0x5,
- EncTicket: []uint8{},
- TicketRealm: "AOL",
- ServicePrincipal: "im/boss",
- ClientRealm: "AOL",
- ClientPrincipal: user.DisplayScreenName.String(),
- AuthTime: 1000,
- StartTime: 1000,
- EndTime: 87400,
- Unknown4: 0x60000000,
- Unknown5: 0x40000000,
- ConnectionMetadata: wire.TLVBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.KerberosTLVBOSServerInfo, wire.KerberosBOSServerInfo{
- Unknown: 1,
- ConnectionInfo: wire.TLVBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.KerberosTLVHostname, "127.0.0.1:5190"),
- wire.NewTLVBE(wire.KerberosTLVCookie, []byte("the-cookie")),
- wire.NewTLVBE(wire.KerberosTLVConnSettings, wire.KerberosConnUseSSL),
- },
- },
- }),
- },
- },
- },
- },
- },
- },
- },
- {
- name: "AIM account exists, incorrect password, login failed",
- advertisedHost: "127.0.0.1:5190",
- timeNow: func() time.Time {
- return time.Unix(1000, 0)
- },
- inputSNAC: wire.SNAC_0x050C_0x0002_KerberosLoginRequest{
- RequestID: 54321,
- ClientPrincipal: user.DisplayScreenName.String(),
- TicketRequestMetadata: wire.TLVBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.KerberosTLVTicketRequest, wire.KerberosLoginRequestTicket{
- Password: []byte("the_WRONG_password"),
- }),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: nil,
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.Kerberos,
- SubGroup: wire.KerberosKerberosLoginErrResponse,
- },
- Body: wire.SNAC_0x050C_0x0004_KerberosLoginErrResponse{
- KerbRequestID: 54321,
- ScreenName: user.DisplayScreenName.String(),
- ErrCode: wire.KerberosErrAuthFailure,
- Message: "Auth failure",
- },
- },
- },
- {
- name: "AIM account exists, correct roasted password, login OK",
- advertisedHost: "127.0.0.1:5190",
- timeNow: func() time.Time {
- return time.Unix(1000, 0)
- },
- inputSNAC: wire.SNAC_0x050C_0x0002_KerberosLoginRequest{
- RequestID: 54321,
- ClientPrincipal: user.DisplayScreenName.String(),
- TicketRequestMetadata: wire.TLVBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.KerberosTLVTicketRequest, wire.KerberosLoginRequestTicket{
- Version: 4,
- Password: wire.RoastKerberosPassword([]byte("the_password")),
- }),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- cookieBakerParams: cookieBakerParams{
- cookieIssueParams: cookieIssueParams{
- {
- dataIn: func() []byte {
- loginCookie := state.ServerCookie{
- Service: wire.BOS,
- ScreenName: user.DisplayScreenName,
- ClientID: "",
- MultiConnFlag: uint8(wire.MultiConnFlagsRecentClient),
- KerberosAuth: 1,
- }
- buf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(loginCookie, buf))
- return buf.Bytes()
- }(),
- cookieOut: []byte("the-cookie"),
- },
- },
- },
- sessionRetrieverParams: sessionRetrieverParams{
- retrieveSessionParams: retrieveSessionParams{
- {
- screenName: user.IdentScreenName,
- result: nil,
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.Kerberos,
- SubGroup: wire.KerberosLoginSuccessResponse,
- },
- Body: wire.SNAC_0x050C_0x0003_KerberosLoginSuccessResponse{
- RequestID: 54321,
- Epoch: 1000,
- ClientPrincipal: user.DisplayScreenName.String(),
- ClientRealm: "AOL",
- Tickets: []wire.KerberosTicket{
- {
- PVNO: 0x5,
- EncTicket: []uint8{},
- TicketRealm: "AOL",
- ServicePrincipal: "im/boss",
- ClientRealm: "AOL",
- ClientPrincipal: user.DisplayScreenName.String(),
- AuthTime: 1000,
- StartTime: 1000,
- EndTime: 87400,
- Unknown4: 0x60000000,
- Unknown5: 0x40000000,
- ConnectionMetadata: wire.TLVBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.KerberosTLVBOSServerInfo, wire.KerberosBOSServerInfo{
- Unknown: 1,
- ConnectionInfo: wire.TLVBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.KerberosTLVHostname, "127.0.0.1:5190"),
- wire.NewTLVBE(wire.KerberosTLVCookie, []byte("the-cookie")),
- wire.NewTLVBE(wire.KerberosTLVConnSettings, wire.KerberosConnUseSSL),
- },
- },
- }),
- },
- },
- },
- },
- },
- },
- },
- {
- name: "AIM account exists, incorrect roasted password, login failed",
- advertisedHost: "127.0.0.1:5190",
- timeNow: func() time.Time {
- return time.Unix(1000, 0)
- },
- inputSNAC: wire.SNAC_0x050C_0x0002_KerberosLoginRequest{
- RequestID: 54321,
- ClientPrincipal: user.DisplayScreenName.String(),
- TicketRequestMetadata: wire.TLVBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.KerberosTLVTicketRequest, wire.KerberosLoginRequestTicket{
- Version: 4,
- Password: wire.RoastKerberosPassword([]byte("the_WRONG_password")),
- }),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: user.IdentScreenName,
- result: &user,
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.Kerberos,
- SubGroup: wire.KerberosKerberosLoginErrResponse,
- },
- Body: wire.SNAC_0x050C_0x0004_KerberosLoginErrResponse{
- KerbRequestID: 54321,
- ScreenName: user.DisplayScreenName.String(),
- ErrCode: wire.KerberosErrAuthFailure,
- Message: "Auth failure",
- },
- },
- },
- }
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- userManager := newMockUserManager(t)
- for _, params := range tc.mockParams.userManagerParams.getUserParams {
- userManager.EXPECT().
- User(matchContext(), params.screenName).
- Return(params.result, params.err)
- }
- cookieBaker := newMockCookieBaker(t)
- for _, params := range tc.mockParams.cookieIssueParams {
- cookieBaker.EXPECT().
- Issue(params.dataIn).
- Return(params.cookieOut, params.err)
- }
- sessionRetriever := newMockSessionRetriever(t)
- for _, params := range tc.mockParams.retrieveSessionParams {
- sessionRetriever.EXPECT().
- RetrieveSession(params.screenName).
- Return(params.result)
- }
- feedbagManager := newMockFeedbagManager(t)
- feedbagManager.EXPECT().Feedbag(matchContext(), mock.Anything).Return(nil, nil).Maybe()
- svc := AuthService{
- config: tc.cfg,
- cookieBaker: cookieBaker,
- userManager: userManager,
- sessionRetriever: sessionRetriever,
- feedbagManager: feedbagManager,
- timeNow: tc.timeNow,
- maxConcurrentLoginsPerUser: 2,
- createAccount: tc.createAccount,
- logger: slog.Default(),
- }
- outputSNAC, err := svc.KerberosLogin(context.Background(), tc.inputSNAC, tc.advertisedHost)
- assert.ErrorIs(t, err, tc.wantErr)
- assert.Equal(t, tc.expectOutput, outputSNAC)
- })
- }
- }
- func TestAuthService_BUCPChallengeRequest(t *testing.T) {
- sessUUID := uuid.UUID{1, 2, 3}
- cases := []struct {
- // name is the unit test name
- name string
- // advertisedHost is the BOS host the client will connect to upon successful login
- advertisedHost string
- // cfg is the app configuration
- cfg config.Config
- // inputSNAC is the SNAC sent from the client to the server
- inputSNAC wire.SNAC_0x17_0x06_BUCPChallengeRequest
- // mockParams is the list of params sent to mocks that satisfy this
- // method's dependencies
- mockParams mockParams
- // expectOutput is the SNAC sent from the server to client
- expectOutput wire.SNACMessage
- // wantErr is the error we expect from the method
- wantErr error
- }{
- {
- name: "login with valid username, expect OK login response",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.SNAC_0x17_0x06_BUCPChallengeRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, "sn_user_a"),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: state.NewIdentScreenName("sn_user_a"),
- result: &state.User{
- IdentScreenName: state.NewIdentScreenName("sn_user_a"),
- AuthKey: "auth_key_user_a",
- },
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPChallengeResponse,
- },
- Body: wire.SNAC_0x17_0x07_BUCPChallengeResponse{
- AuthKey: "auth_key_user_a",
- },
- },
- },
- {
- name: "login with invalid username, expect OK login response (Cfg.DisableAuth=true)",
- advertisedHost: "127.0.0.1:5190",
- cfg: config.Config{
- DisableAuth: true,
- },
- inputSNAC: wire.SNAC_0x17_0x06_BUCPChallengeRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, "sn_user_b"),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: state.NewIdentScreenName("sn_user_b"),
- result: nil,
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPChallengeResponse,
- },
- Body: wire.SNAC_0x17_0x07_BUCPChallengeResponse{
- AuthKey: sessUUID.String(),
- },
- },
- },
- {
- name: "login with invalid username, expect failed login response (Cfg.DisableAuth=false)",
- advertisedHost: "127.0.0.1:5190",
- inputSNAC: wire.SNAC_0x17_0x06_BUCPChallengeRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, "sn_user_b"),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: state.NewIdentScreenName("sn_user_b"),
- result: nil,
- },
- },
- },
- },
- expectOutput: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.BUCP,
- SubGroup: wire.BUCPLoginResponse,
- },
- Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, uint16(0x01)),
- },
- },
- },
- },
- },
- {
- name: "login fails on user manager lookup",
- inputSNAC: wire.SNAC_0x17_0x06_BUCPChallengeRequest{
- TLVRestBlock: wire.TLVRestBlock{
- TLVList: wire.TLVList{
- wire.NewTLVBE(wire.LoginTLVTagsScreenName, "sn_user_b"),
- },
- },
- },
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: state.NewIdentScreenName("sn_user_b"),
- err: io.EOF,
- },
- },
- },
- },
- wantErr: io.EOF,
- },
- }
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- userManager := newMockUserManager(t)
- for _, params := range tc.mockParams.userManagerParams.getUserParams {
- userManager.EXPECT().
- User(matchContext(), params.screenName).
- Return(params.result, params.err)
- }
- svc := AuthService{
- config: tc.cfg,
- userManager: userManager,
- logger: slog.Default(),
- }
- fnNewUUID := func() uuid.UUID {
- return sessUUID
- }
- outputSNAC, err := svc.BUCPChallenge(context.Background(), tc.inputSNAC, fnNewUUID)
- assert.ErrorIs(t, err, tc.wantErr)
- assert.Equal(t, tc.expectOutput, outputSNAC)
- })
- }
- }
- func TestAuthService_RegisterChatSession_HappyPath(t *testing.T) {
- instance := newTestInstance("ScreenName")
- serverCookie := state.ServerCookie{
- ChatCookie: "the-chat-cookie",
- ScreenName: instance.DisplayScreenName(),
- }
- chatSessionRegistry := newMockChatSessionRegistry(t)
- chatSessionRegistry.EXPECT().
- AddSession(mock.Anything, serverCookie.ChatCookie, instance.DisplayScreenName(), mock.Anything).
- Return(instance, nil)
- chatCookieBuf := &bytes.Buffer{}
- assert.NoError(t, wire.MarshalBE(serverCookie, chatCookieBuf))
- svc := NewAuthService(config.Config{}, nil, nil, chatSessionRegistry, nil, nil, nil, nil, nil, nil, wire.DefaultRateLimitClasses(), nil, slog.Default())
- have, err := svc.RegisterChatSession(context.Background(), serverCookie, nil)
- assert.NoError(t, err)
- assert.Equal(t, instance, have)
- }
- func TestAuthService_RegisterBOSSession(t *testing.T) {
- screenName := state.DisplayScreenName("UserScreenName")
- aimAuthCookie := state.ServerCookie{
- ScreenName: screenName,
- }
- uin := state.DisplayScreenName("100003")
- icqAuthCookie := state.ServerCookie{
- ScreenName: uin,
- }
- cases := []struct {
- // name is the unit test name
- name string
- // cookieOut is the auth cookieOut that contains session information
- cookie state.ServerCookie
- // cfg is the server config
- cfg config.Config
- // createAccount is called to auto-create a missing user when DisableAuth is set
- createAccount state.CreateAccountFunc
- // mockParams is the list of params sent to mocks that satisfy this
- // method's dependencies
- mockParams mockParams
- // wantSess asserts the values of one or more session properties
- wantSess func(*state.SessionInstance) bool
- // wantErr is the error we expect from the method
- wantErr error
- }{
- {
- name: "successfully register an AIM session",
- cookie: aimAuthCookie,
- mockParams: mockParams{
- sessionRegistryParams: sessionRegistryParams{
- addSessionParams: addSessionParams{
- {
- screenName: screenName,
- doMultiSess: false,
- result: newTestInstance(screenName),
- },
- },
- },
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: screenName.IdentScreenName(),
- result: &state.User{
- IdentScreenName: screenName.IdentScreenName(),
- DisplayScreenName: screenName,
- },
- },
- },
- },
- accountManagerParams: accountManagerParams{
- accountManagerConfirmStatusParams: accountManagerConfirmStatusParams{
- {
- screenName: screenName.IdentScreenName(),
- confirmStatus: true,
- },
- },
- },
- bartItemManagerParams: bartItemManagerParams{
- buddyIconMetadataParams: buddyIconMetadataParams{
- {
- screenName: screenName.IdentScreenName(),
- result: &wire.BARTID{
- Type: wire.BARTTypesBuddyIcon,
- BARTInfo: wire.BARTInfo{
- Flags: wire.BARTFlagsKnown,
- Hash: []byte{'m', 'y', 'i', 'c', 'o', 'n'},
- },
- },
- },
- },
- },
- },
- wantSess: func(instance *state.SessionInstance) bool {
- want := wire.BARTID{
- Type: wire.BARTTypesBuddyIcon,
- BARTInfo: wire.BARTInfo{
- Flags: wire.BARTFlagsKnown,
- Hash: []byte{'m', 'y', 'i', 'c', 'o', 'n'},
- },
- }
- has, hasIcon := instance.Session().BuddyIcon()
- return assert.True(t, hasIcon) && assert.Equal(t, want, has)
- },
- },
- {
- name: "successfully register an AIM bot session",
- cookie: aimAuthCookie,
- mockParams: mockParams{
- sessionRegistryParams: sessionRegistryParams{
- addSessionParams: addSessionParams{
- {
- screenName: screenName,
- doMultiSess: false,
- result: newTestInstance(screenName),
- },
- },
- },
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: screenName.IdentScreenName(),
- result: &state.User{
- IdentScreenName: screenName.IdentScreenName(),
- DisplayScreenName: screenName,
- IsBot: true,
- },
- },
- },
- },
- accountManagerParams: accountManagerParams{
- accountManagerConfirmStatusParams: accountManagerConfirmStatusParams{
- {
- screenName: screenName.IdentScreenName(),
- confirmStatus: true,
- },
- },
- },
- bartItemManagerParams: bartItemManagerParams{
- buddyIconMetadataParams: buddyIconMetadataParams{
- {
- screenName: screenName.IdentScreenName(),
- result: nil,
- },
- },
- },
- },
- wantSess: func(instance *state.SessionInstance) bool {
- return instance.Session().AllUserInfoBitmask(wire.OServiceUserFlagBot)
- },
- },
- {
- name: "successfully register an ICQ session",
- cookie: icqAuthCookie,
- mockParams: mockParams{
- sessionRegistryParams: sessionRegistryParams{
- addSessionParams: addSessionParams{
- {
- screenName: uin,
- result: newTestInstance(uin),
- },
- },
- },
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: uin.IdentScreenName(),
- result: &state.User{
- IdentScreenName: uin.IdentScreenName(),
- DisplayScreenName: uin,
- },
- },
- },
- },
- accountManagerParams: accountManagerParams{
- accountManagerConfirmStatusParams: accountManagerConfirmStatusParams{
- {
- screenName: uin.IdentScreenName(),
- confirmStatus: true,
- },
- },
- },
- bartItemManagerParams: bartItemManagerParams{
- buddyIconMetadataParams: buddyIconMetadataParams{
- {
- screenName: uin.IdentScreenName(),
- result: nil,
- },
- },
- },
- },
- wantSess: func(instance *state.SessionInstance) bool {
- uinMatches := fmt.Sprintf("%d", instance.UIN()) == uin.String()
- flagsMatch := instance.Session().AllUserInfoBitmask(wire.OServiceUserFlagICQ)
- return uinMatches && flagsMatch
- },
- },
- {
- name: "user not found, DisableAuth false, return error",
- cookie: aimAuthCookie,
- cfg: config.Config{DisableAuth: false},
- wantErr: errors.New("user not found"),
- mockParams: mockParams{
- userManagerParams: userManagerParams{
- getUserParams: getUserParams{
- {
- screenName: screenName.IdentScreenName(),
- result: nil,
- },
- },
- },
- },
- },
- }
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- sessionRegistry := newMockSessionRegistry(t)
- for _, params := range tc.mockParams.addSessionParams {
- sessionRegistry.EXPECT().
- AddSession(mock.Anything, params.screenName, params.doMultiSess, mock.Anything, mock.Anything).
- Return(params.result, params.err)
- }
- userManager := newMockUserManager(t)
- for _, params := range tc.mockParams.userManagerParams.getUserParams {
- userManager.EXPECT().
- User(matchContext(), params.screenName).
- Return(params.result, nil).Once()
- }
- accountManager := newMockAccountManager(t)
- for _, params := range tc.mockParams.accountManagerConfirmStatusParams {
- accountManager.EXPECT().
- ConfirmStatus(matchContext(), params.screenName).
- Return(params.confirmStatus, nil)
- }
- bartItemManager := newMockBARTItemManager(t)
- for _, params := range tc.mockParams.buddyIconMetadataParams {
- bartItemManager.EXPECT().
- BuddyIconMetadata(matchContext(), params.screenName).
- Return(params.result, params.err)
- }
- svc := NewAuthService(tc.cfg, sessionRegistry, nil, nil, userManager, nil, nil, accountManager, bartItemManager, nil, wire.DefaultRateLimitClasses(), tc.createAccount, slog.Default())
- have, err := svc.RegisterBOSSession(context.Background(), tc.cookie, nil)
- if tc.wantErr != nil {
- assert.ErrorContains(t, err, tc.wantErr.Error())
- return
- }
- assert.NoError(t, err)
- if tc.wantSess != nil {
- assert.True(t, tc.wantSess(have))
- }
- })
- }
- }
- func TestAuthService_RetrieveBOSSession_HappyPath(t *testing.T) {
- instance := newTestInstance("screenName", sessOptSignonComplete)
- aimAuthCookie := state.ServerCookie{
- ScreenName: instance.DisplayScreenName(),
- SessionNum: instance.Num(),
- }
- sessionRetriever := newMockSessionRetriever(t)
- sessionRetriever.EXPECT().
- RetrieveSession(instance.IdentScreenName()).
- Return(instance.Session())
- userManager := newMockUserManager(t)
- userManager.EXPECT().
- User(matchContext(), instance.IdentScreenName()).
- Return(&state.User{IdentScreenName: instance.IdentScreenName()}, nil)
- svc := NewAuthService(config.Config{}, nil, sessionRetriever, nil, userManager, nil, nil, nil, nil, nil, wire.DefaultRateLimitClasses(), nil, slog.Default())
- have, err := svc.RetrieveBOSSession(context.Background(), aimAuthCookie)
- assert.NoError(t, err)
- assert.Equal(t, instance, have)
- }
- func TestAuthService_RetrieveBOSSession_SessionNotFound(t *testing.T) {
- instance := newTestInstance("screenName")
- aimAuthCookie := state.ServerCookie{
- ScreenName: instance.DisplayScreenName(),
- SessionNum: instance.Num(),
- }
- sessionRetriever := newMockSessionRetriever(t)
- sessionRetriever.EXPECT().
- RetrieveSession(instance.IdentScreenName()).
- Return(nil)
- userManager := newMockUserManager(t)
- userManager.EXPECT().
- User(matchContext(), instance.IdentScreenName()).
- Return(&state.User{IdentScreenName: instance.IdentScreenName()}, nil)
- svc := NewAuthService(config.Config{}, nil, sessionRetriever, nil, userManager, nil, nil, nil, nil, nil, wire.DefaultRateLimitClasses(), nil, slog.Default())
- have, err := svc.RetrieveBOSSession(context.Background(), aimAuthCookie)
- assert.NoError(t, err)
- assert.Nil(t, have)
- }
- func TestAuthService_SignoutChat(t *testing.T) {
- tests := []struct {
- // name is the unit test name
- name string
- // instance is the session of the user signing out
- instance *state.SessionInstance
- // mockParams is the list of params sent to mocks that satisfy this
- // method's dependencies
- mockParams mockParams
- }{
- {
- name: "user signs out of chat room, room is empty after user leaves",
- instance: newTestInstance("me", sessOptCannedSignonTime, sessOptChatRoomCookie("the-chat-cookie")),
- mockParams: mockParams{
- chatMessageRelayerParams: chatMessageRelayerParams{
- chatRelayToAllExceptParams: chatRelayToAllExceptParams{
- {
- screenName: state.NewIdentScreenName("me"),
- message: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.Chat,
- SubGroup: wire.ChatUsersLeft,
- },
- Body: wire.SNAC_0x0E_0x04_ChatUsersLeft{
- Users: []wire.TLVUserInfo{
- newTestInstance("me", sessOptCannedSignonTime, sessOptChatRoomCookie("the-chat-cookie")).Session().TLVUserInfo(),
- },
- },
- },
- },
- },
- },
- sessionRegistryParams: sessionRegistryParams{
- removeSessionParams: removeSessionParams{
- {
- screenName: state.NewIdentScreenName("me"),
- },
- },
- },
- },
- },
- {
- name: "user signs out of chat room, room is not empty after user leaves",
- instance: newTestInstance("me", sessOptCannedSignonTime, sessOptChatRoomCookie("the-chat-cookie")),
- mockParams: mockParams{
- chatMessageRelayerParams: chatMessageRelayerParams{
- chatRelayToAllExceptParams: chatRelayToAllExceptParams{
- {
- screenName: state.NewIdentScreenName("me"),
- message: wire.SNACMessage{
- Frame: wire.SNACFrame{
- FoodGroup: wire.Chat,
- SubGroup: wire.ChatUsersLeft,
- },
- Body: wire.SNAC_0x0E_0x04_ChatUsersLeft{
- Users: []wire.TLVUserInfo{
- newTestInstance("me", sessOptCannedSignonTime, sessOptChatRoomCookie("the-chat-cookie")).Session().TLVUserInfo(),
- },
- },
- },
- },
- },
- },
- sessionRegistryParams: sessionRegistryParams{
- removeSessionParams: removeSessionParams{
- {
- screenName: state.NewIdentScreenName("me"),
- },
- },
- },
- },
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- chatMessageRelayer := newMockChatMessageRelayer(t)
- for _, params := range tt.mockParams.chatRelayToAllExceptParams {
- chatMessageRelayer.EXPECT().
- RelayToAllExcept(matchContext(), tt.instance.ChatRoomCookie(), params.screenName, params.message)
- }
- sessionManager := newMockChatSessionRegistry(t)
- for _, params := range tt.mockParams.removeSessionParams {
- sessionManager.EXPECT().
- RemoveSession(matchUserSession(params.screenName))
- }
- svc := NewAuthService(config.Config{}, nil, nil, sessionManager, nil, nil, chatMessageRelayer, nil, nil, nil, wire.DefaultRateLimitClasses(), nil, slog.Default())
- svc.SignoutChat(context.Background(), tt.instance.Session())
- })
- }
- }
- func TestAuthService_Signout(t *testing.T) {
- tests := []struct {
- // name is the unit test name
- name string
- // instance is the session of the user signing out
- instance *state.SessionInstance
- // wantErr is the error we expect from the method
- wantErr error
- // mockParams is the list of params sent to mocks that satisfy this
- // method's dependencies
- mockParams mockParams
- }{
- {
- name: "user signs out of chat room, room is empty after user leaves",
- instance: newTestInstance("me", sessOptCannedSignonTime),
- mockParams: mockParams{
- buddyBroadcasterParams: buddyBroadcasterParams{
- broadcastBuddyDepartedParams: broadcastBuddyDepartedParams{
- {
- screenName: state.NewIdentScreenName("me"),
- },
- },
- },
- sessionRegistryParams: sessionRegistryParams{
- removeSessionParams: removeSessionParams{
- {
- screenName: state.NewIdentScreenName("me"),
- },
- },
- },
- },
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- sessionManager := newMockSessionRegistry(t)
- for _, params := range tt.mockParams.removeSessionParams {
- sessionManager.EXPECT().RemoveSession(matchUserSession(params.screenName))
- }
- svc := NewAuthService(config.Config{}, sessionManager, nil, nil, nil, nil, nil, nil, nil, nil, wire.DefaultRateLimitClasses(), nil, slog.Default())
- svc.Signout(context.Background(), tt.instance.Session())
- })
- }
- }
- func TestBuildLinkedAccountsXML(t *testing.T) {
- cases := []struct {
- name string
- screenName state.IdentScreenName
- linkedNames []state.IdentScreenName
- wantXML string
- wantErr bool
- }{
- {
- name: "primary account with no linked accounts",
- screenName: state.NewIdentScreenName("PrimaryUser"),
- linkedNames: []state.IdentScreenName{},
- wantXML: `<SET SETID="1"><RESREC TYPE="PRIMARY-ACCOUNT" ID="1"><n>primaryuser</n></RESREC></SET>`,
- },
- {
- name: "primary account with one linked account",
- screenName: state.NewIdentScreenName("PrimaryUser"),
- linkedNames: []state.IdentScreenName{
- state.NewIdentScreenName("LinkedUser1"),
- },
- 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>`,
- },
- {
- name: "primary account with multiple linked accounts",
- screenName: state.NewIdentScreenName("PrimaryUser"),
- linkedNames: []state.IdentScreenName{
- state.NewIdentScreenName("LinkedUser1"),
- state.NewIdentScreenName("LinkedUser2"),
- state.NewIdentScreenName("LinkedUser3"),
- },
- 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>`,
- },
- }
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- got, err := buildLinkedAccountsXML(tc.screenName, tc.linkedNames)
- if tc.wantErr {
- assert.Error(t, err)
- return
- }
- assert.NoError(t, err)
- assert.Equal(t, tc.wantXML, got)
- })
- }
- }
- func TestAuthService_addLinkedAccountsTLV(t *testing.T) {
- cases := []struct {
- name string
- screenName state.DisplayScreenName
- // feedbagItems is what feedbagManager.Feedbag returns
- feedbagItems []wire.FeedbagItem
- feedbagErr error
- // wantTLVCount is the expected number of TLVs after the call
- wantTLVCount int
- wantErr bool
- }{
- {
- name: "no linked accounts, TLV list unchanged",
- screenName: "PrimaryUser",
- feedbagItems: nil,
- wantTLVCount: 0,
- },
- {
- name: "one linked account, TLV appended",
- screenName: "PrimaryUser",
- feedbagItems: []wire.FeedbagItem{
- {ClassID: wire.FeedbagClassIdAlInfo, Name: "linkeduser1"},
- },
- wantTLVCount: 1,
- },
- {
- name: "multiple linked accounts, single TLV appended",
- screenName: "PrimaryUser",
- feedbagItems: []wire.FeedbagItem{
- {ClassID: wire.FeedbagClassIdAlInfo, Name: "linkeduser1"},
- {ClassID: wire.FeedbagClassIdAlInfo, Name: "linkeduser2"},
- },
- wantTLVCount: 1,
- },
- {
- name: "feedbagManager returns error, error propagated",
- screenName: "PrimaryUser",
- feedbagErr: io.EOF,
- wantErr: true,
- },
- }
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- feedbagManager := newMockFeedbagManager(t)
- feedbagManager.EXPECT().
- Feedbag(matchContext(), state.NewIdentScreenName(string(tc.screenName))).
- Return(tc.feedbagItems, tc.feedbagErr)
- svc := AuthService{feedbagManager: feedbagManager}
- tlvs := wire.TLVList{}
- err := svc.addLinkedAccountsTLV(context.Background(), tc.screenName, &tlvs)
- if tc.wantErr {
- assert.Error(t, err)
- return
- }
- assert.NoError(t, err)
- assert.Len(t, tlvs, tc.wantTLVCount)
- if tc.wantTLVCount > 0 {
- assert.Equal(t, wire.OServiceTLVTagsLinkedAccounts, tlvs[0].Tag)
- }
- })
- }
- }
|