mirror of
https://github.com/amigan/aim-oscar-server.git
synced 2025-01-31 04:52:37 -05:00
change to User.ScreenName, check user verification
This commit is contained in:
parent
4f345b30da
commit
93bdc4fcbd
13 changed files with 104 additions and 60 deletions
|
@ -2,12 +2,12 @@ package aimerror
|
|||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
func FetchingUser(err error, username string) error {
|
||||
return errors.Wrapf(err, "could not fetch user with username %s", username)
|
||||
func FetchingUser(err error, screen_name string) error {
|
||||
return errors.Wrapf(err, "could not fetch user with screen_name %s", screen_name)
|
||||
}
|
||||
|
||||
func UserNotFound(username string) error {
|
||||
return errors.Errorf("no user with UIN %s", username)
|
||||
func UserNotFound(screen_name string) error {
|
||||
return errors.Errorf("no user with UIN %s", screen_name)
|
||||
}
|
||||
|
||||
var NoUserInSession = errors.New("no user in session")
|
||||
|
|
10
main.go
10
main.go
|
@ -114,6 +114,8 @@ func main() {
|
|||
pgdriver.WithWriteTimeout(5*time.Second),
|
||||
)
|
||||
|
||||
log.Printf("DB URL: %s", DB_URL)
|
||||
|
||||
// Set up the DB
|
||||
sqldb := sql.OpenDB(pgconn)
|
||||
db := bun.NewDB(sqldb, pgdialect.New())
|
||||
|
@ -124,7 +126,7 @@ func main() {
|
|||
db.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true)))
|
||||
|
||||
// Register our DB models
|
||||
db.RegisterModel((*models.User)(nil), (*models.Message)(nil), (*models.Buddy)(nil))
|
||||
db.RegisterModel((*models.User)(nil), (*models.Message)(nil), (*models.Buddy)(nil), (*models.EmailVerification)(nil))
|
||||
|
||||
// dev: load in fixtures to test against
|
||||
fixture := dbfixture.New(db, dbfixture.WithRecreateTables())
|
||||
|
@ -169,7 +171,7 @@ func main() {
|
|||
}
|
||||
|
||||
onlineCh <- user
|
||||
sessionManager.RemoveSession(user.Username)
|
||||
sessionManager.RemoveSession(user.ScreenName)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,10 +182,10 @@ func main() {
|
|||
}
|
||||
|
||||
if user := models.UserFromContext(ctx); user != nil {
|
||||
fmt.Printf("%s (%v) ->\n%+v\n", user.Username, session.RemoteAddr(), flap)
|
||||
fmt.Printf("%s (%v) ->\n%+v\n", user.ScreenName, session.RemoteAddr(), flap)
|
||||
user.LastActivityAt = time.Now()
|
||||
ctx = models.NewContextWithUser(ctx, user)
|
||||
sessionManager.SetSession(user.Username, session)
|
||||
sessionManager.SetSession(user.ScreenName, session)
|
||||
} else {
|
||||
fmt.Printf("%v ->\n%+v\n", session.RemoteAddr(), flap)
|
||||
}
|
||||
|
|
18
models/EmailVerification.go
Normal file
18
models/EmailVerification.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
// TODO: move this out of here and into API server
|
||||
type EmailVerification struct {
|
||||
bun.BaseModel `bun:"table:email_verification"`
|
||||
UserUIN int64 `bun:",pk,notnull,unique"`
|
||||
User *User `bun:"rel:has-one,join:user_uin=uin"`
|
||||
Token string `bun:",notnull"`
|
||||
Used bool `bun:",notnull,default:false"`
|
||||
CreatedAt time.Time `bun:",nullzero,notnull,default:current_timestamp"`
|
||||
UpdatedAt time.Time `bun:",nullzero,notnull,default:current_timestamp"`
|
||||
}
|
|
@ -25,12 +25,14 @@ type User struct {
|
|||
bun.BaseModel `bun:"table:users"`
|
||||
UIN int64 `bun:",pk,autoincrement"`
|
||||
Email string `bun:",unique"`
|
||||
Username string `bun:",unique"`
|
||||
ScreenName string `bun:",unique"`
|
||||
Password string
|
||||
Cipher string
|
||||
CreatedAt time.Time `bun:",nullzero,notnull,default:current_timestamp"`
|
||||
UpdatedAt time.Time `bun:",nullzero,notnull,default:current_timestamp"`
|
||||
CreatedAt time.Time `bun:",nullzero,notnull,default:current_timestamp"`
|
||||
UpdatedAt time.Time `bun:",nullzero,notnull,default:current_timestamp"`
|
||||
DeletedAt *time.Time `bun:",nullzero"`
|
||||
Status UserStatus
|
||||
Verified bool `bun:",notnull,default:false"`
|
||||
Profile string
|
||||
ProfileEncoding string
|
||||
AwayMessage string
|
||||
|
@ -48,9 +50,9 @@ var (
|
|||
currentUser = userKey("user")
|
||||
)
|
||||
|
||||
func UserByUsername(ctx context.Context, db *bun.DB, username string) (*User, error) {
|
||||
func UserByScreenName(ctx context.Context, db *bun.DB, screen_name string) (*User, error) {
|
||||
user := new(User)
|
||||
if err := db.NewSelect().Model(user).Where("username = ?", username).Scan(ctx, user); err != nil {
|
||||
if err := db.NewSelect().Model(user).Where("screen_name = ?", screen_name).Scan(ctx, user); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
- model: User
|
||||
rows:
|
||||
- uin: 1
|
||||
username: toof
|
||||
- uin: 10000
|
||||
screen_name: toof
|
||||
password: bar
|
||||
email: toof@example.com
|
||||
- uin: 2
|
||||
username: artem
|
||||
verified: true
|
||||
- uin: 10001
|
||||
screen_name: artem
|
||||
password: bar
|
||||
email: artem@example.com
|
||||
verified: false
|
||||
- model: Message
|
||||
rows: []
|
||||
- model: Buddy
|
||||
rows: []
|
||||
- model: EmailVerification
|
||||
rows:
|
||||
- user_uin: 2
|
||||
token: foobar
|
||||
|
|
|
@ -25,9 +25,9 @@ func OnlineNotification(sm *SessionManager) (chan *models.User, routineFn) {
|
|||
}
|
||||
|
||||
if user.Status == models.UserStatusOnline {
|
||||
log.Printf("%s is now online", user.Username)
|
||||
log.Printf("%s is now online", user.ScreenName)
|
||||
} else if user.Status == models.UserStatusAway {
|
||||
log.Printf("%s is now away", user.Username)
|
||||
log.Printf("%s is now away", user.ScreenName)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
@ -44,11 +44,11 @@ func OnlineNotification(sm *SessionManager) (chan *models.User, routineFn) {
|
|||
if buddy.Source.Status == models.UserStatusAway || buddy.Source.Status == models.UserStatusDnd {
|
||||
continue
|
||||
}
|
||||
log.Printf("telling %s that %s has a new status: %d!", buddy.Source.Username, user.Username, user.Status)
|
||||
log.Printf("telling %s that %s has a new status: %d!", buddy.Source.ScreenName, user.ScreenName, user.Status)
|
||||
|
||||
if s := sm.GetSession(buddy.Source.Username); s != nil {
|
||||
if s := sm.GetSession(buddy.Source.ScreenName); s != nil {
|
||||
onlineSnac := oscar.NewSNAC(3, 0xb)
|
||||
onlineSnac.Data.WriteLPString(user.Username)
|
||||
onlineSnac.Data.WriteLPString(user.ScreenName)
|
||||
onlineSnac.Data.WriteUint16(0) // TODO: user warning level
|
||||
|
||||
tlvs := []*oscar.TLV{
|
||||
|
@ -68,7 +68,7 @@ func OnlineNotification(sm *SessionManager) (chan *models.User, routineFn) {
|
|||
onlineFlap := oscar.NewFLAP(2)
|
||||
onlineFlap.Data.WriteBinary(onlineSnac)
|
||||
if err := s.Send(onlineFlap); err != nil {
|
||||
log.Printf("could not tell %s that %s is online", buddy.Source.Username, user.Username)
|
||||
log.Printf("could not tell %s that %s is online", buddy.Source.ScreenName, user.ScreenName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"version": "1.0.0",
|
||||
"license": "GPLv4",
|
||||
"scripts": {
|
||||
"dev": "nodemon --watch ./ -e go --ignore '*_test.go' --delay 200ms --exec 'go build && ./aim-oscar || exit 1' --signal SIGTERM",
|
||||
"dev": "nodemon --watch './' -e go,yml --ignore '*_test.go' --delay 200ms --exec 'go build && ./aim-oscar || exit 1' --signal SIGTERM",
|
||||
"start": "go build && ./aim-oscar"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -57,7 +57,7 @@ func (g *GenericServiceControls) HandleSNAC(ctx context.Context, db *bun.DB, sna
|
|||
}
|
||||
|
||||
onlineSnac := oscar.NewSNAC(3, 0xb)
|
||||
onlineSnac.Data.WriteLPString(buddy.Source.Username)
|
||||
onlineSnac.Data.WriteLPString(buddy.Source.ScreenName)
|
||||
onlineSnac.Data.WriteUint16(0) // TODO: user warning level
|
||||
|
||||
tlvs := []*oscar.TLV{
|
||||
|
@ -77,7 +77,7 @@ func (g *GenericServiceControls) HandleSNAC(ctx context.Context, db *bun.DB, sna
|
|||
onlineFlap := oscar.NewFLAP(2)
|
||||
onlineFlap.Data.WriteBinary(onlineSnac)
|
||||
if err := session.Send(onlineFlap); err != nil {
|
||||
return ctx, errors.Wrapf(err, "could not tell %s that %s is online", buddy.Source.Username, buddy.Target.Username)
|
||||
return ctx, errors.Wrapf(err, "could not tell %s that %s is online", buddy.Source.ScreenName, buddy.Target.ScreenName)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,8 +132,8 @@ func (g *GenericServiceControls) HandleSNAC(ctx context.Context, db *bun.DB, sna
|
|||
}
|
||||
|
||||
onlineSnac := oscar.NewSNAC(1, 0xf)
|
||||
onlineSnac.Data.WriteUint8(uint8(len(user.Username)))
|
||||
onlineSnac.Data.WriteString(user.Username)
|
||||
onlineSnac.Data.WriteUint8(uint8(len(user.ScreenName)))
|
||||
onlineSnac.Data.WriteString(user.ScreenName)
|
||||
onlineSnac.Data.WriteUint16(0) // warning level
|
||||
|
||||
user.Status = models.UserStatusOnline
|
||||
|
|
|
@ -86,18 +86,18 @@ func (s *LocationServices) HandleSNAC(ctx context.Context, db *bun.DB, snac *osc
|
|||
return ctx, errors.Wrap(err, "missing request type")
|
||||
}
|
||||
|
||||
requestedUsername, err := snac.Data.ReadLPString()
|
||||
requestedScreenName, err := snac.Data.ReadLPString()
|
||||
if err != nil {
|
||||
return ctx, errors.Wrap(err, "missing requested username")
|
||||
return ctx, errors.Wrap(err, "missing requested screen_name")
|
||||
}
|
||||
|
||||
requestedUser, err := models.UserByUsername(ctx, db, requestedUsername)
|
||||
requestedUser, err := models.UserByScreenName(ctx, db, requestedScreenName)
|
||||
if err != nil {
|
||||
return ctx, aimerror.FetchingUser(err, requestedUsername)
|
||||
return ctx, aimerror.FetchingUser(err, requestedScreenName)
|
||||
}
|
||||
|
||||
respSnac := oscar.NewSNAC(2, 6)
|
||||
respSnac.Data.WriteLPString(requestedUser.Username)
|
||||
respSnac.Data.WriteLPString(requestedUser.ScreenName)
|
||||
respSnac.Data.WriteUint16(0) // TODO: warning level
|
||||
|
||||
tlvs := []*oscar.TLV{
|
||||
|
|
|
@ -43,7 +43,7 @@ func (b *BuddyListManagement) HandleSNAC(ctx context.Context, db *bun.DB, snac *
|
|||
return ctx, errors.Wrap(err, "expecting more buddies in list")
|
||||
}
|
||||
|
||||
buddy, err := models.UserByUsername(ctx, db, buddyScreename)
|
||||
buddy, err := models.UserByScreenName(ctx, db, buddyScreename)
|
||||
if err != nil {
|
||||
return ctx, errors.Wrap(err, "error looking for User")
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ func (b *BuddyListManagement) HandleSNAC(ctx context.Context, db *bun.DB, snac *
|
|||
return ctx, err
|
||||
}
|
||||
|
||||
log.Printf("%s added buddy %s to buddy list", user.Username, buddyScreename)
|
||||
log.Printf("%s added buddy %s to buddy list", user.ScreenName, buddyScreename)
|
||||
}
|
||||
|
||||
return ctx, nil
|
||||
|
@ -79,7 +79,7 @@ func (b *BuddyListManagement) HandleSNAC(ctx context.Context, db *bun.DB, snac *
|
|||
return ctx, errors.Wrap(err, "expecting more buddies in list")
|
||||
}
|
||||
|
||||
buddy, err := models.UserByUsername(ctx, db, buddyScreename)
|
||||
buddy, err := models.UserByScreenName(ctx, db, buddyScreename)
|
||||
if err != nil {
|
||||
return ctx, errors.Wrap(err, "error looking for User")
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ func (b *BuddyListManagement) HandleSNAC(ctx context.Context, db *bun.DB, snac *
|
|||
return ctx, err
|
||||
}
|
||||
|
||||
log.Printf("%s removed buddy %s from buddy list", user.Username, buddyScreename)
|
||||
log.Printf("%s removed buddy %s from buddy list", user.ScreenName, buddyScreename)
|
||||
}
|
||||
|
||||
return ctx, nil
|
||||
|
|
|
@ -181,14 +181,14 @@ func (icbm *ICBM) HandleSNAC(ctx context.Context, db *bun.DB, snac *oscar.SNAC)
|
|||
// TLV 0x6 is the client telling the server to store the message if the recipient is offline
|
||||
saveofflineTLV := oscar.FindTLV(tlvs, 6)
|
||||
if saveofflineTLV != nil {
|
||||
message, err = models.InsertMessage(ctx, db, msgID, user.Username, to, string(messageContents))
|
||||
message, err = models.InsertMessage(ctx, db, msgID, user.ScreenName, to, string(messageContents))
|
||||
if err != nil {
|
||||
return ctx, errors.Wrap(err, "could not insert message")
|
||||
}
|
||||
} else {
|
||||
message = &models.Message{
|
||||
Cookie: msgID,
|
||||
From: user.Username,
|
||||
From: user.ScreenName,
|
||||
To: to,
|
||||
Contents: string(messageContents),
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ func (icbm *ICBM) HandleSNAC(ctx context.Context, db *bun.DB, snac *oscar.SNAC)
|
|||
ackSnac := oscar.NewSNAC(4, 0xc)
|
||||
ackSnac.Data.WriteUint64(msgID)
|
||||
ackSnac.Data.WriteUint16(2)
|
||||
ackSnac.Data.WriteLPString(user.Username)
|
||||
ackSnac.Data.WriteLPString(user.ScreenName)
|
||||
ackFlap := oscar.NewFLAP(2)
|
||||
ackFlap.Data.WriteBinary(ackSnac)
|
||||
return ctx, session.Send(ackFlap)
|
||||
|
|
|
@ -88,19 +88,19 @@ func (a *AuthorizationRegistrationService) HandleSNAC(ctx context.Context, db *b
|
|||
tlvs, err := oscar.UnmarshalTLVs(snac.Data.Bytes())
|
||||
util.PanicIfError(err)
|
||||
|
||||
usernameTLV := oscar.FindTLV(tlvs, 1)
|
||||
if usernameTLV == nil {
|
||||
return ctx, errors.New("missing username TLV")
|
||||
screenNameTLV := oscar.FindTLV(tlvs, 1)
|
||||
if screenNameTLV == nil {
|
||||
return ctx, errors.New("missing screen_name TLV")
|
||||
}
|
||||
|
||||
// Fetch the user
|
||||
user, err := models.UserByUsername(ctx, db, string(usernameTLV.Data))
|
||||
user, err := models.UserByScreenName(ctx, db, string(screenNameTLV.Data))
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
if user == nil {
|
||||
snac := oscar.NewSNAC(0x17, 0x03)
|
||||
snac.Data.WriteBinary(usernameTLV)
|
||||
snac.Data.WriteBinary(screenNameTLV)
|
||||
snac.Data.WriteBinary(oscar.NewTLV(0x08, []byte{0, 4}))
|
||||
resp := oscar.NewFLAP(2)
|
||||
resp.Data.WriteBinary(snac)
|
||||
|
@ -126,21 +126,21 @@ func (a *AuthorizationRegistrationService) HandleSNAC(ctx context.Context, db *b
|
|||
tlvs, err := oscar.UnmarshalTLVs(snac.Data.Bytes())
|
||||
util.PanicIfError(err)
|
||||
|
||||
usernameTLV := oscar.FindTLV(tlvs, 1)
|
||||
if usernameTLV == nil {
|
||||
return ctx, errors.New("missing username TLV 0x1")
|
||||
screenNameTLV := oscar.FindTLV(tlvs, 1)
|
||||
if screenNameTLV == nil {
|
||||
return ctx, errors.New("missing screen_name TLV 0x1")
|
||||
}
|
||||
|
||||
username := string(usernameTLV.Data)
|
||||
screen_name := string(screenNameTLV.Data)
|
||||
ctx := context.Background()
|
||||
user, err := models.UserByUsername(ctx, db, username)
|
||||
user, err := models.UserByScreenName(ctx, db, screen_name)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
snac := oscar.NewSNAC(0x17, 0x03)
|
||||
snac.Data.WriteBinary(usernameTLV)
|
||||
snac.Data.WriteBinary(screenNameTLV)
|
||||
snac.Data.WriteBinary(oscar.NewTLV(0x08, []byte{0, 4}))
|
||||
resp := oscar.NewFLAP(2)
|
||||
resp.Data.WriteBinary(snac)
|
||||
|
@ -162,8 +162,24 @@ func (a *AuthorizationRegistrationService) HandleSNAC(ctx context.Context, db *b
|
|||
if !bytes.Equal(expectedPasswordHash, passwordHashTLV.Data) {
|
||||
// Tell the client this was a bad password
|
||||
badPasswordSnac := oscar.NewSNAC(0x17, 0x03)
|
||||
badPasswordSnac.Data.WriteBinary(usernameTLV)
|
||||
badPasswordSnac.Data.WriteBinary(oscar.NewTLV(0x08, []byte{0, 4}))
|
||||
badPasswordSnac.Data.WriteBinary(screenNameTLV)
|
||||
badPasswordSnac.Data.WriteBinary(oscar.NewTLV(0x08, []byte{0, 4})) // incorrect nick/pass
|
||||
badPasswordFlap := oscar.NewFLAP(2)
|
||||
badPasswordFlap.Data.WriteBinary(badPasswordSnac)
|
||||
session.Send(badPasswordFlap)
|
||||
|
||||
// Tell them to leave
|
||||
discoFlap := oscar.NewFLAP(4)
|
||||
return ctx, session.Send(discoFlap)
|
||||
}
|
||||
|
||||
// Only users that have verified their email can use the service
|
||||
if !user.Verified || user.DeletedAt != nil {
|
||||
// Tell the client this was a bad password
|
||||
badPasswordSnac := oscar.NewSNAC(0x17, 0x03)
|
||||
badPasswordSnac.Data.WriteBinary(screenNameTLV)
|
||||
badPasswordSnac.Data.WriteBinary(oscar.NewTLV(0x08, []byte{0, 7})) // invalid account
|
||||
badPasswordSnac.Data.WriteBinary(oscar.NewTLV(0x04, []byte("http://runningman.network/errors/unverified-account")))
|
||||
badPasswordFlap := oscar.NewFLAP(2)
|
||||
badPasswordFlap.Data.WriteBinary(badPasswordSnac)
|
||||
session.Send(badPasswordFlap)
|
||||
|
@ -175,7 +191,7 @@ func (a *AuthorizationRegistrationService) HandleSNAC(ctx context.Context, db *b
|
|||
|
||||
// Send BOS response + cookie
|
||||
authSnac := oscar.NewSNAC(0x17, 0x3)
|
||||
authSnac.Data.WriteBinary(usernameTLV)
|
||||
authSnac.Data.WriteBinary(screenNameTLV)
|
||||
authSnac.Data.WriteBinary(oscar.NewTLV(0x5, []byte(a.BOSAddress)))
|
||||
|
||||
cookie, err := json.Marshal(AuthorizationCookie{
|
||||
|
|
|
@ -18,15 +18,15 @@ func NewSessionManager() *SessionManager {
|
|||
return sm
|
||||
}
|
||||
|
||||
func (sm *SessionManager) SetSession(username string, session *oscar.Session) {
|
||||
func (sm *SessionManager) SetSession(screen_name string, session *oscar.Session) {
|
||||
sm.mutex.Lock()
|
||||
sm.sessions[username] = session
|
||||
sm.sessions[screen_name] = session
|
||||
sm.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (sm *SessionManager) GetSession(username string) *oscar.Session {
|
||||
func (sm *SessionManager) GetSession(screen_name string) *oscar.Session {
|
||||
sm.mutex.RLock()
|
||||
s, ok := sm.sessions[username]
|
||||
s, ok := sm.sessions[screen_name]
|
||||
sm.mutex.RUnlock()
|
||||
|
||||
if ok {
|
||||
|
@ -35,8 +35,8 @@ func (sm *SessionManager) GetSession(username string) *oscar.Session {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (sm *SessionManager) RemoveSession(username string) {
|
||||
func (sm *SessionManager) RemoveSession(screen_name string) {
|
||||
sm.mutex.Lock()
|
||||
sm.sessions[username] = nil
|
||||
sm.sessions[screen_name] = nil
|
||||
sm.mutex.Unlock()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue