🎣 Open-Source Phishing Toolkit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

364 lines
9.2 KiB

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