diff --git a/dao/Factory.go b/dao/Factory.go index 7277052..26826e5 100644 --- a/dao/Factory.go +++ b/dao/Factory.go @@ -11,6 +11,9 @@ var channelDAO IChannelDAO var sessionDAO ISessionDAO var messageDAO IMessageDAO var notifcationDAO INotificationDAO +var roleDAO IRoleDAO +var roleBindingDAO IRoleBindingDAO +var rightDAO IRightDAO func GetUserDAO() (IUserDAO, *util.ChatError) { if userDAO == nil { @@ -51,6 +54,45 @@ func GetMessageDAO() (IMessageDAO, *util.ChatError) { return messageDAO, nil } +func GetRoleDAO() (IRoleDAO, *util.ChatError) { + if roleDAO == nil { + dao, err := postgres.MakeRoleDAO() + if err != nil { + return roleDAO, err + } + + roleDAO = dao + } + + return roleDAO, nil +} + +func GetRoleBindingDAO() (IRoleBindingDAO, *util.ChatError) { + if roleBindingDAO == nil { + dao, err := postgres.MakeRoleBindingDAO() + if err != nil { + return roleBindingDAO, err + } + + roleBindingDAO = dao + } + + return roleBindingDAO, nil +} + +func GetRightDAO() (IRightDAO, *util.ChatError) { + if rightDAO == nil { + dao, err := postgres.MakeRightDAO() + if err != nil { + return rightDAO, err + } + + rightDAO = dao + } + + return rightDAO, nil +} + func GetSessionDAO() (ISessionDAO, *util.ChatError) { if sessionDAO == nil { dao, err := valkey.MakeSessionDAO() diff --git a/dao/IRightDAO.go b/dao/IRightDAO.go new file mode 100644 index 0000000..5159844 --- /dev/null +++ b/dao/IRightDAO.go @@ -0,0 +1,14 @@ +package dao + +import ( + "git.tek.govt.hu/dowerx/chat/server/model" + "git.tek.govt.hu/dowerx/chat/server/util" +) + +type IRightDAO interface { + Grant(model.Role, model.Channel, model.RightEnum) *util.ChatError + Update(model.Role, model.Channel, model.RightEnum) *util.ChatError + Revoke(model.Role, model.Channel) *util.ChatError + ListRoleRights(model.Role) (map[int]model.RightEnum, *util.ChatError) + ListUserRigths(model.User) (map[int]model.RightEnum, *util.ChatError) +} diff --git a/dao/IRoleBindingDAO.go b/dao/IRoleBindingDAO.go new file mode 100644 index 0000000..bd85662 --- /dev/null +++ b/dao/IRoleBindingDAO.go @@ -0,0 +1,13 @@ +package dao + +import ( + "git.tek.govt.hu/dowerx/chat/server/model" + "git.tek.govt.hu/dowerx/chat/server/util" +) + +type IRoleBindingDAO interface { + Bind(model.Role, model.User) *util.ChatError + Unbind(model.Role, model.User) *util.ChatError + ListRolesOfUser(model.User) ([]model.Role, *util.ChatError) + ListUserWithRole(model.Role) ([]model.User, *util.ChatError) +} diff --git a/dao/IRoleDAO.go b/dao/IRoleDAO.go new file mode 100644 index 0000000..014674a --- /dev/null +++ b/dao/IRoleDAO.go @@ -0,0 +1,14 @@ +package dao + +import ( + "git.tek.govt.hu/dowerx/chat/server/model" + "git.tek.govt.hu/dowerx/chat/server/util" +) + +type IRoleDAO interface { + Create(name string) (int, *util.ChatError) + Read(model.Role) (model.Role, *util.ChatError) + List() ([]model.Role, *util.ChatError) + Update(model.Role) *util.ChatError + Delete(model.Role) *util.ChatError +} diff --git a/dao/postgres/RightDAO.go b/dao/postgres/RightDAO.go new file mode 100644 index 0000000..1e627b4 --- /dev/null +++ b/dao/postgres/RightDAO.go @@ -0,0 +1,146 @@ +package postgres + +import ( + "git.tek.govt.hu/dowerx/chat/server/model" + "git.tek.govt.hu/dowerx/chat/server/util" +) + +type RightDAOPG struct { + pgDAO +} + +func (d RightDAOPG) Grant(role model.Role, channel model.Channel, rights model.RightEnum) *util.ChatError { + if role.ID != 0 { + role.Name = "" + } + + if channel.ID != 0 { + channel.Name = "" + } + + _, err := d.db.Exec( + `insert into "right" ("role_id", "channel_id", "rights") values ( + (select "id" from "role" where "id" = $1 or "name" = $2), + (select "id" from "channel" where "id" = $3 or "name" = $4), + $5 + )`, role.ID, role.Name, channel.ID, channel.Name, rights) + + return util.MakeError(err, util.DATABASE_QUERY_FAULT) +} + +func (d RightDAOPG) Revoke(role model.Role, channel model.Channel) *util.ChatError { + if role.ID != 0 { + role.Name = "" + } + + if channel.ID != 0 { + channel.Name = "" + } + + _, err := d.db.Exec( + `delete from "right" + where + "role_id" = (select "id" from "role" where "id" = $1 or "name" = $2) + and + "channel_id" = (select "id" from "channel" where "id" = $3 or "name" = $4)`, + role.ID, role.Name, channel.ID, channel.Name) + + return util.MakeError(err, util.DATABASE_QUERY_FAULT) +} + +func (d RightDAOPG) Update(role model.Role, channel model.Channel, rights model.RightEnum) *util.ChatError { + if role.ID != 0 { + role.Name = "" + } + + if channel.ID != 0 { + channel.Name = "" + } + + _, err := d.db.Exec( + `update "right" set "rights" = $5 + where + "role_id" = (select "id" from "role" where "id" = $1 or "name" = $2) + and + "channel_id" = (select "id" from "channel" where "id" = $3 or "name" = $4)`, + role.ID, role.Name, channel.ID, channel.Name, rights) + + return util.MakeError(err, util.DATABASE_QUERY_FAULT) +} + +func (d RightDAOPG) ListRoleRights(role model.Role) (map[int]model.RightEnum, *util.ChatError) { + if role.ID != 0 { + role.Name = "" + } + + rows, err := d.db.NamedQuery( + `select + "channel_id", "rights" + from "rights" + where "role_id" = (select "id" from "role" where "id" = :id or "name" = :name)`, + &role) + + if err != nil { + return nil, util.MakeError(err, util.DATABASE_QUERY_FAULT) + } + + result := make(map[int]model.RightEnum) + + var channel_id int + var rights model.RightEnum + for rows.Next() { + err = rows.Scan(&channel_id, &rights) + + if err != nil { + return nil, util.MakeError(err, util.DATABASE_QUERY_FAULT) + } + + result[channel_id] = rights + } + + return result, nil +} + +func (d RightDAOPG) ListUserRigths(user model.User) (map[int]model.RightEnum, *util.ChatError) { + if user.ID != 0 { + user.Username = "" + } + + rows, err := d.db.NamedQuery( + `select + "channel_id", "rights" + from "user_rigths_per_channel" + where "user_id" = (select "id" from "user" where "id" = :id or "username" = :username)`, + &user) + + if err != nil { + return nil, util.MakeError(err, util.DATABASE_QUERY_FAULT) + } + + result := make(map[int]model.RightEnum) + + var channel_id int + var rights model.RightEnum + for rows.Next() { + err = rows.Scan(&channel_id, &rights) + + if err != nil { + return nil, util.MakeError(err, util.DATABASE_QUERY_FAULT) + } + + result[channel_id] = rights + } + + return result, nil +} + +func MakeRightDAO() (RightDAOPG, *util.ChatError) { + dao := RightDAOPG{} + conn, err := getDatabase() + if err != nil { + return dao, err + } + + dao.db = conn + return dao, nil +} diff --git a/dao/postgres/RoleBindingDAO.go b/dao/postgres/RoleBindingDAO.go new file mode 100644 index 0000000..d8ba81a --- /dev/null +++ b/dao/postgres/RoleBindingDAO.go @@ -0,0 +1,131 @@ +package postgres + +import ( + "git.tek.govt.hu/dowerx/chat/server/model" + "git.tek.govt.hu/dowerx/chat/server/util" +) + +type RoleBindingDAOPG struct { + pgDAO +} + +// Bind creates a new RoleBinding. +// +// It uses name only if ID is not set. +func (d RoleBindingDAOPG) Bind(role model.Role, user model.User) *util.ChatError { + if role.ID != 0 { + role.Name = "" + } + + if user.ID != 0 { + user.Username = "" + } + _, err := d.db.Exec( + `insert into "role" ("role_id", "user_id") values ( + (select "id" from "role" where "id" = $1 or "name" = $2), + (select "id" from "user" where "id" = $3 or "username" = $4))`, + role.ID, role.Name, + user.ID, user.Username) + + return util.MakeError(err, util.DATABASE_QUERY_FAULT) +} + +// Unbind deletes a RoleBinding. +// +// It uses name only if ID is not set. +func (d RoleBindingDAOPG) Unbind(role model.Role, user model.User) *util.ChatError { + if role.ID != 0 { + role.Name = "" + } + + if user.ID != 0 { + user.Username = "" + } + _, err := d.db.Exec( + `delete from "role" + where + "role_id" = (select "id" from "role" where "id" = $1 or "name" = $2) + and + "user_id" = (select "id" from "user" where "id" = $3 or "username" = $4)`, + role.ID, role.Name, + user.ID, user.Username) + + return util.MakeError(err, util.DATABASE_QUERY_FAULT) +} + +func (d RoleBindingDAOPG) ListRolesOfUser(user model.User) ([]model.Role, *util.ChatError) { + if user.ID != 0 { + user.Username = "" + } + + rows, err := d.db.NamedQuery( + `select + "r"."id" as "id", + "r"."name" as "name" + from "role_binding" "rb" + join "role" "r" on "r"."id" = "rb"."role_id" + where "user_id" = (select "id" from "user" where "id" = :id or "username" = :username)`, + &user) + + if err != nil { + return nil, util.MakeError(err, util.DATABASE_QUERY_FAULT) + } + + var roles []model.Role + + for rows.Next() { + role := model.Role{} + err = rows.Scan(&role) + + if err != nil { + return nil, util.MakeError(err, util.DATABASE_QUERY_FAULT) + } + + roles = append(roles, role) + } + + return roles, nil +} + +func (d RoleBindingDAOPG) ListUserWithRole(role model.Role) ([]model.User, *util.ChatError) { + if role.ID != 0 { + role.Name = "" + } + + rows, err := d.db.NamedQuery( + `select + "u".* + from "user" "u" + join "role_binding" "rb" on "rb"."user_id" = "u"."id" + where "rb"."role_id" = (select "id" from "role" where "id" = :id or "name" = :name)`, + &role) + + if err != nil { + return nil, util.MakeError(err, util.DATABASE_QUERY_FAULT) + } + + var users []model.User + for rows.Next() { + user := model.User{} + err = rows.Scan(&user) + + if err != nil { + return nil, util.MakeError(err, util.DATABASE_QUERY_FAULT) + } + + users = append(users, user) + } + + return users, nil +} + +func MakeRoleBindingDAO() (RoleBindingDAOPG, *util.ChatError) { + dao := RoleBindingDAOPG{} + conn, err := getDatabase() + if err != nil { + return dao, err + } + + dao.db = conn + return dao, nil +} diff --git a/dao/postgres/RoleDAO.go b/dao/postgres/RoleDAO.go new file mode 100644 index 0000000..47a934a --- /dev/null +++ b/dao/postgres/RoleDAO.go @@ -0,0 +1,98 @@ +package postgres + +import ( + "git.tek.govt.hu/dowerx/chat/server/model" + "git.tek.govt.hu/dowerx/chat/server/util" + "github.com/jmoiron/sqlx" +) + +type RoleDAOPG struct { + pgDAO +} + +func (d RoleDAOPG) Create(name string) (int, *util.ChatError) { + rows, err := d.db.Query(`insert into "role" ("name") values ($1) returning "id"`, name) + if err != nil { + return 0, util.MakeError(err, util.DATABASE_QUERY_FAULT) + } + + if !rows.Next() { + return 0, &util.ChatError{Message: "no id returned by insert", Code: util.DATABASE_QUERY_FAULT} + } + + var id int + err = rows.Scan(&id) + return id, util.MakeError(err, util.DATABASE_QUERY_FAULT) +} + +func (d RoleDAOPG) Read(role model.Role) (model.Role, *util.ChatError) { + var rows *sqlx.Rows + var err error + if role.ID != 0 { + rows, err = d.db.NamedQuery(`select * from "role" where "id" = :id`, &role) + } else { + rows, err = d.db.NamedQuery(`select * from "role" where "name" = :name`, &role) + } + defer rows.Close() + + if err != nil { + return role, util.MakeError(err, util.DATABASE_QUERY_FAULT) + } + + if !rows.Next() { + return role, &util.ChatError{Message: "", Code: util.NOT_FOUND} + } + + err = rows.StructScan(&role) + return role, util.MakeError(err, util.DATABASE_QUERY_FAULT) +} + +func (d RoleDAOPG) List() ([]model.Role, *util.ChatError) { + rows, err := d.db.Queryx(`select * from "role" order by "name"`) + if err != nil { + return nil, util.MakeError(err, util.DATABASE_QUERY_FAULT) + } + defer rows.Close() + + roles := make([]model.Role, 0) + + for rows.Next() { + role := model.Role{} + + err = rows.StructScan(&role) + if err != nil { + break + } + + roles = append(roles, role) + } + + return roles, util.MakeError(err, util.DATABASE_QUERY_FAULT) +} + +func (d RoleDAOPG) Update(role model.Role) *util.ChatError { + _, err := d.db.NamedExec(`update "role" set "name" = :name where "id" = :id`, &role) + return util.MakeError(err, util.DATABASE_QUERY_FAULT) +} + +func (d RoleDAOPG) Delete(role model.Role) *util.ChatError { + var err error + if role.ID != 0 { + _, err = d.db.NamedExec(`delete from "role" where "id" = :id`, &role) + } else { + _, err = d.db.NamedExec(`delete from "role" where "name" = :name`, &role) + } + + return util.MakeError(err, util.DATABASE_QUERY_FAULT) +} + +func MakeRoleDAO() (RoleDAOPG, *util.ChatError) { + dao := RoleDAOPG{} + conn, err := getDatabase() + if err != nil { + return dao, err + } + + dao.db = conn + return dao, nil +} diff --git a/model/Channel.go b/model/Channel.go index 16ab800..5fd0153 100644 --- a/model/Channel.go +++ b/model/Channel.go @@ -1,7 +1,8 @@ package model type Channel struct { - ID int `db:"id" json:"id"` - Name string `db:"name" json:"name"` - Description string `db:"description" json:"description"` + ID int `db:"id" json:"id"` + Name string `db:"name" json:"name"` + Description string `db:"description" json:"description"` + Rights RightEnum `db:"rights" json:"rights"` } diff --git a/model/Right.go b/model/Right.go index f193cda..ffb9915 100644 --- a/model/Right.go +++ b/model/Right.go @@ -1,10 +1,10 @@ package model -type Right string +type RightEnum string const ( - RightRead = "R" - RightWrite = "W" - RightReadWrite = "RW" - RightAdmin = "A" + RightRead RightEnum = "R" + RightWrite RightEnum = "W" + RightReadWrite RightEnum = "RW" + RightAdmin RightEnum = "A" ) diff --git a/model/Role.go b/model/Role.go new file mode 100644 index 0000000..8df4c08 --- /dev/null +++ b/model/Role.go @@ -0,0 +1,6 @@ +package model + +type Role struct { + ID int `db:"id"` + Name string `db:"name"` +}