github.com/Ne0nd0g/gophish@v0.7.1-0.20190220040016-11493024a07d/models/group.go (about) 1 package models 2 3 import ( 4 "errors" 5 "fmt" 6 "net/mail" 7 "time" 8 9 log "github.com/gophish/gophish/logger" 10 "github.com/jinzhu/gorm" 11 "github.com/sirupsen/logrus" 12 ) 13 14 // Group contains the fields needed for a user -> group mapping 15 // Groups contain 1..* Targets 16 type Group struct { 17 Id int64 `json:"id"` 18 UserId int64 `json:"-"` 19 Name string `json:"name"` 20 ModifiedDate time.Time `json:"modified_date"` 21 Targets []Target `json:"targets" sql:"-"` 22 } 23 24 // GroupSummaries is a struct representing the overview of Groups. 25 type GroupSummaries struct { 26 Total int64 `json:"total"` 27 Groups []GroupSummary `json:"groups"` 28 } 29 30 // GroupSummary represents a summary of the Group model. The only 31 // difference is that, instead of listing the Targets (which could be expensive 32 // for large groups), it lists the target count. 33 type GroupSummary struct { 34 Id int64 `json:"id"` 35 Name string `json:"name"` 36 ModifiedDate time.Time `json:"modified_date"` 37 NumTargets int64 `json:"num_targets"` 38 } 39 40 // GroupTarget is used for a many-to-many relationship between 1..* Groups and 1..* Targets 41 type GroupTarget struct { 42 GroupId int64 `json:"-"` 43 TargetId int64 `json:"-"` 44 } 45 46 // Target contains the fields needed for individual targets specified by the user 47 // Groups contain 1..* Targets, but 1 Target may belong to 1..* Groups 48 type Target struct { 49 Id int64 `json:"-"` 50 BaseRecipient 51 } 52 53 // BaseRecipient contains the fields for a single recipient. This is the base 54 // struct used in members of groups and campaign results. 55 type BaseRecipient struct { 56 Email string `json:"email"` 57 FirstName string `json:"first_name"` 58 LastName string `json:"last_name"` 59 Position string `json:"position"` 60 } 61 62 // FormatAddress returns the email address to use in the "To" header of the email 63 func (r *BaseRecipient) FormatAddress() string { 64 addr := r.Email 65 if r.FirstName != "" && r.LastName != "" { 66 a := &mail.Address{ 67 Name: fmt.Sprintf("%s %s", r.FirstName, r.LastName), 68 Address: r.Email, 69 } 70 addr = a.String() 71 } 72 return addr 73 } 74 75 // FormatAddress returns the email address to use in the "To" header of the email 76 func (t *Target) FormatAddress() string { 77 addr := t.Email 78 if t.FirstName != "" && t.LastName != "" { 79 a := &mail.Address{ 80 Name: fmt.Sprintf("%s %s", t.FirstName, t.LastName), 81 Address: t.Email, 82 } 83 addr = a.String() 84 } 85 return addr 86 } 87 88 // ErrEmailNotSpecified is thrown when no email is specified for the Target 89 var ErrEmailNotSpecified = errors.New("No email address specified") 90 91 // ErrGroupNameNotSpecified is thrown when a group name is not specified 92 var ErrGroupNameNotSpecified = errors.New("Group name not specified") 93 94 // ErrNoTargetsSpecified is thrown when no targets are specified by the user 95 var ErrNoTargetsSpecified = errors.New("No targets specified") 96 97 // Validate performs validation on a group given by the user 98 func (g *Group) Validate() error { 99 switch { 100 case g.Name == "": 101 return ErrGroupNameNotSpecified 102 case len(g.Targets) == 0: 103 return ErrNoTargetsSpecified 104 } 105 return nil 106 } 107 108 // GetGroups returns the groups owned by the given user. 109 func GetGroups(uid int64) ([]Group, error) { 110 gs := []Group{} 111 err := db.Where("user_id=?", uid).Find(&gs).Error 112 if err != nil { 113 log.Error(err) 114 return gs, err 115 } 116 for i := range gs { 117 gs[i].Targets, err = GetTargets(gs[i].Id) 118 if err != nil { 119 log.Error(err) 120 } 121 } 122 return gs, nil 123 } 124 125 // GetGroupSummaries returns the summaries for the groups 126 // created by the given uid. 127 func GetGroupSummaries(uid int64) (GroupSummaries, error) { 128 gs := GroupSummaries{} 129 query := db.Table("groups").Where("user_id=?", uid) 130 err := query.Select("id, name, modified_date").Scan(&gs.Groups).Error 131 if err != nil { 132 log.Error(err) 133 return gs, err 134 } 135 for i := range gs.Groups { 136 query = db.Table("group_targets").Where("group_id=?", gs.Groups[i].Id) 137 err = query.Count(&gs.Groups[i].NumTargets).Error 138 if err != nil { 139 return gs, err 140 } 141 } 142 gs.Total = int64(len(gs.Groups)) 143 return gs, nil 144 } 145 146 // GetGroup returns the group, if it exists, specified by the given id and user_id. 147 func GetGroup(id int64, uid int64) (Group, error) { 148 g := Group{} 149 err := db.Where("user_id=? and id=?", uid, id).Find(&g).Error 150 if err != nil { 151 log.Error(err) 152 return g, err 153 } 154 g.Targets, err = GetTargets(g.Id) 155 if err != nil { 156 log.Error(err) 157 } 158 return g, nil 159 } 160 161 // GetGroupSummary returns the summary for the requested group 162 func GetGroupSummary(id int64, uid int64) (GroupSummary, error) { 163 g := GroupSummary{} 164 query := db.Table("groups").Where("user_id=? and id=?", uid, id) 165 err := query.Select("id, name, modified_date").Scan(&g).Error 166 if err != nil { 167 log.Error(err) 168 return g, err 169 } 170 query = db.Table("group_targets").Where("group_id=?", id) 171 err = query.Count(&g.NumTargets).Error 172 if err != nil { 173 return g, err 174 } 175 return g, nil 176 } 177 178 // GetGroupByName returns the group, if it exists, specified by the given name and user_id. 179 func GetGroupByName(n string, uid int64) (Group, error) { 180 g := Group{} 181 err := db.Where("user_id=? and name=?", uid, n).Find(&g).Error 182 if err != nil { 183 log.Error(err) 184 return g, err 185 } 186 g.Targets, err = GetTargets(g.Id) 187 if err != nil { 188 log.Error(err) 189 } 190 return g, err 191 } 192 193 // PostGroup creates a new group in the database. 194 func PostGroup(g *Group) error { 195 if err := g.Validate(); err != nil { 196 return err 197 } 198 // Insert the group into the DB 199 err := db.Save(g).Error 200 if err != nil { 201 log.Error(err) 202 return err 203 } 204 for _, t := range g.Targets { 205 insertTargetIntoGroup(t, g.Id) 206 } 207 return nil 208 } 209 210 // PutGroup updates the given group if found in the database. 211 func PutGroup(g *Group) error { 212 if err := g.Validate(); err != nil { 213 return err 214 } 215 // Fetch group's existing targets from database. 216 ts := []Target{} 217 ts, err := GetTargets(g.Id) 218 if err != nil { 219 log.WithFields(logrus.Fields{ 220 "group_id": g.Id, 221 }).Error("Error getting targets from group") 222 return err 223 } 224 // Check existing targets, removing any that are no longer in the group. 225 tExists := false 226 for _, t := range ts { 227 tExists = false 228 // Is the target still in the group? 229 for _, nt := range g.Targets { 230 if t.Email == nt.Email { 231 tExists = true 232 break 233 } 234 } 235 // If the target does not exist in the group any longer, we delete it 236 if !tExists { 237 err := db.Where("group_id=? and target_id=?", g.Id, t.Id).Delete(&GroupTarget{}).Error 238 if err != nil { 239 log.WithFields(logrus.Fields{ 240 "email": t.Email, 241 }).Error("Error deleting email") 242 } 243 } 244 } 245 // Add any targets that are not in the database yet. 246 for _, nt := range g.Targets { 247 // Check and see if the target already exists in the db 248 tExists = false 249 for _, t := range ts { 250 if t.Email == nt.Email { 251 tExists = true 252 nt.Id = t.Id 253 break 254 } 255 } 256 // Add target if not in database, otherwise update target information. 257 if !tExists { 258 insertTargetIntoGroup(nt, g.Id) 259 } else { 260 UpdateTarget(nt) 261 } 262 } 263 err = db.Save(g).Error 264 if err != nil { 265 log.Error(err) 266 return err 267 } 268 return nil 269 } 270 271 // DeleteGroup deletes a given group by group ID and user ID 272 func DeleteGroup(g *Group) error { 273 // Delete all the group_targets entries for this group 274 err := db.Where("group_id=?", g.Id).Delete(&GroupTarget{}).Error 275 if err != nil { 276 log.Error(err) 277 return err 278 } 279 // Delete the group itself 280 err = db.Delete(g).Error 281 if err != nil { 282 log.Error(err) 283 return err 284 } 285 return err 286 } 287 288 func insertTargetIntoGroup(t Target, gid int64) error { 289 if _, err := mail.ParseAddress(t.Email); err != nil { 290 log.WithFields(logrus.Fields{ 291 "email": t.Email, 292 }).Error("Invalid email") 293 return err 294 } 295 trans := db.Begin() 296 err := trans.Where(t).FirstOrCreate(&t).Error 297 if err != nil { 298 log.WithFields(logrus.Fields{ 299 "email": t.Email, 300 }).Error(err) 301 trans.Rollback() 302 return err 303 } 304 err = trans.Where("group_id=? and target_id=?", gid, t.Id).Find(&GroupTarget{}).Error 305 if err == gorm.ErrRecordNotFound { 306 err = trans.Save(&GroupTarget{GroupId: gid, TargetId: t.Id}).Error 307 if err != nil { 308 log.Error(err) 309 trans.Rollback() 310 return err 311 } 312 } 313 if err != nil { 314 log.WithFields(logrus.Fields{ 315 "email": t.Email, 316 }).Error("Error adding many-many mapping") 317 trans.Rollback() 318 return err 319 } 320 err = trans.Commit().Error 321 if err != nil { 322 trans.Rollback() 323 log.Error("Error committing db changes") 324 return err 325 } 326 return nil 327 } 328 329 // UpdateTarget updates the given target information in the database. 330 func UpdateTarget(target Target) error { 331 targetInfo := map[string]interface{}{ 332 "first_name": target.FirstName, 333 "last_name": target.LastName, 334 "position": target.Position, 335 } 336 err := db.Model(&target).Where("id = ?", target.Id).Updates(targetInfo).Error 337 if err != nil { 338 log.WithFields(logrus.Fields{ 339 "email": target.Email, 340 }).Error("Error updating target information") 341 } 342 return err 343 } 344 345 // GetTargets performs a many-to-many select to get all the Targets for a Group 346 func GetTargets(gid int64) ([]Target, error) { 347 ts := []Target{} 348 err := db.Table("targets").Select("targets.id, targets.email, targets.first_name, targets.last_name, targets.position").Joins("left join group_targets gt ON targets.id = gt.target_id").Where("gt.group_id=?", gid).Scan(&ts).Error 349 return ts, err 350 }