auth

Paddy 2015-07-15 Parent:73e12d5a1124

178:0a2c3d677161 Go to Latest

auth/scope_postgres.go

Update to use a generic event emitter. Rather can creating a purpose-built event emitter for each and every event we need to emit (I'm looking at you, login verification event) which is _downright silly_, we're now using a generic event publisher that's based on saying "HEY A MODEL UPDATED". This means we need to change all our setup code in authd to use events.NewNSQPublisher or events.NewStdoutPublisher instead of our homegrown solutions. Which also means updating our config to take an events.Publisher instead of our LoginVerificationNotifier (blergh). Our Context also now uses an events.Publisher instead of a LoginVerificationNotifier. Party all around! We also replaced our SendLoginVerification helper method on Context with a SendModelEvent helper method on Context, which is just a light wrapper around events.PublishModelEvent. Of course, all this means we need to update our email_verification listener to listen to the correct channel (based on the model we want updates about) and filter down to a Created action or our new custom action for "the customer wants their verification resent", which I'm OK making a special case and not generic, because c'mon. But we had a subtle change to all our constants, some of which are unofficial constants now. I'm unsure how I feel about this. We also updated our email_verification listener so that we're unmarshalling to a custom loginEvent, which is just an events.Event that overwrites the Data property to be an auth.Login instance. This is to make sure we don't need to wrangle a map[string]interface{}, which is no fun. I'm also OK with special-casing like this, because it's 1) a tiny amount of code, 2) properly utilising composition, and 3) the only way I can think of to cleanly accomplish what I want. I also added a note about GetLogin's deficient handling of logins, namely that it doesn't recognise admins and return Verification codes to them, which would be a useful property for internal tools to take advantage of. Ah well. I updated the Profile and Login implementations so they're now event.Model instances, mainly by just exporting some strings from them through getters that will let us automatically build an Event from them. This lets us use the PublishModelEvent helper. I updated our CreateProfileHandler to properly mangle the login Verification property, and to fire off the ActionCreated events for the new Login and the new Profile. I updated our GetLoginHandler and UpdateLoginHandler to properly mangle the loginVerification property. God that's annoying. :-/ You'll note I didn't start publishing the events.ActionUpdated or events.ActionDeleted events for Profiles or Logins yet, and didn't bother publishing any events for literally any other type. That's because I'm a lazy piece of crap and will end up publishing them when I absolutely have to. Part of that is because if a channel isn't created/being read for a topic, the messages will just stack up in NSQ, and I don't want that. But mostly I'm lazy. Finally, I got to delete the entire profile_verification.go file, because we're no longer special-casing that. Hooray!

