initiating keycloak access role
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package access
|
||||
|
||||
import (
|
||||
"antrian-operasi/internal/shared"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type AccessHandler struct {
|
||||
repo IAccessRepository
|
||||
}
|
||||
|
||||
func NewAccessHandler(repo IAccessRepository) AccessHandler {
|
||||
return AccessHandler{repo}
|
||||
}
|
||||
|
||||
func (h AccessHandler) SyncKeycloakRole(c *gin.Context) {
|
||||
var req SyncKeycloakRoleRequest
|
||||
|
||||
//bind json body
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(400, shared.BaseErrorResponse{
|
||||
Success: false,
|
||||
Code: 400,
|
||||
Message: "error bind json",
|
||||
Errors: shared.ValidationError(err),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
//check user role exist
|
||||
users, err := h.repo.FindUserByKeycloakId(c, req.KeycloakId)
|
||||
if err != nil {
|
||||
errMessage := []string{err.Error()}
|
||||
c.JSON(500, shared.BaseErrorResponse{
|
||||
Success: false,
|
||||
Code: 500,
|
||||
Message: "error finding user by keycloak id",
|
||||
Errors: errMessage,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
//if not exist, create user & permission
|
||||
if len(users) == 0 {
|
||||
err = h.repo.CreateUserPermission(c, req)
|
||||
} else { //if exist, update role permission
|
||||
userId := users[0].ID
|
||||
err = h.repo.UpdateUserPermission(c, userId, req)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
errMessage := []string{err.Error()}
|
||||
c.JSON(500, shared.BaseErrorResponse{
|
||||
Success: false,
|
||||
Code: 500,
|
||||
Message: "update / insert permission error",
|
||||
Errors: errMessage,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, shared.BaseResponse[SyncKeycloakRoleRequest]{
|
||||
Success: true,
|
||||
Code: 200,
|
||||
Message: "success sync role",
|
||||
Data: req,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package access
|
||||
|
||||
type RolePermissionModel struct {
|
||||
ID string `db:"id"`
|
||||
Name string `db:"name"`
|
||||
IsActive bool `db:"is_active"`
|
||||
}
|
||||
|
||||
type RolePageModel struct {
|
||||
ID string `db:"id"`
|
||||
PagePath string `db:"page_path"`
|
||||
}
|
||||
|
||||
type RoleUserModel struct {
|
||||
ID string `db:"id"`
|
||||
Name string `db:"name"`
|
||||
Email string `db:"email"`
|
||||
KeycloakId string `db:"keycloak_id"`
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
package access
|
||||
|
||||
import (
|
||||
"antrian-operasi/internal/database"
|
||||
queryUtils "antrian-operasi/internal/utils/query"
|
||||
"context"
|
||||
"database/sql"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
const DB_NAME = "db_role_access"
|
||||
const TBL_USER = "role_users"
|
||||
const TBL_PERMISSION = "role_permission"
|
||||
const TBL_USER_PERMISSION = "role_user_permission"
|
||||
|
||||
type IAccessRepository interface {
|
||||
FindUserByKeycloakId(c context.Context, id string) ([]RoleUserModel, error)
|
||||
CreateUserPermission(c context.Context, req SyncKeycloakRoleRequest) error
|
||||
UpdateUserPermission(c context.Context, userId string, req SyncKeycloakRoleRequest) error
|
||||
}
|
||||
|
||||
type accessRepo struct {
|
||||
queryBuilder *queryUtils.QueryBuilder
|
||||
db database.Service
|
||||
}
|
||||
|
||||
func NewRepository(dbService database.Service) IAccessRepository {
|
||||
queryBuilder := queryUtils.NewQueryBuilder(queryUtils.DBTypePostgreSQL).SetAllowedColumns([]string{
|
||||
"id", "name", "is_active", "keycloak_id", "id_user", "id_permission", "email", "created_at",
|
||||
}).SetAllowedTables([]string{TBL_USER})
|
||||
|
||||
queryBuilder.SetSecurityOptions(false, 100)
|
||||
|
||||
return accessRepo{
|
||||
queryBuilder: queryBuilder,
|
||||
db: dbService,
|
||||
}
|
||||
}
|
||||
|
||||
func (r accessRepo) FindUserByKeycloakId(c context.Context, id string) ([]RoleUserModel, error) {
|
||||
var result []RoleUserModel
|
||||
|
||||
query := queryUtils.DynamicQuery{
|
||||
From: TBL_USER,
|
||||
Fields: []queryUtils.SelectField{
|
||||
{Expression: "id"},
|
||||
{Expression: "keycloak_id"},
|
||||
},
|
||||
Filters: []queryUtils.FilterGroup{
|
||||
{
|
||||
Filters: []queryUtils.DynamicFilter{
|
||||
{Column: "keycloak_id", Operator: queryUtils.OpEqual, Value: id},
|
||||
}, LogicOp: "AND",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
dbconn, err := r.db.GetSQLXDB(DB_NAME)
|
||||
if err != nil {
|
||||
log.Printf("Unable to connect db : %s", err)
|
||||
return result, err
|
||||
}
|
||||
|
||||
err = r.queryBuilder.ExecuteQuery(c, dbconn, query, &result)
|
||||
if err != nil {
|
||||
log.Printf("Unable to execute query search user : %s", err)
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (r accessRepo) CreateUserPermission(c context.Context, req SyncKeycloakRoleRequest) error {
|
||||
db, err := r.db.GetSQLXDB(DB_NAME)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// START TRANSACTION
|
||||
tx, err := db.BeginTx(c, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// insert user
|
||||
userId, err := r.insertUser(c, tx, req)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
err = r.getOrCreateUserPermission(c, db, tx, userId, req.Roles)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r accessRepo) UpdateUserPermission(c context.Context, userId string, req SyncKeycloakRoleRequest) error {
|
||||
db, err := r.db.GetSQLXDB(DB_NAME)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// START TRANSACTION
|
||||
tx, err := db.BeginTx(c, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// delete all user permission
|
||||
err = r.deletePermissionByUserId(c, tx, userId)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
err = r.getOrCreateUserPermission(c, db, tx, userId, req.Roles)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PRIVATE FUNCTIONS
|
||||
|
||||
// Table user function
|
||||
|
||||
func (r accessRepo) insertUser(c context.Context, tx *sql.Tx, req SyncKeycloakRoleRequest) (string, error) {
|
||||
newUserId := uuid.New().String()
|
||||
|
||||
insertUserQuery := queryUtils.InsertData{
|
||||
Columns: []string{
|
||||
"id", "name", "email", "keycloak_id", "created_at",
|
||||
},
|
||||
Values: []interface{}{
|
||||
newUserId, req.Username, req.Email, req.KeycloakId, time.Now(),
|
||||
},
|
||||
}
|
||||
|
||||
returningCols := []string{
|
||||
"id",
|
||||
}
|
||||
|
||||
sql, args, err := r.queryBuilder.BuildInsertQuery(TBL_USER, insertUserQuery, returningCols...)
|
||||
if err != nil {
|
||||
log.Printf("error building query create user %s", err)
|
||||
return newUserId, err
|
||||
}
|
||||
|
||||
_, err = tx.ExecContext(c, sql, args...)
|
||||
if err != nil {
|
||||
log.Printf("error executing query create user %s", err)
|
||||
return newUserId, err
|
||||
}
|
||||
log.Printf(sql, args)
|
||||
log.Printf("success insert user")
|
||||
|
||||
return newUserId, nil
|
||||
}
|
||||
|
||||
// End Table user function
|
||||
|
||||
// Table permission functions
|
||||
|
||||
func (r accessRepo) findPermissionByRoleName(c context.Context, dbconn *sqlx.DB, roles []string) ([]RolePermissionModel, error) {
|
||||
var result []RolePermissionModel
|
||||
|
||||
query := queryUtils.DynamicQuery{
|
||||
From: TBL_PERMISSION,
|
||||
Fields: []queryUtils.SelectField{
|
||||
{Expression: "id"},
|
||||
{Expression: "name"},
|
||||
},
|
||||
Filters: []queryUtils.FilterGroup{
|
||||
{
|
||||
Filters: []queryUtils.DynamicFilter{
|
||||
{Column: "name", Operator: queryUtils.OpIn, Value: roles},
|
||||
}, LogicOp: "AND",
|
||||
},
|
||||
},
|
||||
}
|
||||
err := r.queryBuilder.ExecuteQuery(c, dbconn, query, &result)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r accessRepo) insertPermissionByRoleName(c context.Context, tx *sql.Tx, newRoles []string) ([]string, error) {
|
||||
var idRoles []string
|
||||
|
||||
if len(newRoles) > 0 {
|
||||
var valuePermission [][]interface{}
|
||||
|
||||
for _, role := range newRoles {
|
||||
id := uuid.New().String()
|
||||
itemValues := []interface{}{id, role, false}
|
||||
|
||||
idRoles = append(idRoles, id)
|
||||
valuePermission = append(valuePermission, itemValues)
|
||||
}
|
||||
|
||||
insertPermissionQuery := queryUtils.InsertBulkData{
|
||||
Columns: []string{
|
||||
"id", "name", "is_active",
|
||||
},
|
||||
Values: valuePermission,
|
||||
}
|
||||
|
||||
returningCols := []string{
|
||||
"id",
|
||||
}
|
||||
sql, args, err := r.queryBuilder.BuildBulkInsertQuery(TBL_PERMISSION, insertPermissionQuery, returningCols...)
|
||||
if err != nil {
|
||||
log.Printf("error building query insert new permission %s", err)
|
||||
|
||||
return idRoles, err
|
||||
}
|
||||
_, err = tx.ExecContext(c, sql, args...)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return idRoles, err
|
||||
}
|
||||
log.Printf(sql, args)
|
||||
log.Printf("success insert new permission")
|
||||
}
|
||||
|
||||
return idRoles, nil
|
||||
}
|
||||
|
||||
// END table permission functions
|
||||
|
||||
// Table user permission functions
|
||||
|
||||
func (r accessRepo) deletePermissionByUserId(c context.Context, tx *sql.Tx, userId string) error {
|
||||
filters := []queryUtils.FilterGroup{
|
||||
{
|
||||
Filters: []queryUtils.DynamicFilter{
|
||||
{Column: "id_user", Operator: queryUtils.OpEqual, Value: userId},
|
||||
},
|
||||
},
|
||||
}
|
||||
sql, args, err := r.queryBuilder.BuildDeleteQuery(TBL_USER_PERMISSION, filters)
|
||||
if err != nil {
|
||||
log.Printf("Unable to create delete user permission query : %v", err)
|
||||
return err
|
||||
}
|
||||
_, err = tx.ExecContext(c, sql, args...)
|
||||
if err != nil {
|
||||
log.Printf("Unable to executing delete user permission : %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r accessRepo) insertUserPermission(c context.Context, tx *sql.Tx, userId string, roleIds []string) error {
|
||||
if len(roleIds) > 0 {
|
||||
var valueUserPermission [][]interface{}
|
||||
|
||||
for _, roleId := range roleIds {
|
||||
id := uuid.New().String()
|
||||
itemValues := []interface{}{id, userId, roleId}
|
||||
|
||||
valueUserPermission = append(valueUserPermission, itemValues)
|
||||
}
|
||||
|
||||
insertUserPermissionQuery := queryUtils.InsertBulkData{
|
||||
Columns: []string{
|
||||
"id", "id_user", "id_permission",
|
||||
},
|
||||
Values: valueUserPermission,
|
||||
}
|
||||
|
||||
returningCols := []string{
|
||||
"id",
|
||||
}
|
||||
sql, args, err := r.queryBuilder.BuildBulkInsertQuery(TBL_USER_PERMISSION, insertUserPermissionQuery, returningCols...)
|
||||
if err != nil {
|
||||
log.Printf("error building query insert user permission %s", err)
|
||||
|
||||
return err
|
||||
}
|
||||
_, err = tx.ExecContext(c, sql, args...)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
log.Printf(sql, args)
|
||||
log.Printf("success insert user permission")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r accessRepo) getOrCreateUserPermission(c context.Context, db *sqlx.DB, tx *sql.Tx, userId string, roles []string) error {
|
||||
var permissionIds []string
|
||||
mapPermissionExist := make(map[string]string)
|
||||
var newRoles []string
|
||||
|
||||
// fetch permission by name, append the permission ids
|
||||
fetchRoles, err := r.findPermissionByRoleName(c, db, roles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(fetchRoles) > 0 {
|
||||
for _, item := range fetchRoles {
|
||||
permissionIds = append(permissionIds, item.ID)
|
||||
mapPermissionExist[item.Name] = item.ID
|
||||
}
|
||||
}
|
||||
|
||||
// if permission not exist, insert non-existed permission first then append the permission ids
|
||||
for _, role := range roles {
|
||||
if mapPermissionExist[role] == "" {
|
||||
newRoles = append(newRoles, role)
|
||||
}
|
||||
}
|
||||
// insert new , non existed permission
|
||||
if len(newRoles) > 0 {
|
||||
newRoleIds, err := r.insertPermissionByRoleName(c, tx, newRoles)
|
||||
if err != nil {
|
||||
return err
|
||||
} else { // append new role ids to permission ids
|
||||
permissionIds = append(permissionIds, newRoleIds...)
|
||||
}
|
||||
}
|
||||
// insert all permission to user
|
||||
err = r.insertUserPermission(c, tx, userId, permissionIds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// End Table user permission functions
|
||||
@@ -0,0 +1,14 @@
|
||||
package access
|
||||
|
||||
type SyncKeycloakRoleRequest struct {
|
||||
KeycloakId string `json:"keycloak_id" binding:"required"`
|
||||
Username string `json:"name" binding:"required"`
|
||||
Email string `json:"email" binding:"required"`
|
||||
Roles []string `json:"client_role"`
|
||||
}
|
||||
|
||||
type UpsertAccessPermissionRequest struct {
|
||||
Name string `json:"namaHakAkses"`
|
||||
Status string `json:"status"`
|
||||
Pages []string `json:"pages"`
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package access
|
||||
|
||||
import (
|
||||
"antrian-operasi/internal/database"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterRoutes(r *gin.RouterGroup, dbService database.Service) {
|
||||
accessRepo := NewRepository(dbService)
|
||||
accessHandler := NewAccessHandler(accessRepo)
|
||||
|
||||
r.POST("/sync-keycloak-role", accessHandler.SyncKeycloakRole)
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package routes
|
||||
import (
|
||||
"antrian-operasi/internal/config"
|
||||
"antrian-operasi/internal/database"
|
||||
"antrian-operasi/internal/domain/access"
|
||||
antrianoperasi "antrian-operasi/internal/domain/antrian_operasi"
|
||||
"antrian-operasi/internal/domain/dashboard"
|
||||
"antrian-operasi/internal/domain/keycloak"
|
||||
@@ -43,6 +44,10 @@ func RegisterRoutes(cfg *config.Config, dbService database.Service) *gin.Engine
|
||||
|
||||
api := router.Group("/api")
|
||||
|
||||
acc := api.Group("/access", authKeycloak)
|
||||
{
|
||||
access.RegisterRoutes(acc, dbService)
|
||||
}
|
||||
antrian := api.Group("/antrian-operasi", authKeycloak)
|
||||
{
|
||||
antrianoperasi.RegisterRoutes(antrian, dbService)
|
||||
|
||||
Reference in New Issue
Block a user