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  }