History
paddy@152 1 package auth
paddy@152 2
paddy@152 3 import (
paddy@163 4 "code.secondbit.org/pqarrays.hg"
paddy@163 5 "database/sql/driver"
paddy@153 6 "github.com/lib/pq"
paddy@152 7 "github.com/secondbit/pan"
paddy@152 8 )
paddy@152 9
paddy@152 10 func (s Scope) GetSQLTableName() string {
paddy@152 11 return "scopes"
paddy@152 12 }
paddy@152 13
paddy@163 14 func (s Scopes) Value() (driver.Value, error) {
paddy@163 15 ids := make(pqarrays.StringArray, 0, len(s))
paddy@163 16 for _, scope := range s {
paddy@163 17 ids = append(ids, scope.ID)
paddy@163 18 }
paddy@163 19 return ids.Value()
paddy@163 20 }
paddy@163 21
paddy@163 22 func (s *Scopes) Scan(value interface{}) error {
paddy@163 23 *s = (*s)[:0]
paddy@163 24 var ids pqarrays.StringArray
paddy@163 25 err := ids.Scan(value)
paddy@163 26 if err != nil {
paddy@163 27 return err
paddy@163 28 }
paddy@163 29 for _, id := range ids {
paddy@163 30 *s = append(*s, Scope{ID: id})
paddy@163 31 }
paddy@163 32 return nil
paddy@163 33 }
paddy@163 34
paddy@152 35 func (p *postgres) createScopesSQL(scopes []Scope) *pan.Query {
paddy@152 36 fields, _ := pan.GetFields(scopes[0])
paddy@152 37 query := pan.New(pan.POSTGRES, "INSERT INTO "+pan.GetTableName(scopes[0]))
paddy@152 38 query.Include("(" + pan.QueryList(fields) + ")")
paddy@152 39 query.Include("VALUES")
paddy@153 40 query.FlushExpressions(" ")
paddy@152 41 for _, scope := range scopes {
paddy@152 42 _, values := pan.GetFields(scope)
paddy@152 43 query.Include("("+pan.VariableList(len(values))+")", values...)
paddy@152 44 }
paddy@153 45 return query.FlushExpressions(", ")
paddy@152 46 }
paddy@152 47
paddy@152 48 func (p *postgres) createScopes(scopes []Scope) error {
paddy@152 49 if len(scopes) < 1 {
paddy@152 50 return nil
paddy@152 51 }
paddy@152 52 query := p.createScopesSQL(scopes)
paddy@152 53 _, err := p.db.Exec(query.String(), query.Args...)
paddy@153 54 if e, ok := err.(*pq.Error); ok && e.Constraint == "scopes_pkey" {
paddy@153 55 err = ErrScopeAlreadyExists
paddy@153 56 }
paddy@152 57 return err
paddy@152 58 }
paddy@152 59
paddy@152 60 func (p *postgres) getScopesSQL(ids []string) *pan.Query {
paddy@152 61 var scope Scope
paddy@152 62 intids := make([]interface{}, len(ids))
paddy@152 63 for pos, id := range ids {
paddy@152 64 intids[pos] = id
paddy@152 65 }
paddy@152 66 fields, _ := pan.GetFields(scope)
paddy@152 67 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(scope))
paddy@152 68 query.IncludeWhere()
paddy@152 69 query.Include(pan.GetUnquotedColumn(scope, "ID") + " IN")
paddy@152 70 query.Include("("+pan.VariableList(len(ids))+")", intids...)
paddy@152 71 return query.FlushExpressions(" ")
paddy@152 72 }
paddy@152 73
paddy@152 74 func (p *postgres) getScopes(ids []string) ([]Scope, error) {
paddy@152 75 query := p.getScopesSQL(ids)
paddy@152 76 rows, err := p.db.Query(query.String(), query.Args...)
paddy@152 77 if err != nil {
paddy@152 78 return []Scope{}, err
paddy@152 79 }
paddy@152 80 var scopes []Scope
paddy@152 81 for rows.Next() {
paddy@152 82 var scope Scope
paddy@152 83 err := pan.Unmarshal(rows, &scope)
paddy@152 84 if err != nil {
paddy@152 85 return scopes, err
paddy@152 86 }
paddy@152 87 scopes = append(scopes, scope)
paddy@152 88 }
paddy@152 89 if err = rows.Err(); err != nil {
paddy@152 90 return scopes, err
paddy@152 91 }
paddy@152 92 return scopes, nil
paddy@152 93 }
paddy@152 94
paddy@152 95 func (p *postgres) updateScopeSQL(id string, change ScopeChange) *pan.Query {
paddy@152 96 var scope Scope
paddy@152 97 query := pan.New(pan.POSTGRES, "UPDATE "+pan.GetTableName(scope)+" SET ")
paddy@152 98 query.IncludeIfNotNil(pan.GetUnquotedColumn(scope, "Name")+" = ?", change.Name)
paddy@152 99 query.IncludeIfNotNil(pan.GetUnquotedColumn(scope, "Description")+" = ?", change.Description)
paddy@152 100 query.FlushExpressions(", ")
paddy@152 101 query.IncludeWhere()
paddy@152 102 query.Include(pan.GetUnquotedColumn(scope, "ID")+" = ?", id)
paddy@152 103 return query.FlushExpressions(" ")
paddy@152 104 }
paddy@152 105
paddy@152 106 func (p *postgres) updateScope(id string, change ScopeChange) error {
paddy@152 107 if change.Empty() {
paddy@152 108 return nil
paddy@152 109 }
paddy@152 110 query := p.updateScopeSQL(id, change)
paddy@153 111 res, err := p.db.Exec(query.String(), query.Args...)
paddy@153 112 if err != nil {
paddy@153 113 return err
paddy@153 114 }
paddy@153 115 rows, err := res.RowsAffected()
paddy@153 116 if err != nil {
paddy@153 117 return err
paddy@153 118 }
paddy@153 119 if rows < 1 {
paddy@153 120 return ErrScopeNotFound
paddy@153 121 }
paddy@152 122 return err
paddy@152 123 }
paddy@152 124
paddy@152 125 func (p *postgres) removeScopesSQL(ids []string) *pan.Query {
paddy@152 126 var scope Scope
paddy@152 127 intids := make([]interface{}, len(ids))
paddy@152 128 for pos, id := range ids {
paddy@152 129 intids[pos] = id
paddy@152 130 }
paddy@152 131 query := pan.New(pan.POSTGRES, "DELETE FROM "+pan.GetTableName(scope))
paddy@152 132 query.IncludeWhere()
paddy@152 133 query.Include(pan.GetUnquotedColumn(scope, "ID") + " IN")
paddy@152 134 query.Include("("+pan.VariableList(len(ids))+")", intids...)
paddy@152 135 return query.FlushExpressions(" ")
paddy@152 136 }
paddy@152 137
paddy@152 138 func (p *postgres) removeScopes(ids []string) error {
paddy@152 139 query := p.removeScopesSQL(ids)
paddy@153 140 res, err := p.db.Exec(query.String(), query.Args...)
paddy@152 141 if err != nil {
paddy@152 142 return err
paddy@152 143 }
paddy@153 144 rows, err := res.RowsAffected()
paddy@153 145 if err != nil {
paddy@153 146 return err
paddy@153 147 }
paddy@153 148 if rows < 1 {
paddy@153 149 return ErrScopeNotFound
paddy@153 150 }
paddy@152 151 return nil
paddy@152 152 }
paddy@152 153
paddy@152 154 func (p *postgres) listScopesSQL() *pan.Query {
paddy@152 155 var scope Scope
paddy@152 156 fields, _ := pan.GetFields(scope)
paddy@152 157 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(scope))
paddy@152 158 return query.FlushExpressions(" ")
paddy@152 159 }
paddy@152 160
paddy@152 161 func (p *postgres) listScopes() ([]Scope, error) {
paddy@152 162 query := p.listScopesSQL()
paddy@152 163 rows, err := p.db.Query(query.String(), query.Args...)
paddy@152 164 if err != nil {
paddy@152 165 return []Scope{}, err
paddy@152 166 }
paddy@152 167 var scopes []Scope
paddy@152 168 for rows.Next() {
paddy@152 169 var scope Scope
paddy@152 170 err = pan.Unmarshal(rows, &scope)
paddy@152 171 if err != nil {
paddy@152 172 return scopes, err
paddy@152 173 }
paddy@152 174 scopes = append(scopes, scope)
paddy@152 175 }
paddy@152 176 if err = rows.Err(); err != nil {
paddy@152 177 return scopes, err
paddy@152 178 }
paddy@152 179 return scopes, nil
paddy@152 180 }