diff --git a/api/api.go b/api/api.go index 683cd4b..d0639cc 100644 --- a/api/api.go +++ b/api/api.go @@ -71,16 +71,9 @@ func initAuth() { // createUserToken create JWT auth token for current login user func createUserToken(user models.User, role models.Role) (string, error) { claims := jwt.MapClaims{ - "name": user.Username, - "email": user.Email, - "role": role.RoleName, - "archive": role.CanArchive, - "invite": role.CanInvite, - "manage": role.CanManage, - "manageuser": role.CanManageUser, - "moderate": role.CanModerate, - "private": role.CanUsePrivate, - "type": "userauth", + "name": user.Username, + "email": user.Email, + "type": "userauth", } unsignedToken := *jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := unsignedToken.SignedString(hmacSampleSecret) diff --git a/api/api_parameters.go b/api/api_parameters.go index 1be9ec2..39ebdd7 100644 --- a/api/api_parameters.go +++ b/api/api_parameters.go @@ -329,3 +329,5 @@ type loginParam struct { // in:body Login loginParameterModel `json:"login"` } + +// <><><><><> <><><><><> <><><><><> <><><><><> // diff --git a/api/avatar_route.go b/api/avatar_route.go index 89ba439..af07bf2 100644 --- a/api/avatar_route.go +++ b/api/avatar_route.go @@ -5,6 +5,7 @@ import ( "net/http" "strconv" + jwt "github.com/dgrijalva/jwt-go" "github.com/pressly/chi" chiRender "github.com/pressly/chi/render" "github.com/titouanfreville/popcubeapi/datastores" @@ -148,38 +149,36 @@ func avatarContext(next http.Handler) http.Handler { func getAllAvatar(w http.ResponseWriter, r *http.Request) { store := datastores.Store() db := dbStore.db - if err := db.DB().Ping(); err == nil { - result := store.Avatar().GetAll(db) - render.JSON(w, 200, result) - } else { + if err := db.DB().Ping(); err != nil { render.JSON(w, error503.StatusCode, error503) + return } + result := store.Avatar().GetAll(db) + render.JSON(w, 200, result) } func getAvatarFromName(w http.ResponseWriter, r *http.Request) { store := datastores.Store() - db := dbStore.db - if err := db.DB().Ping(); err == nil { - name := r.Context().Value(avatarNameKey).(string) - avatar := store.Avatar().GetByName(name, db) - render.JSON(w, 200, avatar) - } else { + if err := db.DB().Ping(); err != nil { render.JSON(w, error503.StatusCode, error503) + return } + name := r.Context().Value(avatarNameKey).(string) + avatar := store.Avatar().GetByName(name, db) + render.JSON(w, 200, avatar) } func getAvatarFromLink(w http.ResponseWriter, r *http.Request) { store := datastores.Store() - db := dbStore.db - if err := db.DB().Ping(); err == nil { - link := r.Context().Value(avatarLinkKey).(string) - avatar := store.Avatar().GetByLink(link, db) - render.JSON(w, 200, avatar) - } else { + if err := db.DB().Ping(); err != nil { render.JSON(w, error503.StatusCode, error503) + return } + link := r.Context().Value(avatarLinkKey).(string) + avatar := store.Avatar().GetByLink(link, db) + render.JSON(w, 200, avatar) } func newAvatar(w http.ResponseWriter, r *http.Request) { @@ -187,25 +186,31 @@ func newAvatar(w http.ResponseWriter, r *http.Request) { Avatar *models.Avatar OmitID interface{} `json:"id,omitempty"` } + token := r.Context().Value(jwtTokenKey).(*jwt.Token) + if !canManageOrganisation(token) { + res := error401 + res.Message = "You don't have the right to manage organisation." + render.JSON(w, error401.StatusCode, error401) + return + } store := datastores.Store() - db := dbStore.db request := r.Body err := chiRender.Bind(request, &data) if err != nil || data.Avatar == nil { render.JSON(w, error422.StatusCode, error422) - } else { - if err := db.DB().Ping(); err == nil { - err := store.Avatar().Save(data.Avatar, db) - if err == nil { - render.JSON(w, 201, data.Avatar) - } else { - render.JSON(w, err.StatusCode, err) - } - } else { - render.JSON(w, error503.StatusCode, error503) - } + return } + if err := db.DB().Ping(); err != nil { + render.JSON(w, error503.StatusCode, error503) + return + } + rerr := store.Avatar().Save(data.Avatar, db) + if rerr != nil { + render.JSON(w, rerr.StatusCode, rerr) + return + } + render.JSON(w, 201, data.Avatar) } func updateAvatar(w http.ResponseWriter, r *http.Request) { @@ -213,48 +218,60 @@ func updateAvatar(w http.ResponseWriter, r *http.Request) { Avatar *models.Avatar OmitID interface{} `json:"id,omitempty"` } + token := r.Context().Value(jwtTokenKey).(*jwt.Token) + if !canManageOrganisation(token) { + res := error401 + res.Message = "You don't have the right to manage organisation." + render.JSON(w, error401.StatusCode, error401) + return + } store := datastores.Store() - db := dbStore.db request := r.Body err := chiRender.Bind(request, &data) avatar := r.Context().Value(oldAvatarKey).(models.Avatar) if err != nil || data.Avatar == nil { render.JSON(w, error422.StatusCode, error422) - } else { - if err := db.DB().Ping(); err == nil { - err := store.Avatar().Update(&avatar, data.Avatar, db) - if err == nil { - render.JSON(w, 200, avatar) - } else { - render.JSON(w, err.StatusCode, err) - } - } else { - render.JSON(w, error503.StatusCode, error503) - } + return + } + if err := db.DB().Ping(); err != nil { + render.JSON(w, error503.StatusCode, error503) + return } + rerr := store.Avatar().Update(&avatar, data.Avatar, db) + if err != nil { + render.JSON(w, rerr.StatusCode, rerr) + return + } + render.JSON(w, 200, avatar) } func deleteAvatar(w http.ResponseWriter, r *http.Request) { + token := r.Context().Value(jwtTokenKey).(*jwt.Token) + if !canManageOrganisation(token) { + res := error401 + res.Message = "You don't have the right to manage organisation." + render.JSON(w, error401.StatusCode, error401) + return + } avatar := r.Context().Value(oldAvatarKey).(models.Avatar) store := datastores.Store() - message := deleteMessageModel{ Object: avatar, } db := dbStore.db - if err := db.DB().Ping(); err == nil { - err := store.Avatar().Delete(&avatar, db) - if err == nil { - message.Success = true - message.Message = "Avatar well removed." - render.JSON(w, 200, message) - } else { - message.Success = false - message.Message = err.Message - render.JSON(w, err.StatusCode, message.Message) - } - } else { - render.JSON(w, 503, error503) + if err := db.DB().Ping(); err != nil { + render.JSON(w, error503.StatusCode, error503) + return + } + err := store.Avatar().Delete(&avatar, db) + if err != nil { + message.Success = false + message.Message = err.Message + render.JSON(w, err.StatusCode, message.Message) + return } + message.Success = true + message.Message = "Avatar well removed." + render.JSON(w, 200, message) } diff --git a/api/channel_route.go b/api/channel_route.go index 202d112..a928196 100644 --- a/api/channel_route.go +++ b/api/channel_route.go @@ -5,6 +5,7 @@ import ( "net/http" "strconv" + jwt "github.com/dgrijalva/jwt-go" "github.com/pressly/chi" chiRender "github.com/pressly/chi/render" "github.com/titouanfreville/popcubeapi/datastores" @@ -150,6 +151,30 @@ func initChannelRoute(router chi.Router) { }) } +func canModerate(currentChannelID uint64, token *jwt.Token) bool { + store := datastores.Store() + db := dbStore.db + userName := token.Claims.(jwt.MapClaims)["name"].(string) + user := store.User().GetByUserName(userName, db) + userRights := store.Role().GetByID(user.IDRole, db) + chanel := store.Channel().GetByID(currentChannelID, db) + member := store.Member().GetChannelMember(&user, &chanel, db) + memberRights := store.Role().GetByID(member.IDRole, db) + return (memberRights != models.Role{} && memberRights.CanManageUser || memberRights == models.Role{} && userRights.CanManageUser) +} + +func canArchive(currentChannelID uint64, token *jwt.Token) bool { + store := datastores.Store() + db := dbStore.db + userName := token.Claims.(jwt.MapClaims)["name"].(string) + user := store.User().GetByUserName(userName, db) + userRights := store.Role().GetByID(user.IDRole, db) + chanel := store.Channel().GetByID(currentChannelID, db) + member := store.Member().GetChannelMember(&user, &chanel, db) + memberRights := store.Role().GetByID(member.IDRole, db) + return (memberRights != models.Role{} && memberRights.CanArchive || memberRights == models.Role{} && userRights.CanArchive) +} + func channelContext(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { channelID, err := strconv.ParseUint(chi.URLParam(r, "channelID"), 10, 64) @@ -168,44 +193,47 @@ func channelContext(next http.Handler) http.Handler { func getAllChannel(w http.ResponseWriter, r *http.Request) { store := datastores.Store() - db := dbStore.db - if err := db.DB().Ping(); err == nil { - result := store.Channel().GetAll(db) - render.JSON(w, 200, result) - } else { + if err := db.DB().Ping(); err != nil { render.JSON(w, error503.StatusCode, error503) + return } + result := store.Channel().GetAll(db) + render.JSON(w, 200, result) + } func getPublicChannel(w http.ResponseWriter, r *http.Request) { store := datastores.Store() - db := dbStore.db - if err := db.DB().Ping(); err == nil { - result := store.Channel().GetPublic(db) - render.JSON(w, 200, result) - } else { + if err := db.DB().Ping(); err != nil { render.JSON(w, error503.StatusCode, error503) + return } + result := store.Channel().GetPublic(db) + render.JSON(w, 200, result) + } func getPrivateChannel(w http.ResponseWriter, r *http.Request) { store := datastores.Store() - db := dbStore.db - if err := db.DB().Ping(); err == nil { - result := store.Channel().GetPrivate(db) - render.JSON(w, 200, result) - } else { + if err := db.DB().Ping(); err != nil { render.JSON(w, error503.StatusCode, error503) + return } + result := store.Channel().GetPrivate(db) + render.JSON(w, 200, result) + } func getChannelFromName(w http.ResponseWriter, r *http.Request) { store := datastores.Store() - db := dbStore.db + if err := db.DB().Ping(); err != nil { + render.JSON(w, error503.StatusCode, error503) + return + } name := r.Context().Value(channelNameKey).(string) channel := store.Channel().GetByName(name, db) render.JSON(w, 200, channel) @@ -213,8 +241,11 @@ func getChannelFromName(w http.ResponseWriter, r *http.Request) { func getChannelFromType(w http.ResponseWriter, r *http.Request) { store := datastores.Store() - db := dbStore.db + if err := db.DB().Ping(); err != nil { + render.JSON(w, error503.StatusCode, error503) + return + } channelType := r.Context().Value(channelTypeKey).(string) channel := store.Channel().GetByType(channelType, db) render.JSON(w, 200, channel) @@ -225,25 +256,31 @@ func newChannel(w http.ResponseWriter, r *http.Request) { Channel *models.Channel OmitID interface{} `json:"id,omitempty"` } + token := r.Context().Value(jwtTokenKey).(*jwt.Token) + if !canManageUser("global", false, "", token) { + res := error401 + res.Message = "You don't have the right to manage user." + render.JSON(w, error401.StatusCode, error401) + return + } store := datastores.Store() - db := dbStore.db request := r.Body err := chiRender.Bind(request, &data) if err != nil || data.Channel == nil { render.JSON(w, error422.StatusCode, error422) - } else { - if err := db.DB().Ping(); err == nil { - err := store.Channel().Save(data.Channel, db) - if err == nil { - render.JSON(w, 201, data.Channel) - } else { - render.JSON(w, err.StatusCode, err) - } - } else { - render.JSON(w, error503.StatusCode, error503) - } + return + } + if err := db.DB().Ping(); err != nil { + render.JSON(w, error503.StatusCode, error503) + return } + rerr := store.Channel().Save(data.Channel, db) + if err != nil { + render.JSON(w, rerr.StatusCode, rerr) + return + } + render.JSON(w, 201, data.Channel) } func updateChannel(w http.ResponseWriter, r *http.Request) { @@ -251,48 +288,60 @@ func updateChannel(w http.ResponseWriter, r *http.Request) { Channel *models.Channel OmitID interface{} `json:"id,omitempty"` } + channel := r.Context().Value(oldChannelKey).(models.Channel) + token := r.Context().Value(jwtTokenKey).(*jwt.Token) + if !canManageUser(channel.ChannelName, false, "", token) { + res := error401 + res.Message = "You don't have the right to manage user." + render.JSON(w, error401.StatusCode, error401) + return + } store := datastores.Store() - db := dbStore.db request := r.Body err := chiRender.Bind(request, &data) - channel := r.Context().Value(oldChannelKey).(models.Channel) if err != nil || data.Channel == nil { render.JSON(w, error422.StatusCode, error422) - } else { - if err := db.DB().Ping(); err == nil { - err := store.Channel().Update(&channel, data.Channel, db) - if err == nil { - render.JSON(w, 200, channel) - } else { - render.JSON(w, err.StatusCode, err) - } - } else { - render.JSON(w, error503.StatusCode, error503) - } + return + } + if err := db.DB().Ping(); err != nil { + render.JSON(w, error503.StatusCode, error503) + return } + rerr := store.Channel().Update(&channel, data.Channel, db) + if err == nil { + render.JSON(w, rerr.StatusCode, rerr) + return + } + render.JSON(w, 200, channel) } func deleteChannel(w http.ResponseWriter, r *http.Request) { channel := r.Context().Value(oldChannelKey).(models.Channel) + token := r.Context().Value(jwtTokenKey).(*jwt.Token) + if !canManageUser(channel.ChannelName, false, "", token) { + res := error401 + res.Message = "You don't have the right to manage user." + render.JSON(w, error401.StatusCode, error401) + return + } store := datastores.Store() - message := deleteMessageModel{ Object: channel, } db := dbStore.db - if err := db.DB().Ping(); err == nil { - err := store.Channel().Delete(&channel, db) - if err == nil { - message.Success = true - message.Message = "Channel well removed." - render.JSON(w, 200, message) - } else { - message.Success = false - message.Message = err.Message - render.JSON(w, err.StatusCode, message.Message) - } - } else { + if err := db.DB().Ping(); err != nil { render.JSON(w, error503.StatusCode, error503) + return + } + err := store.Channel().Delete(&channel, db) + if err == nil { + message.Success = false + message.Message = err.Message + render.JSON(w, err.StatusCode, message.Message) + return } + message.Success = true + message.Message = "Channel well removed." + render.JSON(w, 200, message) } diff --git a/api/emojis_route.go b/api/emojis_route.go index 2fdfd6a..6026680 100644 --- a/api/emojis_route.go +++ b/api/emojis_route.go @@ -5,6 +5,7 @@ import ( "net/http" "strconv" + jwt "github.com/dgrijalva/jwt-go" "github.com/pressly/chi" chiRender "github.com/pressly/chi/render" "github.com/titouanfreville/popcubeapi/datastores" @@ -165,7 +166,6 @@ func emojiContext(next http.Handler) http.Handler { func getAllEmoji(w http.ResponseWriter, r *http.Request) { store := datastores.Store() - db := dbStore.db if err := db.DB().Ping(); err == nil { result := store.Emoji().GetAll(db) @@ -207,8 +207,14 @@ func newEmoji(w http.ResponseWriter, r *http.Request) { Emoji *models.Emoji OmitID interface{} `json:"id,omitempty"` } + token := r.Context().Value(jwtTokenKey).(*jwt.Token) + if !canManageOrganisation(token) { + res := error401 + res.Message = "You don't have the right to manage organisation." + render.JSON(w, error401.StatusCode, error401) + return + } store := datastores.Store() - db := dbStore.db request := r.Body err := chiRender.Bind(request, &data) @@ -233,8 +239,14 @@ func updateEmoji(w http.ResponseWriter, r *http.Request) { Emoji *models.Emoji OmitID interface{} `json:"id,omitempty"` } + token := r.Context().Value(jwtTokenKey).(*jwt.Token) + if !canManageOrganisation(token) { + res := error401 + res.Message = "You don't have the right to manage organisation." + render.JSON(w, error401.StatusCode, error401) + return + } store := datastores.Store() - db := dbStore.db request := r.Body err := chiRender.Bind(request, &data) @@ -256,9 +268,15 @@ func updateEmoji(w http.ResponseWriter, r *http.Request) { } func deleteEmoji(w http.ResponseWriter, r *http.Request) { + token := r.Context().Value(jwtTokenKey).(*jwt.Token) + if !canManageOrganisation(token) { + res := error401 + res.Message = "You don't have the right to manage organisation." + render.JSON(w, error401.StatusCode, error401) + return + } emoji := r.Context().Value(oldEmojiKey).(models.Emoji) store := datastores.Store() - message := deleteMessageModel{ Object: emoji, } diff --git a/api/organisation_route.go b/api/organisation_route.go index 1a389e1..0a9c7f1 100644 --- a/api/organisation_route.go +++ b/api/organisation_route.go @@ -5,6 +5,7 @@ import ( "net/http" "strconv" + jwt "github.com/dgrijalva/jwt-go" "github.com/pressly/chi" chiRender "github.com/pressly/chi/render" "github.com/titouanfreville/popcubeapi/datastores" @@ -82,6 +83,15 @@ func initOrganisationRoute(router chi.Router) { }) } +func canManageOrganisation(token *jwt.Token) bool { + store := datastores.Store() + db := dbStore.db + userName := token.Claims.(jwt.MapClaims)["name"].(string) + user := store.User().GetByUserName(userName, db) + userRights := store.Role().GetByID(user.IDRole, db) + return userRights.CanManage +} + func organisationContext(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, err := strconv.ParseUint(chi.URLParam(r, "organisationID"), 10, 64) @@ -96,7 +106,6 @@ func organisationContext(next http.Handler) http.Handler { func getAllOrganisation(w http.ResponseWriter, r *http.Request) { store := datastores.Store() - db := dbStore.db if err := db.DB().Ping(); err == nil { result := store.Organisation().Get(db) @@ -111,8 +120,14 @@ func newOrganisation(w http.ResponseWriter, r *http.Request) { Organisation *models.Organisation OmitID interface{} `json:"id,omitempty"` } + token := r.Context().Value(jwtTokenKey).(*jwt.Token) + if !canManageOrganisation(token) { + res := error401 + res.Message = "You don't have the right to manage organisation." + render.JSON(w, error401.StatusCode, error401) + return + } store := datastores.Store() - db := dbStore.db request := r.Body err := chiRender.Bind(request, &data) @@ -138,8 +153,14 @@ func updateOrganisation(w http.ResponseWriter, r *http.Request) { OmitID interface{} `json:"id,omitempty"` } store := datastores.Store() - db := dbStore.db + token := r.Context().Value(jwtTokenKey).(*jwt.Token) + if !canManageOrganisation(token) { + res := error401 + res.Message = "You don't have the right to manage organisation." + render.JSON(w, error401.StatusCode, error401) + return + } request := r.Body err := chiRender.Bind(request, &data) organisation := r.Context().Value(oldOrganisationKey).(models.Organisation) diff --git a/api/parameter_route.go b/api/parameter_route.go index 4a25701..116553d 100644 --- a/api/parameter_route.go +++ b/api/parameter_route.go @@ -5,6 +5,7 @@ import ( "net/http" "strconv" + jwt "github.com/dgrijalva/jwt-go" "github.com/pressly/chi" chiRender "github.com/pressly/chi/render" "github.com/titouanfreville/popcubeapi/datastores" @@ -112,8 +113,14 @@ func newParameter(w http.ResponseWriter, r *http.Request) { Parameter *models.Parameter OmitID interface{} `json:"id,omitempty"` } + token := r.Context().Value(jwtTokenKey).(*jwt.Token) + if !canManageOrganisation(token) { + res := error401 + res.Message = "You don't have the right to manage organisation." + render.JSON(w, error401.StatusCode, error401) + return + } store := datastores.Store() - db := dbStore.db request := r.Body err := chiRender.Bind(request, &data) @@ -138,8 +145,14 @@ func updateParameter(w http.ResponseWriter, r *http.Request) { Parameter *models.Parameter OmitID interface{} `json:"id,omitempty"` } + token := r.Context().Value(jwtTokenKey).(*jwt.Token) + if !canManageOrganisation(token) { + res := error401 + res.Message = "You don't have the right to manage organisation." + render.JSON(w, error401.StatusCode, error401) + return + } store := datastores.Store() - db := dbStore.db request := r.Body err := chiRender.Bind(request, &data) diff --git a/api/user_route.go b/api/user_route.go index 6d1a3f0..f28e422 100644 --- a/api/user_route.go +++ b/api/user_route.go @@ -5,8 +5,6 @@ import ( "net/http" "strconv" - "log" - jwt "github.com/dgrijalva/jwt-go" "github.com/pressly/chi" chiRender "github.com/pressly/chi/render" @@ -254,9 +252,7 @@ func canManageUser(place string, self bool, currentUser string, token *jwt.Token return true } if place == "organisation" || place == "global" { - haveGlobalManageRight, ok := token.Claims.(jwt.MapClaims)["canManageUser"].(bool) - log.Print(haveGlobalManageRight) - return (ok && haveGlobalManageRight) || userRights.CanManageUser + return userRights.CanManageUser } chanel := store.Channel().GetByName(place, db) member := store.Member().GetChannelMember(&user, &chanel, db) @@ -264,6 +260,21 @@ func canManageUser(place string, self bool, currentUser string, token *jwt.Token return channelRights.CanManageUser } +func canInviteUser(place string, token *jwt.Token) bool { + store := datastores.Store() + db := dbStore.db + userName := token.Claims.(jwt.MapClaims)["name"].(string) + user := store.User().GetByUserName(userName, db) + userRights := store.Role().GetByID(user.IDRole, db) + if place == "organisation" || place == "global" { + return userRights.CanInvite + } + chanel := store.Channel().GetByName(place, db) + member := store.Member().GetChannelMember(&user, &chanel, db) + channelRights := store.Role().GetByID(member.IDRole, db) + return channelRights.CanInvite +} + func getAllUser(w http.ResponseWriter, r *http.Request) { store := datastores.Store() db := dbStore.db @@ -389,7 +400,6 @@ func newUser(w http.ResponseWriter, r *http.Request) { store := datastores.Store() token := r.Context().Value(jwtTokenKey).(*jwt.Token) if !canManageUser("global", false, "", token) { - res := error401 res.Message = "You don't have the right to manage user." render.JSON(w, error401.StatusCode, error401) @@ -427,7 +437,7 @@ func inviteUser(w http.ResponseWriter, r *http.Request) { response := inviteOk{} request := r.Body token := r.Context().Value(jwtTokenKey).(*jwt.Token) - if !canManageUser("global", false, "", token) { + if !canManageUser("global", false, "", token) || !canInviteUser("global", token) { res := error401 res.Message = "You don't have the right to manage user." render.JSON(w, error401.StatusCode, error401) diff --git a/docker/database.Dockerfile b/docker/database.Dockerfile index 37cb67d..e1aa0d0 100644 --- a/docker/database.Dockerfile +++ b/docker/database.Dockerfile @@ -1,3 +1,4 @@ FROM mariadb:10.1 MAINTAINER Clement LE CORRE -COPY scripts/init.sql /docker-entrypoint-initdb.d/init.sql +COPY scripts/init.sql /docker-entrypoint-initdb.d/init.sql +COPY scripts/init_values.sql /docker-entrypoint-initdb.d/init_values.sql diff --git a/models/allowedWebMails.go b/models/allowedWebMails.go new file mode 100644 index 0000000..52f1eda --- /dev/null +++ b/models/allowedWebMails.go @@ -0,0 +1,39 @@ +package models + +import ( + "github.com/titouanfreville/popcubeapi/utils" +) + +const ( + allowedWebMailsDisplayNameMaxRunes = 64 + allowedWebMailsNameMaxLength = 64 + allowedWebMailsDescriptionMaxRunes = 1024 + allowedWebMailsSubjectMaxRunes = 250 +) + +// AllowedWebMails object +// +// Describe web mails user that can join organisation without being invited. (ex: @popcube.xyz, @supinfo.com, etc.) +// +// swagger:model +type AllowedWebMails struct { + // id of the allowedWebMails + // + // min: 0 + IDAllowedWebMails uint64 `gorm:"primary_key;column:idAllowedWebMails;AUTO_INCREMENT" json:"id,omitempty"` + // Mail provider + // required: true + Provider string `gorm:"column:provider;not null;" json:"provider,omitempty"` + // Domain name of the allowedWebMails + Domain string `gorm:"column:domain" json:"domain,omitempty"` + // Rights that will automatically atributed to user created accound from the webmail + DefaultRights string `gorm:"column:description" json:"description,omitempty"` +} + +// IsValid check validity of object before saving in DB +func (aw AllowedWebMails) IsValid() *utils.AppError { + if aw.Domain == "" { + return utils.NewLocAppError("allowedWebMails.isValid", "domain undefined", nil, "") + } + return nil +} diff --git a/models/allowedWebMails_test.go b/models/allowedWebMails_test.go new file mode 100644 index 0000000..a53aabe --- /dev/null +++ b/models/allowedWebMails_test.go @@ -0,0 +1,34 @@ +package models + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" + u "github.com/titouanfreville/popcubeapi/utils" +) + +func TestAllowedWebMailssModel(t *testing.T) { + Convey("Testing IsValid function", t, func() { + Convey("Given a correct allowedWebMailss. Should be validated", func() { + allowedWebMails := AllowedWebMails{ + Domain: "popcube.xyz", + Provider: "PopCube", + DefaultRights: "Master", + } + So(allowedWebMails.IsValid(), ShouldBeNil) + So(allowedWebMails.IsValid(), ShouldNotResemble, u.NewLocAppError("allowedWebMails.isValid", "domain undefined", nil, "")) + }) + + Convey("Given incorrect allowedWebMailss. Should be refused", func() { + allowedWebMails := AllowedWebMails{ + Provider: "PopCube", + DefaultRights: "Master", + } + + Convey("Too long or empty Name should return name error", func() { + So(allowedWebMails.IsValid(), ShouldNotBeNil) + So(allowedWebMails.IsValid(), ShouldResemble, u.NewLocAppError("allowedWebMails.isValid", "domain undefined", nil, "")) + }) + }) + }) +} diff --git a/models/member.go b/models/member.go index aa52de5..27b1d05 100644 --- a/models/member.go +++ b/models/member.go @@ -11,7 +11,6 @@ import ( // // swagger:model type Member struct { - // IDMember uint64 `gorm:"primary_key;column:idMember;AUTO_INCREMENT" json:"-"` User User `db:"-" json:"-"` // required: true IDUser uint64 `gorm:"column:idUser; not null;" json:"id_user,omitempty"` diff --git a/models/organisation.go b/models/organisation.go index b19b2d1..689039f 100644 --- a/models/organisation.go +++ b/models/organisation.go @@ -34,8 +34,10 @@ type Organisation struct { DockerStack int `gorm:"column:dockerStack;not null;unique" json:"docker_stack,omitempty"` // required: true OrganisationName string `gorm:"column:organisationName;not null;unique" json:"name,omitempty"` - Description string `gorm:"column:description" json:"description,omitempty"` - Avatar string `gorm:"column:avatar" json:"avatar,omitempty"` + // State if organisation is free to join or not. Default is private (false). + Public bool `gorm:"column:public; not null" json:"public"` + Description string `gorm:"column:description" json:"description,omitempty"` + Avatar string `gorm:"column:avatar" json:"avatar,omitempty"` // Domain name of the organisation Domain string `gorm:"column:domain" json:"domain,omitempty"` } diff --git a/models/read.go b/models/read.go new file mode 100644 index 0000000..4ef968b --- /dev/null +++ b/models/read.go @@ -0,0 +1,30 @@ +package models + +import ( + u "github.com/titouanfreville/popcubeapi/utils" +) + +// Read object. +// +// Read state who read a given message in a given channel. +// +// swagger:model +type Read struct { + // required: true + IDUser uint64 `gorm:"column:idUser; not null;" json:"id_user,omitempty"` + // required: true + IDChannel uint64 `gorm:"column:idChannel; not null;" json:"id_channel,omitempty"` + // required: true + IDMessage uint64 `gorm:"column:idMessage; not null;" json:"id_message,omitempty"` +} + +// IsValid check validity of read object +func (read *Read) IsValid() *u.AppError { + // if read.User == (User{}) { + // return u.NewLocAppError("Read.IsValid", "model.read.user.app_error", nil, "") + // } + // if read.Channel == (Channel{}) { + // return u.NewLocAppError("Read.IsValid", "model.read.channel.app_error", nil, "") + // } + return nil +} diff --git a/models/user.go b/models/user.go index 20c4437..97434a5 100644 --- a/models/user.go +++ b/models/user.go @@ -79,12 +79,8 @@ type User struct { // Number of attemps failed while loging in // // required: true - FailedAttempts int `gorm:"column:failedAttempts; not null;" json:"failed_attempts,omitempty"` - // User langage - // - // required: true - Locale string `gorm:"column:locale; not null;" json:"locale,omitempty"` - Role Role `gorm:"ForeignKey:IDRole;" db:"-" json:"-"` + FailedAttempts int `gorm:"column:failedAttempts; not null;" json:"failed_attempts,omitempty"` + Role Role `gorm:"ForeignKey:IDRole;" db:"-" json:"-"` // Role key of user in the organisation // // required: true @@ -156,10 +152,6 @@ func (user *User) PreSave() { user.LastUpdate = GetMillis() user.LastPasswordUpdate = user.LastUpdate - if user.Locale == "" { - user.Locale = DefaultLocale - } - if len(user.Password) > 0 { user.Password = HashPassword(user.Password) } diff --git a/models/userParameter.go b/models/userParameter.go new file mode 100644 index 0000000..bb23ff3 --- /dev/null +++ b/models/userParameter.go @@ -0,0 +1,67 @@ +package models + +import ( + "strconv" + + u "github.com/titouanfreville/popcubeapi/utils" +) + +// Defined in Parameter. +// const ( +// localMaxSize = 5 +// timeZoneMaxSize = 6 +// maxTime = 1440 +// ) + +// UserParameter object +// +// User parameter store all the parameters set an user can use. +// +// swagger:model +type UserParameter struct { + // id of the user who parameter can be applied to + IDUser uint64 `gorm:"column:idUser; not null" json:"id,omitempty"` + // required true + ParameterName string `gorm:"column: parameterName" json:"parameter_name, omitempty"` + // Default langage + // + // required: true + Local string `gorm:"column:local;not null; unique" json:"local,omitempty"` + // Default time zone + // + // required: true + TimeZone string `gorm:"column:timeZone;not null; unique;" json:"time_zone,omitempty"` + // Default start of non notification period + // + // required: true + SleepStart int `gorm:"column:sleepStart;not null;unique" json:"sleep_start,omitempty"` + // Default end of non notification period + // + // required: true + SleepEnd int `gorm:"column:sleepEnd;not null;unique" json:"sleep_end,omitempty"` +} + +// IsValid is used to check validity of UserParameter objects +func (userParameter *UserParameter) IsValid() *u.AppError { + if userParameter.ParameterName == "" { + return u.NewLocAppError("UserParameter.IsValid", "parameter name Undefined", nil, "") + } + + if len(userParameter.Local) > localMaxSize { + return u.NewLocAppError("UserParameter.IsValid", "too long local", nil, "The local :"+userParameter.Local+" can not be manage. Max size for local is 5") + } + + if len(userParameter.TimeZone) > timeZoneMaxSize { + return u.NewLocAppError("UserParameter.IsValid", "too long timeZone", nil, "The TimeZone :"+userParameter.TimeZone+" can not be manage. Max size for local is 4") + } + + if userParameter.SleepStart < 0 || userParameter.SleepStart > maxTime { + return u.NewLocAppError("UserParameter.IsValid", "invalid hour", nil, "The sleep start time: "+strconv.Itoa(userParameter.SleepStart)+"ms is not valable. It has to be between 0 and 1440.") + } + + if userParameter.SleepEnd < 0 || userParameter.SleepEnd > maxTime { + return u.NewLocAppError("UserParameter.IsValid", "invalid hour", nil, "The sleep end time: "+strconv.Itoa(userParameter.SleepEnd)+"ms is not valable. It has to be between 0 and 1440.") + } + + return nil +} diff --git a/models/userParameter_test.go b/models/userParameter_test.go new file mode 100644 index 0000000..9d4ee2e --- /dev/null +++ b/models/userParameter_test.go @@ -0,0 +1,107 @@ +package models + +import ( + "strconv" + "testing" + + . "github.com/smartystreets/goconvey/convey" + u "github.com/titouanfreville/popcubeapi/utils" +) + +func TestUserParameterModel(t *testing.T) { + Convey("Testing IsValid function", t, func() { + userParameter := UserParameter{ + IDUser: 1, + ParameterName: "test", + Local: "en_EN", + TimeZone: "UTC+2", + SleepStart: 280, + SleepEnd: 12, + } + nameError := u.NewLocAppError("UserParameter.IsValid", "parameter name Undefined", nil, "") + localError := u.NewLocAppError("UserParameter.IsValid", "too long local", nil, "The local :"+userParameter.Local+" can not be manage. Max size for local is 5") + timeZoneError := u.NewLocAppError("UserParameter.IsValid", "too long timeZone", nil, "The TimeZone :"+userParameter.TimeZone+" can not be manage. Max size for local is 4") + sleepStartError := u.NewLocAppError("UserParameter.IsValid", "invalid hour", nil, "The sleep start time: "+strconv.Itoa(userParameter.SleepStart)+"ms is not valable. It has to be between 0 and 1440.") + sleepEndError := u.NewLocAppError("UserParameter.IsValid", "invalid hour", nil, "The sleep end time: "+strconv.Itoa(userParameter.SleepEnd)+"ms is not valable. It has to be between 0 and 1440.") + Convey("Given a correct userParameter. UserParameter should be validate", func() { + So(userParameter.IsValid(), ShouldBeNil) + So(userParameter.IsValid(), ShouldNotResemble, nameError) + So(userParameter.IsValid(), ShouldNotResemble, localError) + So(userParameter.IsValid(), ShouldNotResemble, timeZoneError) + So(userParameter.IsValid(), ShouldNotResemble, sleepStartError) + So(userParameter.IsValid(), ShouldNotResemble, sleepEndError) + }) + Convey("Given an incorrect userParameter. UserParameter should be refused", func() { + empty := UserParameter{} + userParameter.ParameterName = "" + Convey("Empty userParameter should return first error from is valid error", func() { + So(userParameter.IsValid(), ShouldNotBeNil) + So(userParameter.IsValid(), ShouldResemble, nameError) + So(userParameter.IsValid(), ShouldNotResemble, localError) + So(userParameter.IsValid(), ShouldNotResemble, timeZoneError) + So(userParameter.IsValid(), ShouldNotResemble, sleepStartError) + So(userParameter.IsValid(), ShouldNotResemble, sleepEndError) + }) + Convey("Empty userParameter name should return local error", func() { + So(userParameter.IsValid(), ShouldNotBeNil) + So(userParameter.IsValid(), ShouldResemble, nameError) + So(userParameter.IsValid(), ShouldNotResemble, localError) + So(userParameter.IsValid(), ShouldNotResemble, timeZoneError) + So(userParameter.IsValid(), ShouldNotResemble, sleepStartError) + So(userParameter.IsValid(), ShouldNotResemble, sleepEndError) + }) + userParameter.Local = "en_ENG" + Convey("Given too long local should return Local error", func() { + So(userParameter.IsValid(), ShouldNotBeNil) + So(userParameter.IsValid(), ShouldNotResemble, nameError) + So(userParameter.IsValid(), ShouldResemble, localError) + So(userParameter.IsValid(), ShouldNotResemble, timeZoneError) + So(userParameter.IsValid(), ShouldNotResemble, sleepStartError) + So(userParameter.IsValid(), ShouldNotResemble, sleepEndError) + }) + userParameter.Local = "en_EN" + userParameter.TimeZone = "UTF+134" + Convey("Given empty time zone or too long time zone should return Time Zone error", func() { + So(userParameter.IsValid(), ShouldNotBeNil) + So(userParameter.IsValid(), ShouldNotResemble, nameError) + So(userParameter.IsValid(), ShouldNotResemble, localError) + So(userParameter.IsValid(), ShouldResemble, timeZoneError) + So(userParameter.IsValid(), ShouldNotResemble, sleepStartError) + So(userParameter.IsValid(), ShouldNotResemble, sleepEndError) + }) + userParameter.TimeZone = "UTF+12" + userParameter.SleepEnd = -1 + Convey("Given negative or too big Sleep timers should return sleep error", func() { + So(userParameter.IsValid(), ShouldNotBeNil) + So(userParameter.IsValid(), ShouldNotResemble, nameError) + So(userParameter.IsValid(), ShouldNotResemble, localError) + So(userParameter.IsValid(), ShouldNotResemble, timeZoneError) + So(userParameter.IsValid(), ShouldResemble, sleepStartError) + So(userParameter.IsValid(), ShouldNotResemble, sleepEndError) + userParameter.SleepEnd = 1441 + So(userParameter.IsValid(), ShouldNotBeNil) + So(userParameter.IsValid(), ShouldNotResemble, nameError) + So(userParameter.IsValid(), ShouldNotResemble, localError) + So(userParameter.IsValid(), ShouldNotResemble, timeZoneError) + So(userParameter.IsValid(), ShouldResemble, sleepStartError) + So(userParameter.IsValid(), ShouldNotResemble, sleepEndError) + userParameter.SleepEnd = 10 + userParameter.SleepStart = -10 + So(userParameter.IsValid(), ShouldNotBeNil) + So(userParameter.IsValid(), ShouldNotResemble, nameError) + So(userParameter.IsValid(), ShouldNotResemble, localError) + So(userParameter.IsValid(), ShouldNotResemble, timeZoneError) + So(userParameter.IsValid(), ShouldNotResemble, sleepStartError) + So(userParameter.IsValid(), ShouldResemble, sleepEndError) + userParameter.SleepStart = 2000 + So(userParameter.IsValid(), ShouldResemble, u.NewLocAppError("UserParameter.IsValid", "model.userParameter.is_valid.userParameter_sleep_start.app_error", nil, "")) + So(userParameter.IsValid(), ShouldNotBeNil) + So(userParameter.IsValid(), ShouldNotResemble, nameError) + So(userParameter.IsValid(), ShouldNotResemble, localError) + So(userParameter.IsValid(), ShouldNotResemble, timeZoneError) + So(userParameter.IsValid(), ShouldNotResemble, sleepStartError) + So(userParameter.IsValid(), ShouldResemble, sleepEndError) + }) + }) + }) +} diff --git a/models/user_test.go b/models/user_test.go index bfeeae7..bccbc0b 100644 --- a/models/user_test.go +++ b/models/user_test.go @@ -7,6 +7,7 @@ import ( "strconv" "strings" "testing" + u "github.com/titouanfreville/popcubeapi/utils" . "github.com/smartystreets/goconvey/convey" @@ -60,7 +61,6 @@ func TestUserModel(t *testing.T) { So(user.LastUpdate, ShouldNotBeNil) So(user.LastUpdate, ShouldBeGreaterThan, 0) So(user.LastUpdate, ShouldEqual, user.LastPasswordUpdate) - So(user.Locale, ShouldNotBeBlank) So(ComparePassword(user.Password, "test"), ShouldBeTrue) }) @@ -90,7 +90,6 @@ func TestUserModel(t *testing.T) { So(user.LastUpdate, ShouldNotBeNil) So(user.LastUpdate, ShouldBeGreaterThan, 0) So(user.LastUpdate, ShouldEqual, user.LastPasswordUpdate) - So(user.Locale, ShouldNotBeBlank) So(ComparePassword(user.Password, "test"), ShouldBeTrue) }) @@ -111,7 +110,7 @@ func TestUserModel(t *testing.T) { Convey("Given a full user entry", func() { user := User{ WebID: "testID", - LastUpdate: 10, + LastUpdate: 10, Deleted: true, Username: "TesT", Password: "test", @@ -123,7 +122,6 @@ func TestUserModel(t *testing.T) { Role: Owner, LastPasswordUpdate: 20, FailedAttempts: 1, - Locale: "vi", LastActivityAt: 5, } @@ -143,7 +141,6 @@ func TestUserModel(t *testing.T) { So(user.LastPasswordUpdate, ShouldNotEqual, 20) So(user.LastPasswordUpdate, ShouldEqual, user.LastUpdate) So(user.FailedAttempts, ShouldEqual, 1) - So(user.Locale, ShouldEqual, "vi") So(user.LastActivityAt, ShouldEqual, 5) })