Permalink
Browse files

Closes #17 - Manage user permissions via the web interface

The old permission settings in the toml file will be accepted,
but ignored. They're used to migrate to the new database
driven system but after that they're not used.

UI and API rights are separate as they were conceptually with
the toml file. All users are in the "default" ui group and the
"disable" api group unless set otherwise. The status API is still
behind a flag as it requires the view debug permission.
  • Loading branch information...
lfkeitel committed Mar 12, 2018
1 parent 1179388 commit bee5d983e13515343923e2f4ba08a96624dc3e3a
@@ -107,6 +107,10 @@ func main() {
e.Log.WithField("error", err).Fatal("Error loading frontend templates")
}
if err := common.RunSystemInits(e); err != nil {
e.Log.WithField("error", err).Fatal("System initialization failed")
}
go tasks.StartTaskScheduler(e)
// Start web server
@@ -159,7 +159,10 @@ $.onReady(function() {
"valid_start": 0,
"valid_end": 0,
"can_manage": $('[name=can-manage]').prop('checked') ? 1 : 0,
"can_autoreg": $('[name=can-autoreg]').prop('checked') ? 1 : 0
"can_autoreg": $('[name=can-autoreg]').prop('checked') ? 1 : 0,
"allow_status_api": $('[name=user-api-status]').prop('checked') ? 1 : 0,
"ui_group": $('[name=user-ui-group]').value(),
"api_group": $('[name=user-api-group]').value()
};
if ($('[name=clear-pass]').prop("checked")) {
@@ -235,15 +235,6 @@ func setSensibleDefaults(c *Config) (*Config, error) {
if len(c.Auth.AuthMethod) == 0 {
c.Auth.AuthMethod = []string{"local"}
}
if len(c.Auth.AdminUsers) == 0 {
c.Auth.AdminUsers = []string{"admin"}
}
if len(c.Auth.HelpDeskUsers) == 0 {
c.Auth.HelpDeskUsers = []string{"helpdesk"}
}
if len(c.Auth.ReadOnlyUsers) == 0 {
c.Auth.ReadOnlyUsers = []string{"readonly"}
}
// DHCP
c.DHCP.ConfigFile = setStringOrDefault(c.DHCP.ConfigFile, "config/dhcp.conf")
@@ -114,3 +114,23 @@ func (d *DatabaseAccessor) SchemaVersion() int {
verRow.Scan(&currDBVer)
return currDBVer
}
type SystemInitFunc func(*Environment) error
var systemInitFuncs []SystemInitFunc
func RegisterSystemInitFunc(f SystemInitFunc) {
if systemInitFuncs == nil {
systemInitFuncs = make([]SystemInitFunc, 0, 1)
}
systemInitFuncs = append(systemInitFuncs, f)
}
func RunSystemInits(e *Environment) error {
for _, f := range systemInitFuncs {
if err := f(e); err != nil {
return err
}
}
return nil
}
@@ -59,6 +59,28 @@ func (u *UserController) saveUserHandler(w http.ResponseWriter, r *http.Request)
return
}
// Permission groups
uiGroup := r.FormValue("ui_group")
apiGroup := r.FormValue("api_group")
allowStatusAPI := r.FormValue("allow_status_api") == "1"
if (user.UIGroup != uiGroup || user.APIGroup != apiGroup || user.AllowStatusAPI != allowStatusAPI) && !sessionUser.Can(models.EditUserPermissions) {
common.NewAPIResponse("Permission denied", nil).WriteResponse(w, http.StatusForbidden)
return
}
if !common.StringInSlice(uiGroup, []string{"default", "admin", "helpdesk", "readonly"}) {
common.NewAPIResponse("Unknown ui group", nil).WriteResponse(w, http.StatusBadRequest)
return
}
user.UIGroup = uiGroup
if !common.StringInSlice(apiGroup, []string{"disable", "readonly-api", "readwrite-api"}) {
common.NewAPIResponse("Unknown api group", nil).WriteResponse(w, http.StatusBadRequest)
return
}
user.APIGroup = apiGroup
user.AllowStatusAPI = allowStatusAPI
// Password
password := r.FormValue("password")
if password != "" {
@@ -12,14 +12,16 @@ import (
"github.com/packet-guardian/packet-guardian/src/common"
)
const DBVersion = 2
const DBVersion = 3
type dbInit interface {
init(*common.DatabaseAccessor, *common.Config) error
}
var dbInits = make(map[string]dbInit)
type migrateFunc func(*common.DatabaseAccessor, *common.Config) error
func RegisterDatabaseAccessor(name string, db dbInit) {
dbInits[name] = db
}
@@ -12,6 +12,8 @@ import (
"fmt"
"strings"
"github.com/packet-guardian/packet-guardian/src/models/stores"
"github.com/go-sql-driver/mysql" // MySQL driver
"github.com/lfkeitel/verbose"
"github.com/packet-guardian/packet-guardian/src/common"
@@ -23,7 +25,7 @@ func init() {
type mySQLDB struct {
createFuncs map[string]func(*common.DatabaseAccessor) error
migrateFuncs []func(*common.DatabaseAccessor) error
migrateFuncs []migrateFunc
}
func newmySQLDBInit() *mySQLDB {
@@ -38,8 +40,9 @@ func newmySQLDBInit() *mySQLDB {
"user": m.createUserTable,
}
m.migrateFuncs = []func(*common.DatabaseAccessor) error{
m.migrateFuncs = []migrateFunc{
1: m.migrate1,
2: m.migrate2,
}
return m
@@ -106,6 +109,7 @@ func (m *mySQLDB) createTables(d *common.DatabaseAccessor) error {
for table, create := range m.createFuncs {
if !tables[table] {
fmt.Printf("Creating table %s\n", table)
if err := create(d); err != nil {
return err
}
@@ -114,7 +118,7 @@ func (m *mySQLDB) createTables(d *common.DatabaseAccessor) error {
return nil
}
func (m *mySQLDB) migrateTables(d *common.DatabaseAccessor) error {
func (m *mySQLDB) migrateTables(d *common.DatabaseAccessor, c *common.Config) error {
var currDBVer int
verRow := d.DB.QueryRow(`SELECT "value" FROM "settings" WHERE "id" = 'db_version'`)
if verRow == nil {
@@ -141,7 +145,7 @@ func (m *mySQLDB) migrateTables(d *common.DatabaseAccessor) error {
if migrate == nil {
continue
}
if err := migrate(d); err != nil {
if err := migrate(d, c); err != nil {
return err
}
}
@@ -160,7 +164,7 @@ func (m *mySQLDB) init(d *common.DatabaseAccessor, c *common.Config) error {
return err
}
return m.migrateTables(d)
return m.migrateTables(d, c)
}
func (m *mySQLDB) createBlacklistTable(d *common.DatabaseAccessor) error {
@@ -250,22 +254,25 @@ func (m *mySQLDB) createUserTable(d *common.DatabaseAccessor) error {
"can_autoreg" TINYINT DEFAULT 1,
"valid_start" INTEGER DEFAULT 0,
"valid_end" INTEGER DEFAULT 0,
"valid_forever" TINYINT DEFAULT 1
"valid_forever" TINYINT DEFAULT 1,
"ui_group" VARCHAR(20) NOT NULL DEFAULT 'default',
"api_group" VARCHAR(20) NOT NULL DEFAULT 'disable',
"allow_status_api" TINYINT DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=4;`
if _, err := d.DB.Exec(sql); err != nil {
return err
}
_, err := d.DB.Exec(`INSERT INTO "user"
("id", "username", "password") VALUES
(1, 'admin', '$2a$10$rZfN/gdXZdGYyLtUb6LF.eHOraDes3ibBECmWic2I3SocMC0L2Lxa'),
(2, 'helpdesk', '$2a$10$ICCdq/OyZBBoNPTRmfgntOnujD6INGv7ZAtA/Xq6JIdRMO65xCuNC'),
(3, 'readonly', '$2a$10$02NG6kQV.4UicpCnz8hyeefBD4JHKAlZToL2K0EN1HV.u6sXpP1Xy')`)
("id", "username", "password", "ui_group") VALUES
(1, 'admin', '$2a$10$rZfN/gdXZdGYyLtUb6LF.eHOraDes3ibBECmWic2I3SocMC0L2Lxa', 'admin'),
(2, 'helpdesk', '$2a$10$ICCdq/OyZBBoNPTRmfgntOnujD6INGv7ZAtA/Xq6JIdRMO65xCuNC', 'helpdesk'),
(3, 'readonly', '$2a$10$02NG6kQV.4UicpCnz8hyeefBD4JHKAlZToL2K0EN1HV.u6sXpP1Xy', 'readonly')`)
return err
}
func (m *mySQLDB) migrate1(d *common.DatabaseAccessor) error {
func (m *mySQLDB) migrate1(d *common.DatabaseAccessor, c *common.Config) error {
// Move device blacklist to blacklist table
bd, err := d.DB.Query(`SELECT "mac" FROM "device" WHERE "blacklisted" = 1`)
if err != nil {
@@ -295,3 +302,63 @@ func (m *mySQLDB) migrate1(d *common.DatabaseAccessor) error {
}
return nil
}
func (m *mySQLDB) migrate2(d *common.DatabaseAccessor, c *common.Config) error {
sql := `ALTER TABLE "user" ADD COLUMN (
"ui_group" VARCHAR(20) NOT NULL DEFAULT 'default',
"api_group" VARCHAR(20) NOT NULL DEFAULT 'disable',
"allow_status_api" TINYINT DEFAULT 0
);`
if _, err := d.DB.Exec(sql); err != nil {
return err
}
common.RegisterSystemInitFunc(migrateUserPermissions)
return nil
}
func migrateUserPermissions(e *common.Environment) error {
if err := migrateUserGroup(e, e.Config.Auth.AdminUsers, "ui", "admin"); err != nil {
return err
}
if err := migrateUserGroup(e, e.Config.Auth.HelpDeskUsers, "ui", "helpdesk"); err != nil {
return err
}
if err := migrateUserGroup(e, e.Config.Auth.ReadOnlyUsers, "ui", "readonly"); err != nil {
return err
}
if err := migrateUserGroup(e, e.Config.Auth.APIReadOnlyUsers, "api", "readonly-api"); err != nil {
return err
}
if err := migrateUserGroup(e, e.Config.Auth.APIReadWriteUsers, "api", "readwrite-api"); err != nil {
return err
}
if err := migrateUserGroup(e, e.Config.Auth.APIStatusUsers, "api-status", ""); err != nil {
return err
}
return nil
}
func migrateUserGroup(e *common.Environment, members []string, group, groupName string) error {
for _, username := range members {
user, err := stores.GetUserStore(e).GetUserByUsername(username)
if err != nil {
return err
}
switch group {
case "ui":
user.UIGroup = groupName
case "api":
user.APIGroup = groupName
case "api-status":
user.AllowStatusAPI = true
}
if err := user.Save(); err != nil {
return err
}
}
return nil
}
@@ -24,7 +24,7 @@ func init() {
type sqliteDB struct {
createFuncs map[string]func(*common.DatabaseAccessor) error
migrateFuncs []func(*common.DatabaseAccessor) error
migrateFuncs []migrateFunc
}
func newSQLiteDBInit() *sqliteDB {
@@ -39,8 +39,9 @@ func newSQLiteDBInit() *sqliteDB {
"user": s.createUserTable,
}
s.migrateFuncs = []func(*common.DatabaseAccessor) error{
s.migrateFuncs = []migrateFunc{
1: s.migrate1,
2: s.migrate2,
}
return s
@@ -86,6 +87,7 @@ func (s *sqliteDB) createTables(d *common.DatabaseAccessor) error {
for table, create := range s.createFuncs {
if !tables[table] {
fmt.Printf("Creating table %s\n", table)
if err := create(d); err != nil {
return err
}
@@ -94,7 +96,7 @@ func (s *sqliteDB) createTables(d *common.DatabaseAccessor) error {
return nil
}
func (s *sqliteDB) migrateTables(d *common.DatabaseAccessor) error {
func (s *sqliteDB) migrateTables(d *common.DatabaseAccessor, c *common.Config) error {
var currDBVer int
verRow := d.DB.QueryRow(`SELECT "value" FROM "settings" WHERE "id" = 'db_version'`)
if verRow == nil {
@@ -117,7 +119,7 @@ func (s *sqliteDB) migrateTables(d *common.DatabaseAccessor) error {
if migrate == nil {
continue
}
if err := migrate(d); err != nil {
if err := migrate(d, c); err != nil {
return err
}
}
@@ -137,7 +139,7 @@ func (s *sqliteDB) init(d *common.DatabaseAccessor, c *common.Config) error {
return err
}
return s.migrateTables(d)
return s.migrateTables(d, c)
}
func (s *sqliteDB) createBlacklistTable(d *common.DatabaseAccessor) error {
@@ -226,22 +228,25 @@ func (s *sqliteDB) createUserTable(d *common.DatabaseAccessor) error {
"can_autoreg" INTEGER DEFAULT 1,
"valid_start" INTEGER DEFAULT 0,
"valid_end" INTEGER DEFAULT 0,
"valid_forever" INTEGER DEFAULT 1
"valid_forever" INTEGER DEFAULT 1,
"ui_group" VARCHAR(20) NOT NULL DEFAULT 'default',
"api_group" VARCHAR(20) NOT NULL DEFAULT 'disable',
"allow_status_api" INTEGER DEFAULT 0
)`
if _, err := d.DB.Exec(sql); err != nil {
return err
}
_, err := d.DB.Exec(`INSERT INTO "user"
("id", "username", "password") VALUES
(1, 'admin', '$2a$10$rZfN/gdXZdGYyLtUb6LF.eHOraDes3ibBECmWic2I3SocMC0L2Lxa'),
(2, 'helpdesk', '$2a$10$ICCdq/OyZBBoNPTRmfgntOnujD6INGv7ZAtA/Xq6JIdRMO65xCuNC'),
(3, 'readonly', '$2a$10$02NG6kQV.4UicpCnz8hyeefBD4JHKAlZToL2K0EN1HV.u6sXpP1Xy')`)
("id", "username", "password", "ui_group") VALUES
(1, 'admin', '$2a$10$rZfN/gdXZdGYyLtUb6LF.eHOraDes3ibBECmWic2I3SocMC0L2Lxa', 'admin'),
(2, 'helpdesk', '$2a$10$ICCdq/OyZBBoNPTRmfgntOnujD6INGv7ZAtA/Xq6JIdRMO65xCuNC', 'helpdesk'),
(3, 'readonly', '$2a$10$02NG6kQV.4UicpCnz8hyeefBD4JHKAlZToL2K0EN1HV.u6sXpP1Xy', 'readonly')`)
return err
}
func (s *sqliteDB) migrate1(d *common.DatabaseAccessor) error {
func (s *sqliteDB) migrate1(d *common.DatabaseAccessor, c *common.Config) error {
// Move device blacklist to blacklist table
bd, err := d.DB.Query(`SELECT "mac" FROM "device" WHERE "blacklisted" = 1`)
if err != nil {
@@ -271,3 +276,19 @@ func (s *sqliteDB) migrate1(d *common.DatabaseAccessor) error {
}
return nil
}
func (s *sqliteDB) migrate2(d *common.DatabaseAccessor, c *common.Config) error {
sql := `ALTER TABLE "user" ADD COLUMN (
"ui_group" VARCHAR(20) NOT NULL DEFAULT 'default',
"api_group" VARCHAR(20) NOT NULL DEFAULT 'disable',
"allow_status_api" INTEGER DEFAULT 0
);`
if _, err := d.DB.Exec(sql); err != nil {
return err
}
// migrateUserPermissions is defined in the MySQL file, there's nothing DB specific
common.RegisterSystemInitFunc(migrateUserPermissions)
return nil
}
Oops, something went wrong.

0 comments on commit bee5d98

Please sign in to comment.