Compare commits
64 Commits
Author | SHA1 | Date | |
---|---|---|---|
2754fb8144 | |||
4650a971a3 | |||
8cd1292a41 | |||
ea5198a5b9 | |||
d000469836 | |||
02061f5b26 | |||
ea3e8ddfbc | |||
8182b899cf | |||
ae5b864feb | |||
4a6a7270f9 | |||
a7beb94341 | |||
4a0e98bab8 | |||
66a1917e6f | |||
526161f6b4 | |||
ff16acc816 | |||
ee822b5c9d | |||
e427184310 | |||
5719ce8f5d | |||
a78f2b7c88 | |||
1171a7bbaa | |||
8ff92e206e | |||
19ffc9e683 | |||
968bcf1b7a | |||
77d063867a | |||
c82bffe421 | |||
b801563074 | |||
4bc430633e | |||
69a25fbb09 | |||
840b9f4863 | |||
5b87ef0d30 | |||
bfc734a6d1 | |||
b5c9b5ce19 | |||
9f0dafd4fd | |||
abaa291a14 | |||
de1a076d64 | |||
1b5a2e89dd | |||
8799f0f796 | |||
317e0ab83d | |||
116a9e827b | |||
2ab6240a24 | |||
bb3bf3093d | |||
1d1aa098a9 | |||
f68bd9637d | |||
5f63ee235b | |||
092fe32baf | |||
44d3bc71ed | |||
dc3c5d1068 | |||
d834a5c362 | |||
9e0c1e7cd7 | |||
9ac729b684 | |||
7f43662cbc | |||
b35d365467 | |||
77d487c1ce | |||
f5827ef432 | |||
6e1a28d2df | |||
5c3b2e19cf | |||
e109cd084d | |||
f29c733080 | |||
97cd75b0d7 | |||
b278a3c7d8 | |||
498dd9a8c3 | |||
c99ffa8368 | |||
8fec84c118 | |||
2c63ea9632 |
40
.drone.yml
40
.drone.yml
@@ -81,31 +81,29 @@ steps:
|
|||||||
- sudo bash -c 'mv /home/l075239/smt/test.env /home/l075239/smt/.env'
|
- sudo bash -c 'mv /home/l075239/smt/test.env /home/l075239/smt/.env'
|
||||||
- sudo bash -c '/etc/init.d/smt restart'
|
- sudo bash -c '/etc/init.d/smt restart'
|
||||||
|
|
||||||
- name: dell-deploy
|
|
||||||
# # https://github.com/cschlosser/drone-ftps/blob/master/README.md
|
- name: dell-sftp-deploy
|
||||||
image: cschlosser/drone-ftps
|
image: hypervtechnics/drone-sftp
|
||||||
when:
|
settings:
|
||||||
event:
|
host: deft.dell.com
|
||||||
- tag
|
username:
|
||||||
environment:
|
from_secret: DELLFTP_USER
|
||||||
FTP_USERNAME:
|
password:
|
||||||
from_secret: FTP_USERNAME
|
from_secret: DELLFTP_PASS
|
||||||
FTP_PASSWORD:
|
port: 22
|
||||||
from_secret: FTP_PASSWORD
|
source: ./
|
||||||
PLUGIN_HOSTNAME: ftp.emc.com:21
|
filter: smt*
|
||||||
PLUGIN_SECURE: false
|
clean: false
|
||||||
PLUGIN_VERIFY: false
|
target: /
|
||||||
PLUGIN_CHMOD: false
|
overwrite: true
|
||||||
#PLUGIN_DEBUG: false
|
verbose: true
|
||||||
PLUGIN_INCLUDE: ^smt$,^smt_checksum.txt$
|
|
||||||
PLUGIN_EXCLUDE: ^\.git/$,^\controllers/$,^\middlewares/$,^\models/$,^\utils/$,^\pkg.build/$,^\pkg.mod/$,^\www/$
|
|
||||||
|
|
||||||
- name: rebuild-cache-with-filesystem
|
- name: rebuild-cache-with-filesystem
|
||||||
image: meltwater/drone-cache
|
image: meltwater/drone-cache
|
||||||
pull: true
|
pull: true
|
||||||
when:
|
#when:
|
||||||
event:
|
# event:
|
||||||
- tag
|
# - tag
|
||||||
settings:
|
settings:
|
||||||
backend: "filesystem"
|
backend: "filesystem"
|
||||||
#debug: true
|
#debug: true
|
||||||
|
117
README.md
117
README.md
@@ -6,6 +6,8 @@ Build Date: `{BUILDTIME}`
|
|||||||
|
|
||||||
Build Hash: `{SHA1VER}`
|
Build Hash: `{SHA1VER}`
|
||||||
|
|
||||||
|
Go version: `{RUNTIME}`
|
||||||
|
|
||||||
Written by Nathan Coad (nathan.coad@dell.com)
|
Written by Nathan Coad (nathan.coad@dell.com)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
@@ -80,6 +82,10 @@ WantedBy=multi-user.target
|
|||||||
```
|
```
|
||||||
## API Usage
|
## API Usage
|
||||||
|
|
||||||
|
API calls return http status code of **200** if successful, or **4xx** if unsuccessful. API calls that are unsuccessful will also include a JSON response with the key `error` and a value of the reason for the failure. Successful API calls will include a `message` key with a value of either success or something more detailed such as "user deletion success"
|
||||||
|
|
||||||
|
API calls that create or modify a record will include the created/updated record in the JSON response.
|
||||||
|
|
||||||
### Login
|
### Login
|
||||||
**POST** `/api/login`
|
**POST** `/api/login`
|
||||||
|
|
||||||
@@ -92,7 +98,9 @@ Body
|
|||||||
```
|
```
|
||||||
This API call will return a JWT token that must be present for any other API calls to succeed. The validity duration of this token is based on the configured TOKEN_HOUR_LIFESPAN value. JWT token is returned as value of `access_token`, and must be supplied via a HTTP header in the form `"Authorization: Bearer <JWT_TOKEN>"` for all subsequent API calls.
|
This API call will return a JWT token that must be present for any other API calls to succeed. The validity duration of this token is based on the configured TOKEN_HOUR_LIFESPAN value. JWT token is returned as value of `access_token`, and must be supplied via a HTTP header in the form `"Authorization: Bearer <JWT_TOKEN>"` for all subsequent API calls.
|
||||||
|
|
||||||
### Unlock
|
#### Admin Only operations
|
||||||
|
|
||||||
|
#### Unlock
|
||||||
**POST** `/api/admin/unlock`
|
**POST** `/api/admin/unlock`
|
||||||
|
|
||||||
Body
|
Body
|
||||||
@@ -106,6 +114,11 @@ If the SECRETS_KEY environment variable is not defined, this API call to unlock
|
|||||||
|
|
||||||
This API call can only be made once after the service has started. Subsequent calls will receive an error until the service is restarted.
|
This API call can only be made once after the service has started. Subsequent calls will receive an error until the service is restarted.
|
||||||
|
|
||||||
|
#### Event Logs
|
||||||
|
**GET** `/api/admin/logs`
|
||||||
|
|
||||||
|
This operation can only be performed by a user with that is admin enabled. Lists all event logs.
|
||||||
|
|
||||||
### User Operations
|
### User Operations
|
||||||
|
|
||||||
#### Register User
|
#### Register User
|
||||||
@@ -133,6 +146,17 @@ Body
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Add an ldap user
|
||||||
|
|
||||||
|
Body
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"userName": "Ldap User",
|
||||||
|
"groupName": "Users",
|
||||||
|
"ldapUser": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Registering a user requires specifying the group to which the user will belong. There are 2 built-in groups, with groupName of 'Administrators' or 'Users' and corresponding groupId of 1 and 2 respectively. Available groups can be retrieved via the `/api/admin/groups/list`
|
Registering a user requires specifying the group to which the user will belong. There are 2 built-in groups, with groupName of 'Administrators' or 'Users' and corresponding groupId of 1 and 2 respectively. Available groups can be retrieved via the `/api/admin/groups/list`
|
||||||
|
|
||||||
This operation can only be performed by a user that is a member of a group with the admin flag enabled, or a user who has the admin flag enabled individually on their database record.
|
This operation can only be performed by a user that is a member of a group with the admin flag enabled, or a user who has the admin flag enabled individually on their database record.
|
||||||
@@ -178,6 +202,17 @@ Body
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Create a new permission for a group
|
||||||
|
|
||||||
|
Body
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"Description": "Group access to default safe",
|
||||||
|
"safeId": 1,
|
||||||
|
"groupId": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Creates a new permission mapping user/group to safe. Currently the create permission operation requires knowing the correct user Id or group Id, as well as the safe Id onto which permissions will be granted. This operation can only be performed by a user that is admin enabled.
|
Creates a new permission mapping user/group to safe. Currently the create permission operation requires knowing the correct user Id or group Id, as well as the safe Id onto which permissions will be granted. This operation can only be performed by a user that is admin enabled.
|
||||||
|
|
||||||
#### Delete Permission
|
#### Delete Permission
|
||||||
@@ -189,7 +224,7 @@ Delete permission by specifying description
|
|||||||
Body
|
Body
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"Description":"Readonly access to default safe"
|
"Description": "Readonly access to default safe"
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -199,7 +234,7 @@ Delete permission by specifying permission id
|
|||||||
Body
|
Body
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"permissionId":2
|
"permissionId": 2
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -208,6 +243,22 @@ Deletes a permission mapping either a user or a group to a safe. Either the perm
|
|||||||
|
|
||||||
Deleting a permission should be performed prior to deleting any groups specified in that permission.
|
Deleting a permission should be performed prior to deleting any groups specified in that permission.
|
||||||
|
|
||||||
|
#### Update Permission
|
||||||
|
**POST** `/api/admin/permission/update`
|
||||||
|
|
||||||
|
Change description of permission
|
||||||
|
|
||||||
|
Body
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"permissionId": 2
|
||||||
|
"Description": "New Permission Description"
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Updates an existing permission. The permissionId must be specified. This operation can only be performed by a user that is admin enabled.
|
||||||
|
|
||||||
### Group Operations
|
### Group Operations
|
||||||
|
|
||||||
#### List Groups
|
#### List Groups
|
||||||
@@ -318,7 +369,7 @@ This operation can only be performed by a user that is admin enabled, or that is
|
|||||||
|
|
||||||
### Secrets Operations
|
### Secrets Operations
|
||||||
|
|
||||||
#### Store
|
#### Store Secret
|
||||||
**POST** `/api/secret/add`
|
**POST** `/api/secret/add`
|
||||||
|
|
||||||
Store secret if user only has access to a single safe
|
Store secret if user only has access to a single safe
|
||||||
@@ -351,39 +402,55 @@ If a secret exists with a matching deviceName and deviceCategory in a safe that
|
|||||||
|
|
||||||
If the current user has access to multiple safes, then the destination safeId will also need to be specified.
|
If the current user has access to multiple safes, then the destination safeId will also need to be specified.
|
||||||
|
|
||||||
#### Retrieve
|
#### Get Secret
|
||||||
**POST** `/api/secret/get`
|
**POST** `/api/secret/get`
|
||||||
|
|
||||||
Body
|
Body
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"deviceName": "",
|
"deviceName": "device.example.com",
|
||||||
"deviceCategory": "",
|
"deviceCategory": "",
|
||||||
"userName": ""
|
"userName": "example-user"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Must be logged in to execute this command. Only secrets registered with the current user's RoleId can be retrieved.
|
Body
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"secretId": 29
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Must be logged in to execute this command. Only secrets that the logged in user has access to can be retrieved.
|
||||||
|
|
||||||
Either deviceName or deviceCategory can be specified (or both). Wildcards are supported for both deviceName and deviceCategory fields. userName can also be specified in conjunction with deviceName or deviceCategory.
|
Either deviceName or deviceCategory can be specified (or both). Wildcards are supported for both deviceName and deviceCategory fields. userName can also be specified in conjunction with deviceName or deviceCategory.
|
||||||
1. The percent sign % wildcard matches any sequence of zero or more characters.
|
1. The percent sign % wildcard matches any sequence of zero or more characters.
|
||||||
2. The underscore _ wildcard matches any single character.
|
2. The underscore _ wildcard matches any single character.
|
||||||
|
|
||||||
|
If the secretId is known, that can also be used to query for the secret. In this case the secretId uniquely identifies the secret so no other parameters are necessary.
|
||||||
|
|
||||||
#### Search by device name
|
#### Search by device name
|
||||||
|
|
||||||
**GET** `/api/secret/retrieve/name/<searchname>`
|
**GET** `/api/secret/retrieve/name/<searchname>`
|
||||||
|
|
||||||
Search for a secret specified by deviceName using a GET request.
|
Search for a secret specified by deviceName using a GET request.
|
||||||
Must be logged in to execute this command. Only secrets registered with the current user's RoleId can be retrieved.
|
Must be logged in to execute this command. Only secrets in safes that the current user can access can be retrieved.
|
||||||
|
|
||||||
#### Search by device category
|
#### Search by device category
|
||||||
|
|
||||||
**GET** `/api/secret/retrieve/category/<searchname>`
|
**GET** `/api/secret/retrieve/category/<searchname>`
|
||||||
|
|
||||||
Search for a secret specified by deviceCategory using a GET request.
|
Search for a secret specified by deviceCategory using a GET request.
|
||||||
Must be logged in to execute this command. Only secrets registered with the current user's RoleId can be retrieved.
|
Must be logged in to execute this command. Only secrets in safes that the current user can access can be retrieved.
|
||||||
|
|
||||||
#### Update
|
#### Search by username
|
||||||
|
|
||||||
|
**GET** `/api/secret/retrieve/user/<searchname>`
|
||||||
|
|
||||||
|
Search for a secret specified by userName using a GET request.
|
||||||
|
Must be logged in to execute this command. Only secrets in safes that the current user can access can be retrieved.
|
||||||
|
|
||||||
|
#### Update Secret
|
||||||
**POST** `/api/secret/update`
|
**POST** `/api/secret/update`
|
||||||
|
|
||||||
Update secret value for existing secret record
|
Update secret value for existing secret record
|
||||||
@@ -415,10 +482,36 @@ The values specified in deviceName and deviceCategory must match exactly one exi
|
|||||||
|
|
||||||
If a user has read-write access to multiple safes, then specifying a different safeId to the one currently holding the secret will allow the secret to be moved into the other safe.
|
If a user has read-write access to multiple safes, then specifying a different safeId to the one currently holding the secret will allow the secret to be moved into the other safe.
|
||||||
|
|
||||||
#### List
|
#### List Secrets
|
||||||
|
|
||||||
**GET** `/api/secret/list`
|
**GET** `/api/secret/list`
|
||||||
|
|
||||||
Will generate a list of secrets with their secretId, userName, deviceCategory and deviceName fields, but not secret data. Only secrets belonging to safes that are accessible by the currently logged in user will be returned
|
Will generate a list of secrets with their secretId, userName, deviceCategory and deviceName fields, but not secret data. Only secrets belonging to safes that are accessible by the currently logged in user will be returned
|
||||||
|
|
||||||
|
#### Delete Secret
|
||||||
|
|
||||||
|
**POST** `/api/secret/delete`
|
||||||
|
|
||||||
|
|
||||||
|
Body
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"deviceName": "device.example.com",
|
||||||
|
"deviceCategory": "",
|
||||||
|
"userName": "example-user"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Body
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"secretId": 29
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Deletes specified secret. User must have read-write access to the safe the secret is stored in.
|
||||||
|
|
||||||
|
Secret can be specified either by the secretId, or a unique combination of deviceName, deviceCategory and userName.
|
||||||
|
|
||||||
## Database Schema
|
## Database Schema
|
||||||

|

|
@@ -17,9 +17,10 @@ import (
|
|||||||
|
|
||||||
type AddUserInput struct {
|
type AddUserInput struct {
|
||||||
UserName string `json:"userName" binding:"required"`
|
UserName string `json:"userName" binding:"required"`
|
||||||
Password string `json:"password" binding:"required"`
|
Password string `json:"password"`
|
||||||
GroupId int `json:"groupId"`
|
GroupId int `json:"groupId"`
|
||||||
GroupName string `json:"groupName"`
|
GroupName string `json:"groupName"`
|
||||||
|
LdapUser bool `json:"ldapUser"`
|
||||||
//RoleId int `json:"roleid"`
|
//RoleId int `json:"roleid"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,9 +81,10 @@ func DeleteUser(c *gin.Context) {
|
|||||||
// Create audit record
|
// Create audit record
|
||||||
a := models.Audit{
|
a := models.Audit{
|
||||||
UserId: RequestingUserId,
|
UserId: RequestingUserId,
|
||||||
|
IpAddress: c.ClientIP(),
|
||||||
EventText: fmt.Sprintf("Deleted User Id %d", testUser.UserId),
|
EventText: fmt.Sprintf("Deleted User Id %d", testUser.UserId),
|
||||||
}
|
}
|
||||||
a.AuditAdd()
|
a.AuditLogAdd()
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "user deletion success"})
|
c.JSON(http.StatusOK, gin.H{"message": "user deletion success"})
|
||||||
}
|
}
|
||||||
@@ -98,12 +100,17 @@ func AddUser(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(input.UserName) == 0 {
|
if len(input.UserName) == 0 {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no username specified"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "username must be specified"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(input.Password) == 0 {
|
if len(input.Password) == 0 && !input.LdapUser {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no password specified"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "password must be specified for non-ldap user"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.LdapUser && len(input.Password) > 0 {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "password should not be specified for ldap user"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,6 +124,7 @@ func AddUser(c *gin.Context) {
|
|||||||
u := models.User{}
|
u := models.User{}
|
||||||
u.UserName = input.UserName
|
u.UserName = input.UserName
|
||||||
u.Password = input.Password
|
u.Password = input.Password
|
||||||
|
u.LdapUser = input.LdapUser
|
||||||
|
|
||||||
// Determine which GroupId to save
|
// Determine which GroupId to save
|
||||||
// Can be specified either by GroupName or GroupId in the request
|
// Can be specified either by GroupName or GroupId in the request
|
||||||
@@ -160,18 +168,20 @@ func AddUser(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//turn password into hash
|
//turn password into hash if defined
|
||||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
|
if len(input.Password) > 0 {
|
||||||
if err != nil {
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"Error hashing password": err.Error()})
|
if err != nil {
|
||||||
return
|
c.JSON(http.StatusBadRequest, gin.H{"Error hashing password": err.Error()})
|
||||||
} else {
|
return
|
||||||
//log.Printf("Register generated hashed password value '%s' from '%s'\n", string(hashedPassword), input.Password)
|
} else {
|
||||||
log.Printf("Register generated hashed password value '%s'\n", string(hashedPassword))
|
//log.Printf("Register generated hashed password value '%s' from '%s'\n", string(hashedPassword), input.Password)
|
||||||
|
log.Printf("Register generated hashed password value '%s'\n", string(hashedPassword))
|
||||||
|
}
|
||||||
|
u.Password = string(hashedPassword)
|
||||||
}
|
}
|
||||||
u.Password = string(hashedPassword)
|
|
||||||
|
|
||||||
_, err = u.SaveUser()
|
_, err := u.SaveUser()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"Error saving user": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"Error saving user": err.Error()})
|
||||||
@@ -181,9 +191,10 @@ func AddUser(c *gin.Context) {
|
|||||||
// Create audit record
|
// Create audit record
|
||||||
a := models.Audit{
|
a := models.Audit{
|
||||||
UserId: RequestingUserId,
|
UserId: RequestingUserId,
|
||||||
EventText: fmt.Sprintf("Created User Id %d", u.UserId),
|
IpAddress: c.ClientIP(),
|
||||||
|
EventText: fmt.Sprintf("Created User '%s' with id %d", u.UserName, u.UserId),
|
||||||
}
|
}
|
||||||
a.AuditAdd()
|
a.AuditLogAdd()
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "user registration success", "data": u})
|
c.JSON(http.StatusOK, gin.H{"message": "user registration success", "data": u})
|
||||||
}
|
}
|
||||||
@@ -210,7 +221,8 @@ func Login(c *gin.Context) {
|
|||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "username or password is incorrect."})
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "username or password is incorrect."})
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Login verified, returning token '%s'\n", token)
|
//log.Printf("Login verified, returning token '%s'\n", token)
|
||||||
|
log.Printf("Login verified, returning token\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"access_token": token})
|
c.JSON(http.StatusOK, gin.H{"access_token": token})
|
||||||
|
@@ -34,6 +34,7 @@ func GetGroupsHandler(c *gin.Context) {
|
|||||||
|
|
||||||
func AddGroupHandler(c *gin.Context) {
|
func AddGroupHandler(c *gin.Context) {
|
||||||
var input GroupInput
|
var input GroupInput
|
||||||
|
var RequestingUserId int
|
||||||
|
|
||||||
if err := c.ShouldBindJSON(&input); err != nil {
|
if err := c.ShouldBindJSON(&input); err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
@@ -50,6 +51,13 @@ func AddGroupHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if val, ok := c.Get("user-id"); !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "error determining user"})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
RequestingUserId = val.(int)
|
||||||
|
}
|
||||||
|
|
||||||
g := models.Group{}
|
g := models.Group{}
|
||||||
g.GroupName = input.GroupName
|
g.GroupName = input.GroupName
|
||||||
g.LdapGroup = input.LdapGroup
|
g.LdapGroup = input.LdapGroup
|
||||||
@@ -90,6 +98,14 @@ func AddGroupHandler(c *gin.Context) {
|
|||||||
// Verification checks passed, return group
|
// Verification checks passed, return group
|
||||||
group, err := g.GroupAdd()
|
group, err := g.GroupAdd()
|
||||||
|
|
||||||
|
// Create audit record
|
||||||
|
a := models.Audit{
|
||||||
|
UserId: RequestingUserId,
|
||||||
|
IpAddress: c.ClientIP(),
|
||||||
|
EventText: fmt.Sprintf("Created Group '%s' with id %d", g.GroupName, g.GroupId),
|
||||||
|
}
|
||||||
|
a.AuditLogAdd()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errString := fmt.Sprintf("error creating group : '%s'", err)
|
errString := fmt.Sprintf("error creating group : '%s'", err)
|
||||||
log.Printf("AddGroupHandler %s\n", errString)
|
log.Printf("AddGroupHandler %s\n", errString)
|
||||||
@@ -102,6 +118,7 @@ func AddGroupHandler(c *gin.Context) {
|
|||||||
|
|
||||||
func DeleteGroupHandler(c *gin.Context) {
|
func DeleteGroupHandler(c *gin.Context) {
|
||||||
var input GroupInput
|
var input GroupInput
|
||||||
|
var RequestingUserId int
|
||||||
|
|
||||||
if err := c.ShouldBindJSON(&input); err != nil {
|
if err := c.ShouldBindJSON(&input); err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
@@ -114,6 +131,13 @@ func DeleteGroupHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if val, ok := c.Get("user-id"); !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "error determining user"})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
RequestingUserId = val.(int)
|
||||||
|
}
|
||||||
|
|
||||||
g := models.Group{}
|
g := models.Group{}
|
||||||
g.GroupId = input.GroupId
|
g.GroupId = input.GroupId
|
||||||
g.GroupName = input.GroupName
|
g.GroupName = input.GroupName
|
||||||
@@ -152,6 +176,14 @@ func DeleteGroupHandler(c *gin.Context) {
|
|||||||
|
|
||||||
err := g.GroupDelete()
|
err := g.GroupDelete()
|
||||||
|
|
||||||
|
// Create audit record
|
||||||
|
a := models.Audit{
|
||||||
|
UserId: RequestingUserId,
|
||||||
|
IpAddress: c.ClientIP(),
|
||||||
|
EventText: fmt.Sprintf("Deleted Group '%s' with id %d", g.GroupName, g.GroupId),
|
||||||
|
}
|
||||||
|
a.AuditLogAdd()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errString := fmt.Sprintf("error deleting group : '%s'", err)
|
errString := fmt.Sprintf("error deleting group : '%s'", err)
|
||||||
log.Printf("DeleteGroupHandler %s\n", errString)
|
log.Printf("DeleteGroupHandler %s\n", errString)
|
||||||
|
@@ -6,6 +6,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"smt/models"
|
"smt/models"
|
||||||
|
"smt/utils"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -35,6 +36,7 @@ func GetPermissionsHandler(c *gin.Context) {
|
|||||||
|
|
||||||
func AddPermissionHandler(c *gin.Context) {
|
func AddPermissionHandler(c *gin.Context) {
|
||||||
var input PermissionInput
|
var input PermissionInput
|
||||||
|
var RequestingUserId int
|
||||||
|
|
||||||
if err := c.ShouldBindJSON(&input); err != nil {
|
if err := c.ShouldBindJSON(&input); err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
@@ -55,6 +57,13 @@ func AddPermissionHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if val, ok := c.Get("user-id"); !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "error determining user"})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
RequestingUserId = val.(int)
|
||||||
|
}
|
||||||
|
|
||||||
p := models.Permission{
|
p := models.Permission{
|
||||||
PermissionId: input.PermissionId,
|
PermissionId: input.PermissionId,
|
||||||
Description: input.Description,
|
Description: input.Description,
|
||||||
@@ -82,6 +91,14 @@ func AddPermissionHandler(c *gin.Context) {
|
|||||||
|
|
||||||
_, err := p.PermissionAdd()
|
_, err := p.PermissionAdd()
|
||||||
|
|
||||||
|
// Create audit record
|
||||||
|
a := models.Audit{
|
||||||
|
UserId: RequestingUserId,
|
||||||
|
IpAddress: c.ClientIP(),
|
||||||
|
EventText: fmt.Sprintf("Created Permission '%s' with id %d on safe id %d for group id %d or user id %d", p.Description, p.PermissionId, p.SafeId, p.GroupId, p.UserId),
|
||||||
|
}
|
||||||
|
a.AuditLogAdd()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errString := fmt.Sprintf("error creating permission : '%s'", err)
|
errString := fmt.Sprintf("error creating permission : '%s'", err)
|
||||||
log.Printf("AddPermissionHandler %s\n", errString)
|
log.Printf("AddPermissionHandler %s\n", errString)
|
||||||
@@ -94,6 +111,7 @@ func AddPermissionHandler(c *gin.Context) {
|
|||||||
|
|
||||||
func DeletePermissionHandler(c *gin.Context) {
|
func DeletePermissionHandler(c *gin.Context) {
|
||||||
var input PermissionInput
|
var input PermissionInput
|
||||||
|
var RequestingUserId int
|
||||||
|
|
||||||
if err := c.ShouldBindJSON(&input); err != nil {
|
if err := c.ShouldBindJSON(&input); err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
@@ -108,6 +126,13 @@ func DeletePermissionHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if val, ok := c.Get("user-id"); !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "error determining user"})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
RequestingUserId = val.(int)
|
||||||
|
}
|
||||||
|
|
||||||
p := models.Permission{
|
p := models.Permission{
|
||||||
PermissionId: input.PermissionId,
|
PermissionId: input.PermissionId,
|
||||||
Description: input.Description,
|
Description: input.Description,
|
||||||
@@ -121,23 +146,122 @@ func DeletePermissionHandler(c *gin.Context) {
|
|||||||
p.Description = html.EscapeString(strings.TrimSpace(p.Description))
|
p.Description = html.EscapeString(strings.TrimSpace(p.Description))
|
||||||
|
|
||||||
// Check if permission definition already exists
|
// Check if permission definition already exists
|
||||||
testPermission, _ := models.PermissionGetByDesc(p.Description)
|
if len(p.Description) > 0 {
|
||||||
log.Printf("DeletePermissionHandler confirming permission with description '%s' exists\n", p.Description)
|
log.Printf("DeletePermissionHandler confirming permission with description '%s' exists\n", p.Description)
|
||||||
if (models.Permission{} == testPermission) {
|
testPermission, _ := models.PermissionGetByDesc(p.Description)
|
||||||
errString := fmt.Sprintf("attempt to delete non-existing permission with description '%s'", p.Description)
|
|
||||||
log.Printf("DeletePermissionHandler %s\n", errString)
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
err := p.PermissionDelete()
|
|
||||||
|
|
||||||
if err != nil {
|
if (models.Permission{} == testPermission) {
|
||||||
errString := fmt.Sprintf("error deleting permission : '%s'", err)
|
errString := fmt.Sprintf("attempt to delete non-existing permission with description '%s'", p.Description)
|
||||||
log.Printf("DeletePermissionHandler %s\n", errString)
|
log.Printf("DeletePermissionHandler %s\n", errString)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("DeletePermissionHandler confirming permission with id '%d' exists\n", p.PermissionId)
|
||||||
|
testPermission, _ := models.PermissionGetById(p.PermissionId)
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "permission deletion success"})
|
if (models.Permission{} == testPermission) {
|
||||||
|
errString := fmt.Sprintf("attempt to delete non-existing permission with id '%d'", p.PermissionId)
|
||||||
|
log.Printf("DeletePermissionHandler %s\n", errString)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := p.PermissionDelete()
|
||||||
|
|
||||||
|
// Create audit record
|
||||||
|
a := models.Audit{
|
||||||
|
UserId: RequestingUserId,
|
||||||
|
IpAddress: c.ClientIP(),
|
||||||
|
EventText: fmt.Sprintf("Deleted Permission '%s' with id %d", p.Description, p.PermissionId),
|
||||||
|
}
|
||||||
|
a.AuditLogAdd()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errString := fmt.Sprintf("error deleting permission : '%s'", err)
|
||||||
|
log.Printf("DeletePermissionHandler %s\n", errString)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"message": "permission deletion success"})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdatePermissionHandler(c *gin.Context) {
|
||||||
|
var input PermissionInput
|
||||||
|
var RequestingUserId int
|
||||||
|
|
||||||
|
if err := c.ShouldBindJSON(&input); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input validation
|
||||||
|
if input.PermissionId == 0 {
|
||||||
|
errString := "must specify permission id"
|
||||||
|
log.Printf("UpdatePermissionHandler %s\n", errString)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, ok := c.Get("user-id"); !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "error determining user"})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
RequestingUserId = val.(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check specified permission currently exists
|
||||||
|
currentPermission, err := models.PermissionGetById(input.PermissionId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errString := fmt.Sprintf("error querying existing permission : '%s'", err)
|
||||||
|
log.Printf("UpdatePermissionHandler %s\n", errString)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (models.Permission{} == currentPermission) {
|
||||||
|
errString := fmt.Sprintf("no permission id '%d' found", input.PermissionId)
|
||||||
|
log.Printf("UpdatePermissionHandler %s\n", errString)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new struct with values supplied by user
|
||||||
|
newPermission := models.Permission{
|
||||||
|
PermissionId: input.PermissionId,
|
||||||
|
Description: input.Description,
|
||||||
|
ReadOnly: input.ReadOnly,
|
||||||
|
SafeId: input.SafeId,
|
||||||
|
UserId: input.UserId,
|
||||||
|
GroupId: input.GroupId,
|
||||||
|
}
|
||||||
|
|
||||||
|
//remove leading/trailing spaces in permission description
|
||||||
|
newPermission.Description = html.EscapeString(strings.TrimSpace(newPermission.Description))
|
||||||
|
|
||||||
|
// Copy newPermission into currentPermission
|
||||||
|
utils.UpdateStruct(¤tPermission, &newPermission)
|
||||||
|
|
||||||
|
// run the database update
|
||||||
|
_, err = currentPermission.PermissionUpdate()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errString := fmt.Sprintf("error updating permission : '%s'", err)
|
||||||
|
log.Printf("UpdatePermissionHandler %s\n", errString)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//create audit record
|
||||||
|
a := models.Audit{
|
||||||
|
UserId: RequestingUserId,
|
||||||
|
IpAddress: c.ClientIP(),
|
||||||
|
EventText: fmt.Sprintf("Updated Permission '%s' with id %d", currentPermission.Description, currentPermission.PermissionId),
|
||||||
|
}
|
||||||
|
a.AuditLogAdd()
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"message": "permission update success", "data": currentPermission})
|
||||||
}
|
}
|
||||||
|
23
controllers/retrieveAudits.go
Normal file
23
controllers/retrieveAudits.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"smt/models"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetAuditLogsHandler(c *gin.Context) {
|
||||||
|
logs, err := models.AuditLogList()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errString := fmt.Sprintf("error retrieving audit logs : '%s'", err)
|
||||||
|
log.Printf("GetAuditLogsHandler %s\n", errString)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"message": "success", "data": logs})
|
||||||
|
}
|
@@ -5,11 +5,13 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"smt/models"
|
"smt/models"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RetrieveInput struct {
|
type RetrieveInput struct {
|
||||||
|
SecretId int `json:"secretId"`
|
||||||
DeviceName string `json:"deviceName"`
|
DeviceName string `json:"deviceName"`
|
||||||
DeviceCategory string `json:"deviceCategory"`
|
DeviceCategory string `json:"deviceCategory"`
|
||||||
UserName string `json:"userName"`
|
UserName string `json:"userName"`
|
||||||
@@ -18,42 +20,43 @@ type RetrieveInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ListSecret struct {
|
type ListSecret struct {
|
||||||
SecretId int `db:"SecretId" json:"secretId"`
|
SecretId int `db:"SecretId" json:"secretId"`
|
||||||
SafeId int `db:"SafeId" json:"safeId"`
|
SafeId int `db:"SafeId" json:"safeId"`
|
||||||
DeviceName string `db:"DeviceName" json:"deviceName"`
|
DeviceName string `db:"DeviceName" json:"deviceName"`
|
||||||
DeviceCategory string `db:"DeviceCategory" json:"deviceCategory"`
|
DeviceCategory string `db:"DeviceCategory" json:"deviceCategory"`
|
||||||
UserName string `db:"UserName" json:"userName"`
|
UserName string `db:"UserName" json:"userName"`
|
||||||
Secret string `db:"Secret" json:"-"`
|
Secret string `db:"Secret" json:"-"`
|
||||||
|
LastUpdated time.Time `db:"LastUpdated" json:"lastUpdated"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func RetrieveSecret(c *gin.Context) {
|
func RetrieveSecret(c *gin.Context) {
|
||||||
var input RetrieveInput
|
var input RetrieveInput
|
||||||
//var results []models.Secret
|
|
||||||
//var userIsAdmin bool = false
|
|
||||||
|
|
||||||
// Validate the input matches our struct
|
// Validate the input matches our struct
|
||||||
if err := c.ShouldBindJSON(&input); err != nil {
|
if err := c.ShouldBindJSON(&input); err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Printf("RetrieveSecret received JSON input '%v'\n", input)
|
//log.Printf("RetrieveSecret received JSON input '%v'\n", input)
|
||||||
|
|
||||||
/*
|
|
||||||
// Get the user and role id of the requestor
|
|
||||||
u, err := models.UserGetRoleFromToken(c)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Populate fields
|
// Populate fields
|
||||||
s := models.Secret{}
|
s := models.Secret{}
|
||||||
//s.RoleId = u.RoleId
|
|
||||||
s.DeviceName = input.DeviceName
|
s.DeviceName = input.DeviceName
|
||||||
s.DeviceCategory = input.DeviceCategory
|
s.DeviceCategory = input.DeviceCategory
|
||||||
s.UserName = input.UserName
|
s.UserName = input.UserName
|
||||||
|
|
||||||
|
if input.SecretId > 0 {
|
||||||
|
s.SecretId = input.SecretId
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.DeviceName == "" && input.DeviceCategory == "" && input.UserName == "" && input.SecretId == 0 {
|
||||||
|
errString := "no values provided to select secret"
|
||||||
|
log.Printf("RetrieveSecret %s\n", errString)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
retrieveSpecifiedSecret(&s, c)
|
retrieveSpecifiedSecret(&s, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +64,9 @@ func RetrieveSecretByDevicename(c *gin.Context) {
|
|||||||
DeviceName := c.Param("devicename")
|
DeviceName := c.Param("devicename")
|
||||||
|
|
||||||
if DeviceName == "" {
|
if DeviceName == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no devicename value specified"})
|
errString := "no devicename value specified"
|
||||||
|
log.Printf("RetrieveSecretByDevicename %s\n", errString)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +81,9 @@ func RetrieveSecretByDevicecategory(c *gin.Context) {
|
|||||||
DeviceCategory := c.Param("devicecategory")
|
DeviceCategory := c.Param("devicecategory")
|
||||||
|
|
||||||
if DeviceCategory == "" {
|
if DeviceCategory == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no devicecategory value specified"})
|
errString := "no devicecategory value specified"
|
||||||
|
log.Printf("RetrieveSecretByDevicecategory %s\n", errString)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,34 +93,31 @@ func RetrieveSecretByDevicecategory(c *gin.Context) {
|
|||||||
retrieveSpecifiedSecret(&s, c)
|
retrieveSpecifiedSecret(&s, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func retrieveSpecifiedSecret(s *models.Secret, c *gin.Context) {
|
func RetrieveSecretByUsername(c *gin.Context) {
|
||||||
/*
|
userName := c.Param("username")
|
||||||
// Get the user and role id of the requestor
|
|
||||||
u, err := models.UserGetRoleFromToken(c)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.RoleId = u.RoleId
|
|
||||||
|
|
||||||
results, err := models.GetSecrets(s, false)
|
if userName == "" {
|
||||||
if err != nil {
|
errString := "no username value specified"
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
log.Printf("RetrieveSecretByUsername %s\n", errString)
|
||||||
return
|
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
||||||
}
|
return
|
||||||
*/
|
}
|
||||||
|
|
||||||
|
// Create object based on specified data
|
||||||
|
s := models.Secret{UserName: userName}
|
||||||
|
|
||||||
|
retrieveSpecifiedSecret(&s, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func retrieveSpecifiedSecret(s *models.Secret, c *gin.Context) {
|
||||||
var UserId int
|
var UserId int
|
||||||
var results []models.Secret
|
var results []models.Secret
|
||||||
/*
|
|
||||||
user_id, err := token.ExtractTokenID(c)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "error determining user"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// Get userId that we stored in the context earlier
|
// Get userId that we stored in the context earlier
|
||||||
if val, ok := c.Get("user-id"); !ok {
|
if val, ok := c.Get("user-id"); !ok {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "error determining user"})
|
errString := "error determining user"
|
||||||
|
log.Printf("retrieveSpecifiedSecret %s\n", errString)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
UserId = val.(int)
|
UserId = val.(int)
|
||||||
@@ -123,7 +127,9 @@ func retrieveSpecifiedSecret(s *models.Secret, c *gin.Context) {
|
|||||||
safeList, err := models.UserGetSafesAllowed(int(UserId))
|
safeList, err := models.UserGetSafesAllowed(int(UserId))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "error determining user safes"})
|
errString := "error determining user safes"
|
||||||
|
log.Printf("retrieveSpecifiedSecret %s\n", errString)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,29 +157,29 @@ func retrieveSpecifiedSecret(s *models.Secret, c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(results) == 1 {
|
if len(results) == 0 {
|
||||||
|
|
||||||
// Create audit record
|
|
||||||
a := models.Audit{
|
|
||||||
UserId: UserId,
|
|
||||||
EventText: fmt.Sprintf("Retrieved Secret Id %d", results[0].SecretId),
|
|
||||||
}
|
|
||||||
a.AuditAdd()
|
|
||||||
|
|
||||||
// output results as json
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "success", "data": results})
|
|
||||||
} else if len(results) > 1 {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "found multiple matching secrets"})
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "found no matching secrets"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "found no matching secrets"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create audit record for results
|
||||||
|
for i := range results {
|
||||||
|
a := models.Audit{
|
||||||
|
UserId: UserId,
|
||||||
|
SecretId: results[i].SecretId,
|
||||||
|
IpAddress: c.ClientIP(),
|
||||||
|
EventText: fmt.Sprintf("User '%s' retrieved SecretId %d", safeList[0].User.UserName, results[i].SecretId),
|
||||||
|
}
|
||||||
|
a.AuditLogAdd()
|
||||||
|
}
|
||||||
|
|
||||||
|
// output results as json
|
||||||
|
c.JSON(http.StatusOK, gin.H{"message": "success", "data": results, "count": len(results)})
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListSecrets(c *gin.Context) {
|
func ListSecrets(c *gin.Context) {
|
||||||
var UserId int
|
var UserId int
|
||||||
var output []ListSecret
|
var results []ListSecret
|
||||||
|
|
||||||
//var results []models.Secret
|
//var results []models.Secret
|
||||||
s := models.Secret{}
|
s := models.Secret{}
|
||||||
@@ -196,61 +202,18 @@ func ListSecrets(c *gin.Context) {
|
|||||||
|
|
||||||
// Extract the normal secret fields from the allowed list
|
// Extract the normal secret fields from the allowed list
|
||||||
for _, secret := range secretList {
|
for _, secret := range secretList {
|
||||||
output = append(output, ListSecret(secret.Secret))
|
results = append(results, ListSecret(secret.Secret))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create audit record
|
// Create audit record
|
||||||
a := models.Audit{
|
a := models.Audit{
|
||||||
UserId: UserId,
|
UserId: UserId,
|
||||||
EventText: fmt.Sprintf("Listed %d secrets, %+v", len(output), s),
|
IpAddress: c.ClientIP(),
|
||||||
|
EventText: fmt.Sprintf("Listed %d secrets accessible to user", len(results)),
|
||||||
}
|
}
|
||||||
a.AuditAdd()
|
a.AuditLogAdd()
|
||||||
|
|
||||||
// output results as json
|
// output results as json
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "success", "data": output})
|
c.JSON(http.StatusOK, gin.H{"message": "success", "data": results, "count": len(results)})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func RetrieveMultpleSecrets(c *gin.Context) {
|
|
||||||
// TODO implement with new schema
|
|
||||||
/*
|
|
||||||
var input RetrieveInput
|
|
||||||
|
|
||||||
// Validate the input matches our struct
|
|
||||||
if err := c.ShouldBindJSON(&input); err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Printf("StoreSecret received JSON input '%v'\n", input)
|
|
||||||
|
|
||||||
// Get the user and role id of the requestor
|
|
||||||
user_id, err := token.ExtractTokenID(c)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := models.GetUserRoleByID(user_id)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate fields
|
|
||||||
s := models.Secret{}
|
|
||||||
s.RoleId = u.RoleId
|
|
||||||
s.DeviceName = input.DeviceName
|
|
||||||
s.DeviceCategory = input.DeviceCategory
|
|
||||||
|
|
||||||
results, err := models.GetSecrets(&s, false)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// output results as json
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "success", "data": results})
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
@@ -14,6 +14,7 @@ import (
|
|||||||
type SecretInput struct {
|
type SecretInput struct {
|
||||||
SafeId int `json:"safeId"`
|
SafeId int `json:"safeId"`
|
||||||
SafeName string `json:"safeName"`
|
SafeName string `json:"safeName"`
|
||||||
|
SecretId int `json:"secretId"`
|
||||||
DeviceName string `json:"deviceName"`
|
DeviceName string `json:"deviceName"`
|
||||||
DeviceCategory string `json:"deviceCategory"`
|
DeviceCategory string `json:"deviceCategory"`
|
||||||
UserName string `json:"userName"`
|
UserName string `json:"userName"`
|
||||||
@@ -146,9 +147,11 @@ func StoreSecret(c *gin.Context) {
|
|||||||
// Create audit record
|
// Create audit record
|
||||||
a := models.Audit{
|
a := models.Audit{
|
||||||
UserId: UserId,
|
UserId: UserId,
|
||||||
|
SecretId: s.SecretId,
|
||||||
|
IpAddress: c.ClientIP(),
|
||||||
EventText: fmt.Sprintf("Created Secret Id %d", s.SecretId),
|
EventText: fmt.Sprintf("Created Secret Id %d", s.SecretId),
|
||||||
}
|
}
|
||||||
a.AuditAdd()
|
a.AuditLogAdd()
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "secret stored successfully", "data": models.SecretRestricted(s)})
|
c.JSON(http.StatusOK, gin.H{"message": "secret stored successfully", "data": models.SecretRestricted(s)})
|
||||||
}
|
}
|
||||||
@@ -254,16 +257,6 @@ func UpdateSecret(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporarily disable because we should be able to figure it out without user specifying
|
|
||||||
/*
|
|
||||||
if input.SafeId == 0 && len(input.SafeName) == 0 {
|
|
||||||
errString := "UpdateSecret no safe specified\n"
|
|
||||||
log.Print(errString)
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": errString})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Get userId that we stored in the context earlier
|
// Get userId that we stored in the context earlier
|
||||||
if val, ok := c.Get("user-id"); !ok {
|
if val, ok := c.Get("user-id"); !ok {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "error determining user"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "error determining user"})
|
||||||
@@ -367,9 +360,11 @@ func UpdateSecret(c *gin.Context) {
|
|||||||
// Create audit record
|
// Create audit record
|
||||||
a := models.Audit{
|
a := models.Audit{
|
||||||
UserId: UserId,
|
UserId: UserId,
|
||||||
|
SecretId: s.SecretId,
|
||||||
|
IpAddress: c.ClientIP(),
|
||||||
EventText: fmt.Sprintf("Updated Secret Id %d", s.SecretId),
|
EventText: fmt.Sprintf("Updated Secret Id %d", s.SecretId),
|
||||||
}
|
}
|
||||||
a.AuditAdd()
|
a.AuditLogAdd()
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "secret updated successfully", "data": models.SecretRestricted(s)})
|
c.JSON(http.StatusOK, gin.H{"message": "secret updated successfully", "data": models.SecretRestricted(s)})
|
||||||
} else {
|
} else {
|
||||||
@@ -402,6 +397,10 @@ func DeleteSecret(c *gin.Context) {
|
|||||||
// Populate fields
|
// Populate fields
|
||||||
s := models.Secret{}
|
s := models.Secret{}
|
||||||
|
|
||||||
|
if input.SecretId > 0 {
|
||||||
|
s.SecretId = input.SecretId
|
||||||
|
}
|
||||||
|
|
||||||
s.UserName = input.UserName
|
s.UserName = input.UserName
|
||||||
s.DeviceName = input.DeviceName
|
s.DeviceName = input.DeviceName
|
||||||
s.DeviceCategory = input.DeviceCategory
|
s.DeviceCategory = input.DeviceCategory
|
||||||
@@ -453,9 +452,11 @@ func DeleteSecret(c *gin.Context) {
|
|||||||
// Create audit record
|
// Create audit record
|
||||||
a := models.Audit{
|
a := models.Audit{
|
||||||
UserId: UserId,
|
UserId: UserId,
|
||||||
|
SecretId: s.SecretId,
|
||||||
|
IpAddress: c.ClientIP(),
|
||||||
EventText: fmt.Sprintf("Deleted Secret Id %d", s.SecretId),
|
EventText: fmt.Sprintf("Deleted Secret Id %d", s.SecretId),
|
||||||
}
|
}
|
||||||
a.AuditAdd()
|
a.AuditLogAdd()
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "secret deleted successfully"})
|
c.JSON(http.StatusOK, gin.H{"message": "secret deleted successfully"})
|
||||||
} else {
|
} else {
|
||||||
|
77
go.mod
77
go.mod
@@ -1,54 +1,57 @@
|
|||||||
module smt
|
module smt
|
||||||
|
|
||||||
go 1.19
|
go 1.24.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/gin-gonic/gin v1.10.1
|
||||||
github.com/gin-gonic/gin v1.9.0
|
github.com/go-ldap/ldap/v3 v3.4.11
|
||||||
github.com/go-ldap/ldap v3.0.3+incompatible
|
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||||
github.com/jmoiron/sqlx v1.3.5
|
github.com/jmoiron/sqlx v1.4.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
golang.org/x/crypto v0.13.0
|
golang.org/x/crypto v0.39.0
|
||||||
modernc.org/sqlite v1.21.0
|
modernc.org/sqlite v1.38.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bytedance/sonic v1.8.6 // indirect
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
github.com/bytedance/sonic v1.13.3 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||||
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||||
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.12.0 // indirect
|
github.com/go-playground/validator/v10 v10.27.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
github.com/google/uuid v1.3.1 // indirect
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.11 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/leodido/go-urn v1.2.2 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||||
golang.org/x/arch v0.3.0 // indirect
|
golang.org/x/arch v0.18.0 // indirect
|
||||||
golang.org/x/mod v0.8.0 // indirect
|
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
|
||||||
golang.org/x/net v0.10.0 // indirect
|
golang.org/x/net v0.41.0 // indirect
|
||||||
golang.org/x/sys v0.12.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
golang.org/x/text v0.13.0 // indirect
|
golang.org/x/text v0.26.0 // indirect
|
||||||
golang.org/x/tools v0.6.0 // indirect
|
golang.org/x/tools v0.34.0 // indirect
|
||||||
google.golang.org/protobuf v1.30.0 // indirect
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
lukechampine.com/uint128 v1.2.0 // indirect
|
modernc.org/gc/v3 v3.1.0 // indirect
|
||||||
modernc.org/cc/v3 v3.40.0 // indirect
|
modernc.org/libc v1.66.2 // indirect
|
||||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
modernc.org/libc v1.22.3 // indirect
|
modernc.org/memory v1.11.0 // indirect
|
||||||
modernc.org/mathutil v1.5.0 // indirect
|
modernc.org/strutil v1.2.1 // indirect
|
||||||
modernc.org/memory v1.5.0 // indirect
|
modernc.org/token v1.1.0 // indirect
|
||||||
modernc.org/opt v0.1.3 // indirect
|
|
||||||
modernc.org/strutil v1.1.3 // indirect
|
|
||||||
modernc.org/token v1.0.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
265
go.sum
265
go.sum
@@ -1,139 +1,216 @@
|
|||||||
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
|
||||||
github.com/bytedance/sonic v1.8.6 h1:aUgO9S8gvdN6SyW2EhIpAw5E4ChworywIEndZCkCVXk=
|
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||||
github.com/bytedance/sonic v1.8.6/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||||
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
|
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
|
||||||
|
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
|
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
||||||
|
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||||
|
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||||
|
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
|
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||||
|
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
|
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||||
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8=
|
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||||
github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k=
|
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||||
github.com/go-ldap/ldap v3.0.3+incompatible h1:HTeSZO8hWMS1Rgb2Ziku6b8a7qRIZZMHjsvuZyatzwk=
|
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||||
github.com/go-ldap/ldap v3.0.3+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
|
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||||
|
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||||
|
github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
|
||||||
|
github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI=
|
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||||
github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA=
|
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||||
|
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
|
||||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
|
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
||||||
|
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||||
|
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
||||||
|
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
|
||||||
|
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
|
||||||
|
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
|
||||||
|
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
|
||||||
|
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
|
||||||
|
github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=
|
||||||
|
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
|
||||||
|
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
|
||||||
|
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||||
|
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
||||||
|
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4=
|
github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
|
||||||
github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ=
|
github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us=
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
|
||||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
|
||||||
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
|
||||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
|
||||||
|
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
||||||
|
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||||
|
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
|
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||||
|
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||||
|
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||||
|
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||||
|
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||||
|
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=
|
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
|
||||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||||
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
|
modernc.org/ccgo/v4 v4.20.7 h1:skrinQsjxWfvj6nbC3ztZPJy+NuwmB3hV9zX/pthNYQ=
|
||||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
modernc.org/ccgo/v4 v4.20.7/go.mod h1:UOkI3JSG2zT4E2ioHlncSOZsXbuDCZLvPi3uMlZT5GY=
|
||||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
modernc.org/gc/v2 v2.5.0 h1:bJ9ChznK1L1mUtAQtxi0wi5AtAs5jQuw4PrPHO5pb6M=
|
||||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
modernc.org/gc/v2 v2.5.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||||
modernc.org/libc v1.22.3 h1:D/g6O5ftAfavceqlLOFwaZuA5KYafKwmr30A6iSqoyY=
|
modernc.org/gc/v3 v3.0.0-20240801135723-a856999a2e4a h1:CfbpOLEo2IwNzJdMvE8aiRbPMxoTpgAJeyePh0SmO8M=
|
||||||
modernc.org/libc v1.22.3/go.mod h1:MQrloYP209xa2zHome2a8HLiLm6k0UT8CoHpV74tOFw=
|
modernc.org/gc/v3 v3.0.0-20240801135723-a856999a2e4a/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
modernc.org/gc/v3 v3.1.0 h1:CiObI+9ROz7pjjH3iAgMPaFCN5zE3sN5KF4jet8BWdc=
|
||||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
modernc.org/gc/v3 v3.1.0/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
modernc.org/libc v1.59.9 h1:k+nNDDakwipimgmJ1D9H466LhFeSkaPPycAs1OZiDmY=
|
||||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
modernc.org/libc v1.59.9/go.mod h1:EY/egGEU7Ju66eU6SBqCNYaFUDuc4npICkMWnU5EE3A=
|
||||||
|
modernc.org/libc v1.66.2 h1:JCBxlJzZOIwZY54fzjHN3Wsn8Ty5PUTPr/xioRkmecI=
|
||||||
|
modernc.org/libc v1.66.2/go.mod h1:ceIGzvXxP+JV3pgVjP9avPZo6Chlsfof2egXBH3YT5Q=
|
||||||
|
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||||
|
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||||
|
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||||
|
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||||
|
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
|
||||||
|
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
|
||||||
|
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||||
|
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||||
modernc.org/sqlite v1.21.0 h1:4aP4MdUf15i3R3M2mx6Q90WHKz3nZLoz96zlB6tNdow=
|
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||||
modernc.org/sqlite v1.21.0/go.mod h1:XwQ0wZPIh1iKb5mkvCJ3szzbhk+tykC8ZWqTRTgYRwI=
|
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
|
||||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
modernc.org/sqlite v1.32.0 h1:6BM4uGza7bWypsw4fdLRsLxut6bHe4c58VeqjRgST8s=
|
||||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA=
|
||||||
modernc.org/tcl v1.15.1 h1:mOQwiEK4p7HruMZcwKTZPw/aqtGM4aY00uzWhlKKYws=
|
modernc.org/sqlite v1.38.0 h1:+4OrfPQ8pxHKuWG4md1JpR/EYAh3Md7TdejuuzE7EUI=
|
||||||
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
modernc.org/sqlite v1.38.0/go.mod h1:1Bj+yES4SVvBZ4cBOpVZ6QgesMCKpJZDq0nxYzOpmNE=
|
||||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||||
modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE=
|
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||||
|
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||||
|
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||||
|
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||||
|
18
main.go
18
main.go
@@ -11,6 +11,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"runtime"
|
||||||
"smt/controllers"
|
"smt/controllers"
|
||||||
"smt/middlewares"
|
"smt/middlewares"
|
||||||
"smt/models"
|
"smt/models"
|
||||||
@@ -100,6 +101,7 @@ func main() {
|
|||||||
// These replacements are for the embedded html generated from README.md
|
// These replacements are for the embedded html generated from README.md
|
||||||
replacements["{SHA1VER}"] = sha1ver
|
replacements["{SHA1VER}"] = sha1ver
|
||||||
replacements["{BUILDTIME}"] = buildTime
|
replacements["{BUILDTIME}"] = buildTime
|
||||||
|
replacements["{RUNTIME}"] = runtime.Version()
|
||||||
|
|
||||||
// Load data from environment file
|
// Load data from environment file
|
||||||
envFilename := utils.GetFilePath(".env")
|
envFilename := utils.GetFilePath(".env")
|
||||||
@@ -121,7 +123,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.SetOutput(logfileWriter)
|
log.SetOutput(logfileWriter)
|
||||||
log.Printf("SMT starting execution. Built on %s from sha1 %s\n", buildTime, sha1ver)
|
log.Printf("SMT starting execution. Built on %s from sha1 %s. Runtime %s\n", buildTime, sha1ver, runtime.Version())
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// for debugging, list all the files that we embedded at compile time
|
// for debugging, list all the files that we embedded at compile time
|
||||||
@@ -199,7 +201,7 @@ func main() {
|
|||||||
// Determine bind port
|
// Determine bind port
|
||||||
bindPort := os.Getenv("BIND_PORT")
|
bindPort := os.Getenv("BIND_PORT")
|
||||||
if bindPort == "" {
|
if bindPort == "" {
|
||||||
bindIP = "8443"
|
bindPort = "8443"
|
||||||
}
|
}
|
||||||
bindAddress := fmt.Sprint(bindIP, ":", bindPort)
|
bindAddress := fmt.Sprint(bindIP, ":", bindPort)
|
||||||
log.Printf("Will listen on address 'https://%s'\n", bindAddress)
|
log.Printf("Will listen on address 'https://%s'\n", bindAddress)
|
||||||
@@ -248,19 +250,20 @@ func main() {
|
|||||||
adminOnly.POST("/user/add", controllers.AddUser)
|
adminOnly.POST("/user/add", controllers.AddUser)
|
||||||
adminOnly.GET("/users", controllers.GetUsers)
|
adminOnly.GET("/users", controllers.GetUsers)
|
||||||
// TODO
|
// TODO
|
||||||
//adminOnly.POST("/user/update", controllers.UpdateUser)
|
//adminOnly.POST("/user/update", controllers.UpdateUserHandler)
|
||||||
|
|
||||||
// Group functions for admin
|
// Group functions for admin
|
||||||
adminOnly.GET("/groups", controllers.GetGroupsHandler)
|
adminOnly.GET("/groups", controllers.GetGroupsHandler)
|
||||||
adminOnly.POST("/group/add", controllers.AddGroupHandler)
|
adminOnly.POST("/group/add", controllers.AddGroupHandler)
|
||||||
// TODO
|
// TODO
|
||||||
//adminOnly.POST("/group/update", controllers.UpdateGroup)
|
//adminOnly.POST("/group/update", controllers.UpdateGroupHandler)
|
||||||
adminOnly.POST("/group/delete", controllers.DeleteGroupHandler)
|
adminOnly.POST("/group/delete", controllers.DeleteGroupHandler)
|
||||||
|
|
||||||
// Permission functions for admin
|
// Permission functions for admin
|
||||||
adminOnly.GET("/permissions", controllers.GetPermissionsHandler)
|
adminOnly.GET("/permissions", controllers.GetPermissionsHandler)
|
||||||
adminOnly.POST("/permission/add", controllers.AddPermissionHandler)
|
adminOnly.POST("/permission/add", controllers.AddPermissionHandler)
|
||||||
adminOnly.POST("/permission/delete", controllers.DeletePermissionHandler)
|
adminOnly.POST("/permission/delete", controllers.DeletePermissionHandler)
|
||||||
|
adminOnly.POST("/permission/update", controllers.UpdatePermissionHandler)
|
||||||
|
|
||||||
// Safe functions for admin
|
// Safe functions for admin
|
||||||
adminOnly.GET("/safe/listall", controllers.GetAllSafesHandler)
|
adminOnly.GET("/safe/listall", controllers.GetAllSafesHandler)
|
||||||
@@ -269,6 +272,10 @@ func main() {
|
|||||||
|
|
||||||
// Other functions for admin
|
// Other functions for admin
|
||||||
adminOnly.POST("/unlock", controllers.Unlock)
|
adminOnly.POST("/unlock", controllers.Unlock)
|
||||||
|
adminOnly.GET("/logs", controllers.GetAuditLogsHandler)
|
||||||
|
// TODO
|
||||||
|
//adminOnly.GET("/logs/secret/:id", controllers.GetAuditLogsBySecretHandler)
|
||||||
|
//adminOnly.GET("/logs/user/:id", controllers.GetAuditLogsByUserHandler)
|
||||||
|
|
||||||
// Get secrets
|
// Get secrets
|
||||||
secretRoutes := router.Group("/api/secret")
|
secretRoutes := router.Group("/api/secret")
|
||||||
@@ -276,12 +283,10 @@ func main() {
|
|||||||
secretRoutes.POST("/retrieve", controllers.RetrieveSecret) // TODO deprecate, replace retrieve with get
|
secretRoutes.POST("/retrieve", controllers.RetrieveSecret) // TODO deprecate, replace retrieve with get
|
||||||
secretRoutes.POST("/get", controllers.RetrieveSecret)
|
secretRoutes.POST("/get", controllers.RetrieveSecret)
|
||||||
secretRoutes.GET("/list", controllers.ListSecrets)
|
secretRoutes.GET("/list", controllers.ListSecrets)
|
||||||
//secretRoutes.POST("/retrieveMultiple", controllers.RetrieveMultpleSecrets) // TODO is this still required?
|
|
||||||
secretRoutes.POST("/store", controllers.StoreSecret) // TODO deprecate, replace store with add
|
secretRoutes.POST("/store", controllers.StoreSecret) // TODO deprecate, replace store with add
|
||||||
secretRoutes.POST("/add", controllers.StoreSecret)
|
secretRoutes.POST("/add", controllers.StoreSecret)
|
||||||
|
|
||||||
secretRoutes.POST("/update", controllers.UpdateSecret)
|
secretRoutes.POST("/update", controllers.UpdateSecret)
|
||||||
// TODO
|
|
||||||
secretRoutes.POST("/delete", controllers.DeleteSecret)
|
secretRoutes.POST("/delete", controllers.DeleteSecret)
|
||||||
|
|
||||||
// Get Safes (only those user allowed to access)
|
// Get Safes (only those user allowed to access)
|
||||||
@@ -293,6 +298,7 @@ func main() {
|
|||||||
// See https://gin-gonic.com/docs/examples/param-in-path/
|
// See https://gin-gonic.com/docs/examples/param-in-path/
|
||||||
secretRoutes.GET("/retrieve/name/:devicename", controllers.RetrieveSecretByDevicename)
|
secretRoutes.GET("/retrieve/name/:devicename", controllers.RetrieveSecretByDevicename)
|
||||||
secretRoutes.GET("/retrieve/category/:devicecategory", controllers.RetrieveSecretByDevicecategory)
|
secretRoutes.GET("/retrieve/category/:devicecategory", controllers.RetrieveSecretByDevicecategory)
|
||||||
|
secretRoutes.GET("/retrieve/user/:username", controllers.RetrieveSecretByUsername)
|
||||||
|
|
||||||
// Initializing the server in a goroutine so that
|
// Initializing the server in a goroutine so that
|
||||||
// it won't block the graceful shutdown handling below
|
// it won't block the graceful shutdown handling below
|
||||||
|
@@ -12,10 +12,11 @@ type Audit struct {
|
|||||||
SecretId int `db:"SecretId" json:"secretId"`
|
SecretId int `db:"SecretId" json:"secretId"`
|
||||||
EventText string `db:"EventText" json:"eventText"`
|
EventText string `db:"EventText" json:"eventText"`
|
||||||
EventTime time.Time `db:"EventTime" json:"eventTime"`
|
EventTime time.Time `db:"EventTime" json:"eventTime"`
|
||||||
|
IpAddress string `db:"IpAddress" json:"ipAddress"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuditAdd adds a new audit record to the database
|
// AuditLogAdd adds a new audit record to the database
|
||||||
func (a *Audit) AuditAdd() (*Audit, error) {
|
func (a *Audit) AuditLogAdd() (*Audit, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Populate timestamp field if not already set
|
// Populate timestamp field if not already set
|
||||||
@@ -23,17 +24,45 @@ func (a *Audit) AuditAdd() (*Audit, error) {
|
|||||||
a.EventTime = time.Now().UTC()
|
a.EventTime = time.Now().UTC()
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := db.NamedExec(("INSERT INTO audit (UserId, SecretId, EventText, EventTime) VALUES (:UserId, :SecretId, :EventText, :EventTime);"), a)
|
result, err := db.NamedExec(("INSERT INTO audit (UserId, SecretId, EventText, EventTime, IpAddress) VALUES (:UserId, :SecretId, :EventText, :EventTime, :IpAddress);"), a)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("AuditAdd error executing sql record : '%s'\n", err)
|
log.Printf("AuditLogAdd error executing sql record : '%s'\n", err)
|
||||||
return &Audit{}, err
|
return &Audit{}, err
|
||||||
} else {
|
} else {
|
||||||
affected, _ := result.RowsAffected()
|
affected, _ := result.RowsAffected()
|
||||||
id, _ := result.LastInsertId()
|
id, _ := result.LastInsertId()
|
||||||
a.AuditId = int(id)
|
a.AuditId = int(id)
|
||||||
log.Printf("AuditAdd insert returned result id '%d' affecting %d row(s).\n", id, affected)
|
log.Printf("AuditLogAdd insert returned result id '%d' affecting %d row(s).\n", id, affected)
|
||||||
}
|
}
|
||||||
|
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AuditList returns a list of all audit logs in database
|
||||||
|
func AuditLogList() ([]Audit, error) {
|
||||||
|
var results []Audit
|
||||||
|
|
||||||
|
// Query database for groups
|
||||||
|
rows, err := db.Queryx("SELECT * FROM audit")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("AuditLogList error executing sql record : '%s'\n", err)
|
||||||
|
return results, err
|
||||||
|
} else {
|
||||||
|
// parse all the results into a slice
|
||||||
|
for rows.Next() {
|
||||||
|
var a Audit
|
||||||
|
err = rows.StructScan(&a)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("AuditLogList error parsing sql record : '%s'\n", err)
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
results = append(results, a)
|
||||||
|
|
||||||
|
}
|
||||||
|
log.Printf("AuditLogList retrieved '%d' results\n", len(results))
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
@@ -27,7 +27,8 @@ const createUsers string = `
|
|||||||
UserName VARCHAR,
|
UserName VARCHAR,
|
||||||
Password VARCHAR,
|
Password VARCHAR,
|
||||||
Admin BOOLEAN DEFAULT 0,
|
Admin BOOLEAN DEFAULT 0,
|
||||||
LdapUser BOOLEAN DEFAULT 0
|
LdapUser BOOLEAN DEFAULT 0,
|
||||||
|
LastLogin datetime DEFAULT (datetime('1970-01-01 00:00:00'))
|
||||||
);
|
);
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -68,6 +69,7 @@ const createSecrets string = `
|
|||||||
DeviceCategory VARCHAR,
|
DeviceCategory VARCHAR,
|
||||||
UserName VARCHAR,
|
UserName VARCHAR,
|
||||||
Secret VARCHAR,
|
Secret VARCHAR,
|
||||||
|
LastUpdated datetime DEFAULT (datetime('1970-01-01 00:00:00')),
|
||||||
FOREIGN KEY (SafeId) REFERENCES safes(SafeId)
|
FOREIGN KEY (SafeId) REFERENCES safes(SafeId)
|
||||||
);
|
);
|
||||||
`
|
`
|
||||||
@@ -83,8 +85,9 @@ const createAudit string = `
|
|||||||
AuditId INTEGER PRIMARY KEY AUTOINCREMENT,
|
AuditId INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
UserId INTEGER DEFAULT 0,
|
UserId INTEGER DEFAULT 0,
|
||||||
SecretId INTEGER DEFAULT 0,
|
SecretId INTEGER DEFAULT 0,
|
||||||
EventText VARCHAR,
|
EventText VARCHAR DEFAULT '',
|
||||||
EventTime datetime
|
IpAddress VARCHAR DEFAULT '',
|
||||||
|
EventTime datetime DEFAULT (datetime('1970-01-01 00:00:00'))
|
||||||
);
|
);
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -119,29 +122,8 @@ func DisconnectDatabase() {
|
|||||||
func CreateTables() {
|
func CreateTables() {
|
||||||
var err error
|
var err error
|
||||||
var rowCount int
|
var rowCount int
|
||||||
|
|
||||||
// Create database tables if it doesn't exist
|
// Create database tables if it doesn't exist
|
||||||
/*
|
|
||||||
// Roles table should go first since other tables refer to it
|
|
||||||
if _, err = db.Exec(createRoles); err != nil {
|
|
||||||
log.Printf("Error checking roles table : '%s'", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
rowCount, _ = CheckCount("roles")
|
|
||||||
if rowCount == 0 {
|
|
||||||
if _, err = db.Exec("INSERT INTO roles VALUES(1, 'Admin', false);"); err != nil {
|
|
||||||
log.Printf("Error adding initial admin role : '%s'", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
if _, err = db.Exec("INSERT INTO roles VALUES(2, 'UserRole', false);"); err != nil {
|
|
||||||
log.Printf("Error adding initial user role : '%s'", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
if _, err = db.Exec("INSERT INTO roles VALUES(3, 'GuestRole', true);"); err != nil {
|
|
||||||
log.Printf("Error adding initial guest role : '%s'", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// groups table
|
// groups table
|
||||||
if _, err = db.Exec(createGroups); err != nil {
|
if _, err = db.Exec(createGroups); err != nil {
|
||||||
@@ -247,11 +229,16 @@ func CreateTables() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove users RoleId column
|
// Check the database schema version
|
||||||
userRoleIdCheck, _ := CheckColumnExists("users", "RoleId")
|
version, _ := GetSchemaVersion()
|
||||||
if userRoleIdCheck {
|
if version >= 3 {
|
||||||
//_, err := db.Exec("ALTER TABLE users DROP COLUMN RoleId;")
|
log.Printf("Database schema up to date\n")
|
||||||
_, err := db.Exec(`
|
} else {
|
||||||
|
// Remove users RoleId column
|
||||||
|
userRoleIdCheck, _ := CheckColumnExists("users", "RoleId")
|
||||||
|
if userRoleIdCheck {
|
||||||
|
//_, err := db.Exec("ALTER TABLE users DROP COLUMN RoleId;")
|
||||||
|
_, err := db.Exec(`
|
||||||
PRAGMA foreign_keys=off;
|
PRAGMA foreign_keys=off;
|
||||||
BEGIN TRANSACTION;
|
BEGIN TRANSACTION;
|
||||||
ALTER TABLE users RENAME TO _users_old;
|
ALTER TABLE users RENAME TO _users_old;
|
||||||
@@ -269,43 +256,49 @@ func CreateTables() {
|
|||||||
PRAGMA foreign_keys=on;
|
PRAGMA foreign_keys=on;
|
||||||
DROP TABLE _users_old;
|
DROP TABLE _users_old;
|
||||||
`)
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error altering users table to drop RoleId column : '%s'\n", err)
|
log.Printf("Error altering users table to drop RoleId column : '%s'\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set any unassigned secrets to the default safe id
|
||||||
|
if _, err = db.Exec("UPDATE users SET LdapUser = 0 WHERE LdapUser is null;"); err != nil {
|
||||||
|
log.Printf("Error setting LdapUser flag to false for existing users : '%s'", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Remove LdapGroup column from roles table
|
// Remove LdapGroup column from roles table
|
||||||
ldapCheck, _ := CheckColumnExists("roles", "LdapGroup")
|
ldapCheck, _ := CheckColumnExists("roles", "LdapGroup")
|
||||||
if ldapCheck {
|
if ldapCheck {
|
||||||
_, err := db.Exec("ALTER TABLE roles DROP COLUMN LdapGroup;")
|
_, err := db.Exec("ALTER TABLE roles DROP COLUMN LdapGroup;")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error altering roles table to renmove LdapGroup column : '%s'\n", err)
|
log.Printf("Error altering roles table to renmove LdapGroup column : '%s'\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add SafeId column to secrets table
|
||||||
|
safeIdCheck, _ := CheckColumnExists("secrets", "SafeId")
|
||||||
|
if !safeIdCheck {
|
||||||
|
// Add the column for LdapGroup in the roles table
|
||||||
|
_, err := db.Exec("ALTER TABLE secrets ADD COLUMN SafeId INTEGER REFERENCES safes(SafeId);")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error altering secrets table to add SafeId column : '%s'\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set any unassigned secrets to the default safe id
|
||||||
|
if _, err = db.Exec("UPDATE secrets SET SafeId = 1 WHERE SafeId is null;"); err != nil {
|
||||||
|
log.Printf("Error setting safe ID of existing secrets : '%s'", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Add SafeId column to secrets table
|
// Remove RoleId column from secrets table
|
||||||
safeIdCheck, _ := CheckColumnExists("secrets", "SafeId")
|
secretsRoleIdCheck, _ := CheckColumnExists("secrets", "RoleId")
|
||||||
if !safeIdCheck {
|
if secretsRoleIdCheck {
|
||||||
// Add the column for LdapGroup in the roles table
|
_, err := db.Exec(`
|
||||||
_, err := db.Exec("ALTER TABLE secrets ADD COLUMN SafeId INTEGER REFERENCES safes(SafeId);")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error altering secrets table to add SafeId column : '%s'\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set any unassigned secrets to the default safe id
|
|
||||||
if _, err = db.Exec("UPDATE secrets SET SafeId = 1 WHERE SafeId is null;"); err != nil {
|
|
||||||
log.Printf("Error setting safe ID of existing secrets : '%s'", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove RoleId column from secrets table
|
|
||||||
secretsRoleIdCheck, _ := CheckColumnExists("secrets", "RoleId")
|
|
||||||
if secretsRoleIdCheck {
|
|
||||||
_, err := db.Exec(`
|
|
||||||
PRAGMA foreign_keys=off;
|
PRAGMA foreign_keys=off;
|
||||||
BEGIN TRANSACTION;
|
BEGIN TRANSACTION;
|
||||||
ALTER TABLE secrets RENAME TO _secrets_old;
|
ALTER TABLE secrets RENAME TO _secrets_old;
|
||||||
@@ -320,32 +313,34 @@ func CreateTables() {
|
|||||||
Secret VARCHAR,
|
Secret VARCHAR,
|
||||||
FOREIGN KEY (SafeId) REFERENCES safes(SafeId)
|
FOREIGN KEY (SafeId) REFERENCES safes(SafeId)
|
||||||
);
|
);
|
||||||
INSERT INTO secrets SELECT SecretId, SafeId, DeviceName, DeviceCategory, UserName, Secret FROM _secrets_old;
|
INSERT INTO secrets SELECT SecretId, RoleId, SafeId, DeviceName, DeviceCategory, UserName, Secret FROM _secrets_old;
|
||||||
ALTER TABLE secrets DROP COLUMN RoleId;
|
ALTER TABLE secrets DROP COLUMN RoleId;
|
||||||
|
ALTER TABLE secrets ADD COLUMN LastUpdated datetime;
|
||||||
|
UPDATE secrets SET LastUpdated = (datetime('1970-01-01 00:00:00')) WHERE LastUpdated is null;
|
||||||
COMMIT;
|
COMMIT;
|
||||||
PRAGMA foreign_keys=on;
|
PRAGMA foreign_keys=on;
|
||||||
DROP TABLE _secrets_old;
|
DROP TABLE _secrets_old;
|
||||||
`)
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error altering secrets table to remove RoleId column : '%s'\n", err)
|
log.Printf("Error altering secrets table to remove RoleId column : '%s'\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the Admin column from roles table
|
// Remove the Admin column from roles table
|
||||||
rolesAdminCheck, _ := CheckColumnExists("roles", "Admin")
|
rolesAdminCheck, _ := CheckColumnExists("roles", "Admin")
|
||||||
if rolesAdminCheck {
|
if rolesAdminCheck {
|
||||||
_, err := db.Exec("ALTER TABLE roles DROP COLUMN Admin;")
|
_, err := db.Exec("ALTER TABLE roles DROP COLUMN Admin;")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error altering roles table to remove Admin column : '%s'\n", err)
|
log.Printf("Error altering roles table to remove Admin column : '%s'\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the RoleId from permissiosn table
|
// Remove the RoleId from permissiosn table
|
||||||
permissionsRoleIdCheck, _ := CheckColumnExists("permissions", "RoleId")
|
permissionsRoleIdCheck, _ := CheckColumnExists("permissions", "RoleId")
|
||||||
if permissionsRoleIdCheck {
|
if permissionsRoleIdCheck {
|
||||||
_, err := db.Exec(`
|
_, err := db.Exec(`
|
||||||
PRAGMA foreign_keys=off;
|
PRAGMA foreign_keys=off;
|
||||||
BEGIN TRANSACTION;
|
BEGIN TRANSACTION;
|
||||||
ALTER TABLE permissions RENAME TO _permissions_old;
|
ALTER TABLE permissions RENAME TO _permissions_old;
|
||||||
@@ -368,43 +363,75 @@ func CreateTables() {
|
|||||||
PRAGMA foreign_keys=on;
|
PRAGMA foreign_keys=on;
|
||||||
DROP TABLE _permissions_old;
|
DROP TABLE _permissions_old;
|
||||||
`)
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error altering permissions table to remove RoleId column : '%s'\n", err)
|
log.Printf("Error altering permissions table to remove RoleId column : '%s'\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
secretsLastUpdatedCheck, _ := CheckColumnExists("secrets", "LastUpdated")
|
||||||
|
if !secretsLastUpdatedCheck {
|
||||||
|
// Add the column for LastUpdated in the secrets table
|
||||||
|
_, err := db.Exec("ALTER TABLE secrets ADD COLUMN LastUpdated datetime;")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error altering secrets table to add LastUpdated column : '%s'\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default value
|
||||||
|
if _, err = db.Exec("UPDATE secrets SET LastUpdated = (datetime('1970-01-01 00:00:00')) WHERE LastUpdated is null;"); err != nil {
|
||||||
|
log.Printf("Error setting LastUpdated of existing secrets : '%s'", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastLoginCheck, _ := CheckColumnExists("users", "LastLogin")
|
||||||
|
if !lastLoginCheck {
|
||||||
|
// Add the column for LastUpdated in the secrets table
|
||||||
|
_, err := db.Exec("ALTER TABLE users ADD COLUMN LastLogin datetime;")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error altering users table to add LastLogin column : '%s'\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default value
|
||||||
|
if _, err = db.Exec("UPDATE users SET LastLogin = (datetime('1970-01-01 00:00:00')) WHERE LastLogin is null;"); err != nil {
|
||||||
|
log.Printf("Error setting LastLogin of existing users : '%s'", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add IpAddress column to audit table
|
||||||
|
auditIPCheck, _ := CheckColumnExists("audit", "IpAddress")
|
||||||
|
if !auditIPCheck {
|
||||||
|
// Add the column for LdapGroup in the roles table
|
||||||
|
_, err := db.Exec("ALTER TABLE audit ADD COLUMN IpAddress VARCHAR;")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error altering audit table to add IpAddress column : '%s'\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = db.Exec("UPDATE audit SET IpAddress = '' WHERE IpAddress is null;"); err != nil {
|
||||||
|
log.Printf("Error setting IpAddress of existing audit records : '%s'", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the schema version
|
||||||
|
rowCount, _ = CheckCount("schema")
|
||||||
|
if rowCount > 0 {
|
||||||
|
if _, err = db.Exec("UPDATE schema SET Version = 3;"); err != nil {
|
||||||
|
log.Printf("Error setting schema to version 3 : '%s'", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err = db.Exec("INSERT INTO schema (Version) VALUES (3);"); err != nil {
|
||||||
|
log.Printf("Error setting schema to version 3 : '%s'", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// Database updates added after initial version released
|
|
||||||
ldapCheck, _ := CheckColumnExists("roles", "LdapGroup")
|
|
||||||
|
|
||||||
if !ldapCheck {
|
|
||||||
// Add the column for LdapGroup in the roles table
|
|
||||||
_, err := db.Exec("ALTER TABLE roles ADD COLUMN LdapGroup VARCHAR DEFAULT '';")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error altering roles table to add LdapGroup column : '%s'\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the two LDAP columns to the users table if they weren't there
|
|
||||||
ldapUserCheck, _ := CheckColumnExists("users", "LdapUser")
|
|
||||||
if !ldapUserCheck {
|
|
||||||
log.Printf("CreateTables creating ldap columns in user table")
|
|
||||||
_, err := db.Exec("ALTER TABLE users ADD COLUMN LdapUser BOOLEAN DEFAULT 0;")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error altering users table to add LdapUser column : '%s'\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("ALTER TABLE users ADD COLUMN LdapDn VARCHAR DEFAULT '';")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error altering users table to add LdapDn column : '%s'\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count the number of records in the sqlite database
|
// Count the number of records in the sqlite database
|
||||||
@@ -425,6 +452,23 @@ func CheckCount(tablename string) (int, error) {
|
|||||||
return count, nil
|
return count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetSchemaVersion() (int, error) {
|
||||||
|
var version int
|
||||||
|
|
||||||
|
stmt, err := db.Prepare("SELECT Version FROM schema;")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("GetSchemaVersion error preparing sqlite statement : '%s'\n", err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
err = stmt.QueryRow().Scan(&version)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("GetSchemaVersion error querying database record count : '%s'\n", err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
stmt.Close() // or use defer rows.Close(), idc
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
|
|
||||||
// From https://stackoverflow.com/a/60100045
|
// From https://stackoverflow.com/a/60100045
|
||||||
func GenerateInsertMethod(q interface{}) (string, error) {
|
func GenerateInsertMethod(q interface{}) (string, error) {
|
||||||
if reflect.ValueOf(q).Kind() == reflect.Struct {
|
if reflect.ValueOf(q).Kind() == reflect.Struct {
|
@@ -12,7 +12,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-ldap/ldap"
|
"github.com/go-ldap/ldap/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Code relating to AD integration
|
// Code relating to AD integration
|
||||||
@@ -236,7 +236,7 @@ func LdapGetGroupMembership(username string, password string) ([]string, error)
|
|||||||
defer ldaps.Close()
|
defer ldaps.Close()
|
||||||
|
|
||||||
// try an authenticated bind to AD to verify credentials
|
// try an authenticated bind to AD to verify credentials
|
||||||
log.Printf("GetLdapGroupMembership Attempting LDAP bind with user '%s' and password length '%d'\n", username, len(password))
|
log.Printf("LdapGetGroupMembership Attempting LDAP bind with user '%s' and password length '%d'\n", username, len(password))
|
||||||
err = ldaps.Bind(username, password)
|
err = ldaps.Bind(username, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ldapErr, ok := err.(*ldap.Error); ok && ldapErr.ResultCode == ldap.LDAPResultInvalidCredentials {
|
if ldapErr, ok := err.(*ldap.Error); ok && ldapErr.ResultCode == ldap.LDAPResultInvalidCredentials {
|
||||||
@@ -244,17 +244,17 @@ func LdapGetGroupMembership(username string, password string) ([]string, error)
|
|||||||
log.Print(errString)
|
log.Print(errString)
|
||||||
return nil, errors.New(errString)
|
return nil, errors.New(errString)
|
||||||
} else {
|
} else {
|
||||||
errString := fmt.Sprintf("GetLdapGroupMembership error binding to LDAP with supplied credentials : '%s'\n", err)
|
errString := fmt.Sprintf("LdapGetGroupMembership error binding to LDAP with supplied credentials : '%s'\n", err)
|
||||||
log.Print(errString)
|
log.Print(errString)
|
||||||
return nil, errors.New(errString)
|
return nil, errors.New(errString)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Printf("GetLdapGroupMembership successfully bound to LDAP\n")
|
log.Printf("LdapGetGroupMembership successfully bound to LDAP\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
groups, err := GetGroupsOfUser(username, LdapBaseDn, ldaps)
|
groups, err := GetGroupsOfUser(username, LdapBaseDn, ldaps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errString := fmt.Sprintf("GetLdapGroupMembership group search error : '%s'\n", err)
|
errString := fmt.Sprintf("LdapGetGroupMembership group search error : '%s'\n", err)
|
||||||
log.Print(errString)
|
log.Print(errString)
|
||||||
return nil, errors.New(errString)
|
return nil, errors.New(errString)
|
||||||
}
|
}
|
||||||
@@ -373,3 +373,12 @@ func GetLdapUserDn(username string, baseDN string, conn *ldap.Conn) (string, err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the user portion of a UPN formatted username
|
||||||
|
func GetUserFromUPN(email string) string {
|
||||||
|
parts := strings.Split(email, "@")
|
||||||
|
if len(parts) > 0 {
|
||||||
|
return parts[0]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
@@ -142,3 +142,29 @@ func (p *Permission) PermissionDelete() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PermissionUpdate updates an existing permission definition in the database
|
||||||
|
func (p *Permission) PermissionUpdate() (*Permission, error) {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
log.Printf("PermissionUpdate storing values '%v'\n", p)
|
||||||
|
|
||||||
|
if p.PermissionId == 0 {
|
||||||
|
err = errors.New("PermissionUpdate unable to update permission with empty PermissionId field")
|
||||||
|
log.Printf("PermissionUpdate error in pre-check : '%s'\n", err)
|
||||||
|
return p, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := db.NamedExec((`UPDATE permissions SET Description = :Description, ReadOnly = :ReadOnly, SafeId = :SafeId, UserId = :UserId, GroupId = :GroupId WHERE PermissionId = :PermissionId`), p)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("PermissionUpdate error executing sql record : '%s'\n", err)
|
||||||
|
return &Permission{}, err
|
||||||
|
} else {
|
||||||
|
affected, _ := result.RowsAffected()
|
||||||
|
id, _ := result.LastInsertId()
|
||||||
|
log.Printf("PermissionUpdate returned result id '%d' affecting %d row(s).\n", id, affected)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
@@ -11,35 +11,39 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"smt/utils"
|
"smt/utils"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const nonceSize = 12
|
const nonceSize = 12
|
||||||
|
|
||||||
// We use the json:"-" field tag to prevent showing these details to the user
|
// We use the json:"-" field tag to prevent showing these details to the user
|
||||||
type Secret struct {
|
type Secret struct {
|
||||||
SecretId int `db:"SecretId" json:"secretId"`
|
SecretId int `db:"SecretId" json:"secretId"`
|
||||||
SafeId int `db:"SafeId" json:"safeId"`
|
SafeId int `db:"SafeId" json:"safeId"`
|
||||||
DeviceName string `db:"DeviceName" json:"deviceName"`
|
DeviceName string `db:"DeviceName" json:"deviceName"`
|
||||||
DeviceCategory string `db:"DeviceCategory" json:"deviceCategory"`
|
DeviceCategory string `db:"DeviceCategory" json:"deviceCategory"`
|
||||||
UserName string `db:"UserName" json:"userName"`
|
UserName string `db:"UserName" json:"userName"`
|
||||||
Secret string `db:"Secret" json:"secret"`
|
Secret string `db:"Secret" json:"secret"`
|
||||||
|
LastUpdated time.Time `db:"LastUpdated" json:"lastUpdated"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecretRestricted is for when we want to output a Secret but not the protected information
|
// SecretRestricted is for when we want to output a Secret but not the protected information
|
||||||
type SecretRestricted struct {
|
type SecretRestricted struct {
|
||||||
SecretId int `db:"SecretId" json:"secretId"`
|
SecretId int `db:"SecretId" json:"secretId"`
|
||||||
SafeId int `db:"SafeId" json:"safeId"`
|
SafeId int `db:"SafeId" json:"safeId"`
|
||||||
DeviceName string `db:"DeviceName" json:"deviceName"`
|
DeviceName string `db:"DeviceName" json:"deviceName"`
|
||||||
DeviceCategory string `db:"DeviceCategory" json:"deviceCategory"`
|
DeviceCategory string `db:"DeviceCategory" json:"deviceCategory"`
|
||||||
UserName string `db:"UserName" json:"userName"`
|
UserName string `db:"UserName" json:"userName"`
|
||||||
Secret string `db:"Secret" json:"-"`
|
Secret string `db:"Secret" json:"-"`
|
||||||
|
LastUpdated time.Time `db:"LastUpdated" json:"lastUpdated"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used for querying all secrets the user has access to
|
// Used for querying all secrets the user has access to
|
||||||
// Since there are some ambiguous column names (eg UserName is present in both users and secrets table), the order of fields in this struct matters
|
// Since there are some ambiguous column names (eg UserName is present in both users and secrets table), the order of fields in this struct matters
|
||||||
type UserSecret struct {
|
type UserSecret struct {
|
||||||
Secret
|
Secret
|
||||||
UserUserId int `db:"UserUserId"`
|
UserUserId int `db:"UserUserId"`
|
||||||
|
UserUserName string `db:"UserUserName"`
|
||||||
User
|
User
|
||||||
//Group
|
//Group
|
||||||
Permission
|
Permission
|
||||||
@@ -51,11 +55,15 @@ func (s Secret) GetId() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Secret) SaveSecret() (*Secret, error) {
|
func (s *Secret) SaveSecret() (*Secret, error) {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
// Populate timestamp field if not already set
|
||||||
|
if s.LastUpdated.IsZero() {
|
||||||
|
s.LastUpdated = time.Now().UTC()
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("SaveSecret storing values '%v'\n", s)
|
log.Printf("SaveSecret storing values '%v'\n", s)
|
||||||
result, err := db.NamedExec((`INSERT INTO secrets (SafeId, DeviceName, DeviceCategory, UserName, Secret) VALUES (:SafeId, :DeviceName, :DeviceCategory, :UserName, :Secret)`), s)
|
result, err := db.NamedExec((`INSERT INTO secrets (SafeId, DeviceName, DeviceCategory, UserName, Secret, LastUpdated) VALUES (:SafeId, :DeviceName, :DeviceCategory, :UserName, :Secret, :LastUpdated)`), s)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("StoreSecret error executing sql record : '%s'\n", err)
|
log.Printf("StoreSecret error executing sql record : '%s'\n", err)
|
||||||
@@ -78,7 +86,7 @@ func SecretsGetAllowed(s *Secret, userId int) ([]UserSecret, error) {
|
|||||||
// Query for group access
|
// Query for group access
|
||||||
queryArgs := []interface{}{}
|
queryArgs := []interface{}{}
|
||||||
query := `
|
query := `
|
||||||
SELECT users.UserId AS UserUserId, permissions.*,
|
SELECT users.UserId AS UserUserId, users.UserName AS UserUserName, permissions.*,
|
||||||
secrets.SecretId, secrets.SafeId, secrets.DeviceName, secrets.DeviceCategory, secrets.UserName
|
secrets.SecretId, secrets.SafeId, secrets.DeviceName, secrets.DeviceCategory, secrets.UserName
|
||||||
FROM users
|
FROM users
|
||||||
INNER JOIN groups ON users.GroupId = groups.GroupId
|
INNER JOIN groups ON users.GroupId = groups.GroupId
|
||||||
@@ -88,6 +96,11 @@ func SecretsGetAllowed(s *Secret, userId int) ([]UserSecret, error) {
|
|||||||
queryArgs = append(queryArgs, userId)
|
queryArgs = append(queryArgs, userId)
|
||||||
|
|
||||||
// Add any other arguments to the query if they were specified
|
// Add any other arguments to the query if they were specified
|
||||||
|
if s.SecretId > 0 {
|
||||||
|
query += " AND SecretId = ? "
|
||||||
|
queryArgs = append(queryArgs, s.SecretId)
|
||||||
|
}
|
||||||
|
|
||||||
if s.DeviceName != "" {
|
if s.DeviceName != "" {
|
||||||
query += " AND DeviceName LIKE ? "
|
query += " AND DeviceName LIKE ? "
|
||||||
queryArgs = append(queryArgs, s.DeviceName)
|
queryArgs = append(queryArgs, s.DeviceName)
|
||||||
@@ -106,7 +119,7 @@ func SecretsGetAllowed(s *Secret, userId int) ([]UserSecret, error) {
|
|||||||
// Query for user access
|
// Query for user access
|
||||||
query += `
|
query += `
|
||||||
UNION
|
UNION
|
||||||
SELECT users.UserId AS UserUserId, permissions.*,
|
SELECT users.UserId AS UserUserId, users.UserName AS UserUserName, permissions.*,
|
||||||
secrets.SecretId, secrets.SafeId, secrets.DeviceName, secrets.DeviceCategory, secrets.UserName
|
secrets.SecretId, secrets.SafeId, secrets.DeviceName, secrets.DeviceCategory, secrets.UserName
|
||||||
FROM users
|
FROM users
|
||||||
INNER JOIN permissions ON users.UserId = permissions.UserId
|
INNER JOIN permissions ON users.UserId = permissions.UserId
|
||||||
@@ -116,6 +129,10 @@ func SecretsGetAllowed(s *Secret, userId int) ([]UserSecret, error) {
|
|||||||
queryArgs = append(queryArgs, userId)
|
queryArgs = append(queryArgs, userId)
|
||||||
|
|
||||||
// Add any other arguments to the query if they were specified
|
// Add any other arguments to the query if they were specified
|
||||||
|
if s.SecretId > 0 {
|
||||||
|
query += " AND SecretId = ? "
|
||||||
|
queryArgs = append(queryArgs, s.SecretId)
|
||||||
|
}
|
||||||
if s.DeviceName != "" {
|
if s.DeviceName != "" {
|
||||||
query += " AND DeviceName LIKE ? "
|
query += " AND DeviceName LIKE ? "
|
||||||
queryArgs = append(queryArgs, s.DeviceName)
|
queryArgs = append(queryArgs, s.DeviceName)
|
||||||
@@ -132,37 +149,42 @@ func SecretsGetAllowed(s *Secret, userId int) ([]UserSecret, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Execute the query
|
// Execute the query
|
||||||
log.Printf("SecretsGetAllowed query string : '%s'\nArguments:%+v\n", query, queryArgs)
|
//log.Printf("SecretsGetAllowed query string : '%s'\nArguments:%+v\n", query, queryArgs)
|
||||||
rows, err := db.Queryx(query, queryArgs...)
|
rows, err := db.Queryx(query, queryArgs...)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("SecretsGetAllowed error executing sql record : '%s'\n", err)
|
log.Printf("SecretsGetAllowed error executing sql record : '%s'\n", err)
|
||||||
return secretResults, err
|
return secretResults, err
|
||||||
} else {
|
} else {
|
||||||
|
//log.Printf("SecretsGetAllowed any error '%s'\n", rows.Err())
|
||||||
// parse all the results into a slice
|
// parse all the results into a slice
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
|
//log.Printf("SecretsGetAllowed processing row\n")
|
||||||
var r UserSecret
|
var r UserSecret
|
||||||
err = rows.StructScan(&r)
|
err = rows.StructScan(&r)
|
||||||
|
//log.Printf("SecretsGetAllowed performed struct scan\n")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("SecretsGetAllowed error parsing sql record : '%s'\n", err)
|
log.Printf("SecretsGetAllowed error parsing sql record : '%s'\n", err)
|
||||||
return secretResults, err
|
return secretResults, err
|
||||||
}
|
}
|
||||||
//log.Printf("r: %v\n", r)
|
//log.Printf("r: %v\n", r)
|
||||||
|
|
||||||
|
//log.Printf("SecretsGetAllowed performed err check\n")
|
||||||
|
|
||||||
// work around to get the UserId populated in the User field of the struct
|
// work around to get the UserId populated in the User field of the struct
|
||||||
r.User.UserId = r.UserUserId
|
r.User.UserId = r.UserUserId
|
||||||
|
r.User.UserName = r.UserUserName
|
||||||
|
|
||||||
// For debugging purposes
|
// For debugging purposes
|
||||||
debugPrint := utils.PrintStructContents(&r, 0)
|
//debugPrint := utils.PrintStructContents(&r, 0)
|
||||||
log.Println(debugPrint)
|
//log.Println(debugPrint)
|
||||||
|
|
||||||
// Append the secrets to the query output, don't decrypt the secrets (we didn't SELECT them anyway)
|
|
||||||
//secretResults = append(secretResults, r)
|
|
||||||
|
|
||||||
// Use generics and the GetID() method on the UserSecret struct
|
// Use generics and the GetID() method on the UserSecret struct
|
||||||
// to avoid adding this element to the results
|
// to avoid adding this element to the results
|
||||||
// if there is already a secret with the same ID present
|
// if there is already a secret with the same ID present
|
||||||
secretResults = utils.AppendIfNotExists(secretResults, r)
|
secretResults = utils.AppendIfNotExists(secretResults, r)
|
||||||
|
|
||||||
|
//log.Printf("SecretsGetAllowed added secret results\n")
|
||||||
}
|
}
|
||||||
log.Printf("SecretsGetAllowed retrieved '%d' results\n", len(secretResults))
|
log.Printf("SecretsGetAllowed retrieved '%d' results\n", len(secretResults))
|
||||||
}
|
}
|
||||||
@@ -175,7 +197,7 @@ func SecretsGetFromMultipleSafes(s *Secret, safeIds []int) ([]Secret, error) {
|
|||||||
var err error
|
var err error
|
||||||
var secretResults []Secret
|
var secretResults []Secret
|
||||||
|
|
||||||
args := []interface{}{}
|
queryArgs := []interface{}{}
|
||||||
var query string
|
var query string
|
||||||
// Generate placeholders for the IN clause to match multiple SafeId values
|
// Generate placeholders for the IN clause to match multiple SafeId values
|
||||||
placeholders := make([]string, len(safeIds))
|
placeholders := make([]string, len(safeIds))
|
||||||
@@ -189,28 +211,33 @@ func SecretsGetFromMultipleSafes(s *Secret, safeIds []int) ([]Secret, error) {
|
|||||||
|
|
||||||
// Add the Safe Ids to the arguments list
|
// Add the Safe Ids to the arguments list
|
||||||
for _, g := range safeIds {
|
for _, g := range safeIds {
|
||||||
args = append(args, g)
|
queryArgs = append(queryArgs, g)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add any other arguments to the query if they were specified
|
// Add any other arguments to the query if they were specified
|
||||||
|
if s.SecretId > 0 {
|
||||||
|
query += " AND SecretId = ? "
|
||||||
|
queryArgs = append(queryArgs, s.SecretId)
|
||||||
|
}
|
||||||
|
|
||||||
if s.DeviceName != "" {
|
if s.DeviceName != "" {
|
||||||
query += " AND DeviceName LIKE ? "
|
query += " AND DeviceName LIKE ? "
|
||||||
args = append(args, s.DeviceName)
|
queryArgs = append(queryArgs, s.DeviceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.DeviceCategory != "" {
|
if s.DeviceCategory != "" {
|
||||||
query += " AND DeviceCategory LIKE ? "
|
query += " AND DeviceCategory LIKE ? "
|
||||||
args = append(args, s.DeviceCategory)
|
queryArgs = append(queryArgs, s.DeviceCategory)
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.UserName != "" {
|
if s.UserName != "" {
|
||||||
query += " AND UserName LIKE ? "
|
query += " AND UserName LIKE ? "
|
||||||
args = append(args, s.UserName)
|
queryArgs = append(queryArgs, s.UserName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the query
|
// Execute the query
|
||||||
log.Printf("SecretsGetMultipleSafes query string :\n'%s'\nQuery Args : %+v\n", query, args)
|
//log.Printf("SecretsGetMultipleSafes query string :\n'%s'\nQuery Args : %+v\n", query, queryArgs)
|
||||||
rows, err := db.Queryx(query, args...)
|
rows, err := db.Queryx(query, queryArgs...)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("SecretsGetMultipleSafes error executing sql record : '%s'\n", err)
|
log.Printf("SecretsGetMultipleSafes error executing sql record : '%s'\n", err)
|
||||||
@@ -246,6 +273,11 @@ func (s *Secret) UpdateSecret() (*Secret, error) {
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
// Populate timestamp field if not already set
|
||||||
|
if s.LastUpdated.IsZero() {
|
||||||
|
s.LastUpdated = time.Now().UTC()
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("UpdateSecret storing values '%v'\n", s)
|
log.Printf("UpdateSecret storing values '%v'\n", s)
|
||||||
|
|
||||||
if s.SecretId == 0 {
|
if s.SecretId == 0 {
|
||||||
@@ -254,7 +286,7 @@ func (s *Secret) UpdateSecret() (*Secret, error) {
|
|||||||
return s, err
|
return s, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := db.NamedExec((`UPDATE secrets SET DeviceName = :DeviceName, DeviceCategory = :DeviceCategory, UserName = :UserName, Secret = :Secret WHERE SecretId = :SecretId`), s)
|
result, err := db.NamedExec((`UPDATE secrets SET DeviceName = :DeviceName, DeviceCategory = :DeviceCategory, UserName = :UserName, Secret = :Secret, LastUpdated = :LastUpdated WHERE SecretId = :SecretId`), s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("UpdateSecret error executing sql record : '%s'\n", err)
|
log.Printf("UpdateSecret error executing sql record : '%s'\n", err)
|
||||||
return &Secret{}, err
|
return &Secret{}, err
|
||||||
|
114
models/user.go
114
models/user.go
@@ -5,20 +5,22 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"smt/utils"
|
|
||||||
"smt/utils/token"
|
"smt/utils/token"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
UserId int `db:"UserId" json:"userId"`
|
UserId int `db:"UserId" json:"userId"`
|
||||||
GroupId int `db:"GroupId" json:"groupId"`
|
GroupId int `db:"GroupId" json:"groupId"`
|
||||||
UserName string `db:"UserName" json:"userName"`
|
UserName string `db:"UserName" json:"userName"`
|
||||||
Password string `db:"Password" json:"-"`
|
Password string `db:"Password" json:"-"`
|
||||||
LdapUser bool `db:"LdapUser" json:"ldapUser"`
|
LdapUser bool `db:"LdapUser" json:"ldapUser"`
|
||||||
Admin bool `db:"Admin"`
|
Admin bool `db:"Admin"`
|
||||||
//LdapDn string `db:"LdapDn" json:"ldapDn"`
|
LastLogin time.Time `db:"LastLogin" json:"lastLogin"`
|
||||||
|
LdapGroup bool `db:"LdapGroup"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserRole struct {
|
type UserRole struct {
|
||||||
@@ -49,12 +51,16 @@ func (u *User) SaveUser() (*User, error) {
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
if u.LastLogin.IsZero() {
|
||||||
|
u.LastLogin = time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
// Validate username not already in use
|
// Validate username not already in use
|
||||||
_, err = UserGetByName(u.UserName)
|
_, err = UserGetByName(u.UserName)
|
||||||
if err != nil && err.Error() == "user not found" {
|
if err != nil && err.Error() == "user not found" {
|
||||||
log.Printf("SaveUser confirmed no existing user, continuing with creation of user '%s'\n", u.UserName)
|
log.Printf("SaveUser confirmed no existing user, continuing with creation of user '%s'\n", u.UserName)
|
||||||
//log.Printf("u: %v\n", u)
|
//log.Printf("u: %v\n", u)
|
||||||
result, err := db.NamedExec((`INSERT INTO users (GroupId, UserName, Password, LdapUser, Admin) VALUES (:GroupId, :UserName, :Password, :LdapUser, :Admin)`), u)
|
result, err := db.NamedExec((`INSERT INTO users (GroupId, UserName, Password, LdapUser, Admin, LastLogin) VALUES (:GroupId, :UserName, :Password, :LdapUser, :Admin, :LastLogin)`), u)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("SaveUser error executing sql record : '%s'\n", err)
|
log.Printf("SaveUser error executing sql record : '%s'\n", err)
|
||||||
@@ -107,7 +113,7 @@ func VerifyPassword(password, hashedPassword string) error {
|
|||||||
return errors.New("unable to compare password with empty hash")
|
return errors.New("unable to compare password with empty hash")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("VerifyPassword comparing input against hashed value '%s'\n", hashedPassword)
|
//log.Printf("VerifyPassword comparing input against hashed value '%s'\n", hashedPassword)
|
||||||
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
|
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,12 +124,32 @@ func LoginCheck(username string, password string) (string, error) {
|
|||||||
|
|
||||||
// Query database for matching user object
|
// Query database for matching user object
|
||||||
// Use IFNULL to handle situation where a user might not be a member of a group
|
// Use IFNULL to handle situation where a user might not be a member of a group
|
||||||
err = db.QueryRowx("SELECT UserId, IFNULL(GroupId, 0) GroupId, UserName, Password, LdapUser, Admin FROM Users WHERE Username=?", username).StructScan(&u)
|
// Join on groups table so we can get the value in LdapGroup column
|
||||||
|
|
||||||
|
// if username is UPN format then get just the user portion
|
||||||
|
if strings.Contains(username, "@") {
|
||||||
|
plainUser := GetUserFromUPN(username)
|
||||||
|
// check for original username or plainUser
|
||||||
|
err = db.QueryRowx(`
|
||||||
|
SELECT users.UserId, IFNULL(users.GroupId, 0) GroupId, UserName, Password, LdapUser, users.Admin, groups.LdapGroup FROM Users
|
||||||
|
INNER JOIN groups ON users.GroupId = groups.GroupId
|
||||||
|
WHERE Username=? OR Username=?`, username, plainUser).StructScan(&u)
|
||||||
|
} else {
|
||||||
|
err = db.QueryRowx(`
|
||||||
|
SELECT users.UserId, IFNULL(users.GroupId, 0) GroupId, UserName, Password, LdapUser, users.Admin, groups.LdapGroup FROM Users
|
||||||
|
INNER JOIN groups ON users.GroupId = groups.GroupId
|
||||||
|
WHERE Username=?`, username).StructScan(&u)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
|
log.Printf("LoginCheck found no users matching username '%s'\n", username)
|
||||||
|
|
||||||
|
// TODO - if username contains UPN style login then try extracting just the username and doing a query on that
|
||||||
|
|
||||||
// check LDAP if enabled
|
// check LDAP if enabled
|
||||||
if LdapEnabled {
|
if LdapEnabled {
|
||||||
|
log.Printf("LoginCheck initiating ldap lookup for username '%s'\n", username)
|
||||||
ldapUser, err := UserLdapNewLoginCheck(username, password)
|
ldapUser, err := UserLdapNewLoginCheck(username, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errString := fmt.Sprintf("LoginCheck error checking LDAP for user : '%s'\n", err)
|
errString := fmt.Sprintf("LoginCheck error checking LDAP for user : '%s'\n", err)
|
||||||
@@ -154,10 +180,11 @@ func LoginCheck(username string, password string) (string, error) {
|
|||||||
return "", errors.New(errString)
|
return "", errors.New(errString)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Printf("LoginCheck retrieved user '%v' from database\n", u)
|
//log.Printf("LoginCheck retrieved user '%v' from database\n", u)
|
||||||
|
log.Printf("LoginCheck retrieved user id '%d' from database\n", u.UserId)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("u: %v\n", u)
|
//log.Printf("u: %v\n", u)
|
||||||
|
|
||||||
if !u.LdapUser {
|
if !u.LdapUser {
|
||||||
// Locally defined user, perform password verification
|
// Locally defined user, perform password verification
|
||||||
@@ -181,14 +208,22 @@ func LoginCheck(username string, password string) (string, error) {
|
|||||||
} else {
|
} else {
|
||||||
log.Printf("LoginCheck successfully verified LDAP user\n")
|
log.Printf("LoginCheck successfully verified LDAP user\n")
|
||||||
|
|
||||||
// confirm that current LDAP group membership matches a group
|
// check if user's group membership is an ldap group or not
|
||||||
err := UserLdapGroupVerify(username, password)
|
log.Printf("User id '%d' is a member of group '%d' which has ldapGroup status '%v'\n", u.UserId, u.GroupId, u.LdapGroup)
|
||||||
|
|
||||||
if err != nil {
|
// If user's group membership is an ldap group, then run UserLdapGroupVerify as we were doing before
|
||||||
// No valid group membership
|
if u.LdapGroup {
|
||||||
errString := fmt.Sprintf("ldap group membership check unsuccessful : '%s'\n", err)
|
// confirm that current LDAP group membership matches a group
|
||||||
log.Printf("LoginCheck %s\n", errString)
|
err := UserLdapGroupVerify(username, password)
|
||||||
return "", errors.New(errString)
|
|
||||||
|
if err != nil {
|
||||||
|
// No valid group membership
|
||||||
|
errString := fmt.Sprintf("ldap group membership check unsuccessful : '%s'\n", err)
|
||||||
|
log.Printf("LoginCheck %s\n", errString)
|
||||||
|
return "", errors.New(errString)
|
||||||
|
}
|
||||||
|
} else { // If user's group membership is not an ldap group, then we are fine and the login attempt was successful
|
||||||
|
log.Printf("No need to check ldap group membership since user is not a member of an ldap group\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -206,6 +241,8 @@ func LoginCheck(username string, password string) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u.UserSetLastLogin()
|
||||||
|
|
||||||
return token, nil
|
return token, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -260,6 +297,10 @@ func UserLdapNewLoginCheck(username string, password string) (User, error) {
|
|||||||
matchFound := false
|
matchFound := false
|
||||||
|
|
||||||
for _, group := range groupList {
|
for _, group := range groupList {
|
||||||
|
// Skip any groups that aren't LDAP groups
|
||||||
|
if len(group.LdapDn) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
for _, lg := range ldapGroups {
|
for _, lg := range ldapGroups {
|
||||||
if group.LdapDn == lg {
|
if group.LdapDn == lg {
|
||||||
log.Printf("Found match with groupname '%s' and LDAP group DN '%s', user is a member of group ID '%d'\n", group.GroupName, group.LdapDn, group.GroupId)
|
log.Printf("Found match with groupname '%s' and LDAP group DN '%s', user is a member of group ID '%d'\n", group.GroupName, group.LdapDn, group.GroupId)
|
||||||
@@ -281,15 +322,23 @@ func UserLdapNewLoginCheck(username string, password string) (User, error) {
|
|||||||
return u, nil
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
func (u *User) UserSetLastLogin() error {
|
||||||
// StoreLdapUser creates a user record in the database and returns the corresponding userId
|
|
||||||
func StoreLdapUser(u *User) error {
|
|
||||||
|
|
||||||
// TODO
|
u.LastLogin = time.Now().UTC()
|
||||||
|
|
||||||
|
result, err := db.NamedExec((`UPDATE users SET LastLogin = :LastLogin WHERE UserId = :UserId`), u)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("UserSetLastLogin error executing sql update : '%s'\n", err)
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
affected, _ := result.RowsAffected()
|
||||||
|
id, _ := result.LastInsertId()
|
||||||
|
log.Printf("UserSetLastLogin returned result id '%d' affecting %d row(s).\n", id, affected)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
func UserGetByID(uid uint) (User, error) {
|
func UserGetByID(uid uint) (User, error) {
|
||||||
|
|
||||||
@@ -389,7 +438,7 @@ func UserGetSafesAllowed(userId int) ([]UserSafe, error) {
|
|||||||
|
|
||||||
// join users, groups and permissions
|
// join users, groups and permissions
|
||||||
rows, err := db.Queryx(`
|
rows, err := db.Queryx(`
|
||||||
SELECT users.UserId, users.GroupId,
|
SELECT users.UserId, users.GroupId, users.UserName,
|
||||||
permissions.SafeId, permissions.ReadOnly, safes.SafeName FROM users
|
permissions.SafeId, permissions.ReadOnly, safes.SafeName FROM users
|
||||||
INNER JOIN groups ON users.GroupId = groups.GroupId
|
INNER JOIN groups ON users.GroupId = groups.GroupId
|
||||||
INNER JOIN permissions ON groups.GroupId = permissions.GroupId
|
INNER JOIN permissions ON groups.GroupId = permissions.GroupId
|
||||||
@@ -421,12 +470,13 @@ func UserGetSafesAllowed(userId int) ([]UserSafe, error) {
|
|||||||
return results, err
|
return results, err
|
||||||
}
|
}
|
||||||
//log.Printf("UserGetSafesAllowed adding record : '%+v'\n", us)
|
//log.Printf("UserGetSafesAllowed adding record : '%+v'\n", us)
|
||||||
debugPrint := utils.PrintStructContents(&us, 0)
|
//debugPrint := utils.PrintStructContents(&us, 0)
|
||||||
log.Printf("UserGetSafesAllowed adding record :\n%s\n", debugPrint)
|
//log.Printf("UserGetSafesAllowed adding record :\n%s\n", debugPrint)
|
||||||
|
|
||||||
results = append(results, us)
|
results = append(results, us)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
// For intense debugging
|
||||||
// Create a map to store column names and values
|
// Create a map to store column names and values
|
||||||
rowValues := make(map[string]interface{})
|
rowValues := make(map[string]interface{})
|
||||||
|
|
||||||
@@ -445,7 +495,7 @@ func UserGetSafesAllowed(userId int) ([]UserSafe, error) {
|
|||||||
log.Println("-----------")
|
log.Println("-----------")
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
log.Printf("UserGetSafesAllowed retrieved '%d' results\n", len(results))
|
//log.Printf("UserGetSafesAllowed retrieved '%d' results\n", len(results))
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return results, nil
|
||||||
@@ -458,7 +508,7 @@ func UserList() ([]User, error) {
|
|||||||
rows, err := db.Queryx("SELECT * FROM users")
|
rows, err := db.Queryx("SELECT * FROM users")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("QueryUsers error executing sql record : '%s'\n", err)
|
log.Printf("UserList error executing sql record : '%s'\n", err)
|
||||||
return results, err
|
return results, err
|
||||||
} else {
|
} else {
|
||||||
// parse all the results into a slice
|
// parse all the results into a slice
|
||||||
@@ -466,13 +516,13 @@ func UserList() ([]User, error) {
|
|||||||
var u User
|
var u User
|
||||||
err = rows.StructScan(&u)
|
err = rows.StructScan(&u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("QueryUsers error parsing sql record : '%s'\n", err)
|
log.Printf("UserList error parsing sql record : '%s'\n", err)
|
||||||
return results, err
|
return results, err
|
||||||
}
|
}
|
||||||
results = append(results, u)
|
results = append(results, u)
|
||||||
|
|
||||||
}
|
}
|
||||||
log.Printf("QueryUsers retrieved '%d' results\n", len(results))
|
log.Printf("UserList retrieved '%d' results\n", len(results))
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return results, nil
|
||||||
|
@@ -4,8 +4,52 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
func PrintStructContents(s interface{}, indentLevel int) string {
|
||||||
|
var result strings.Builder
|
||||||
|
|
||||||
|
val := reflect.ValueOf(s)
|
||||||
|
|
||||||
|
if val.Kind() == reflect.Ptr {
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := val.Type()
|
||||||
|
|
||||||
|
for i := 0; i < val.NumField(); i++ {
|
||||||
|
field := val.Field(i)
|
||||||
|
fieldType := typ.Field(i)
|
||||||
|
|
||||||
|
log.Printf("PrintStructContents [%d] field '%s' (%T)\n", i, field, fieldType)
|
||||||
|
|
||||||
|
indent := strings.Repeat("\t", indentLevel)
|
||||||
|
result.WriteString(fmt.Sprintf("%s%s: ", indent, fieldType.Name))
|
||||||
|
log.Printf("%s%s: \n", indent, fieldType.Name)
|
||||||
|
|
||||||
|
switch field.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
result.WriteString("\n")
|
||||||
|
foo := PrintStructContents(field.Interface(), indentLevel+1)
|
||||||
|
log.Printf("foo: %s\n", foo)
|
||||||
|
result.WriteString(foo)
|
||||||
|
case reflect.Uint64:
|
||||||
|
log.Printf("uint64 %v\n", field.Interface())
|
||||||
|
result.WriteString(fmt.Sprintf("%v\n", field.Interface()))
|
||||||
|
default:
|
||||||
|
log.Printf("default %v\n", field.Interface())
|
||||||
|
result.WriteString(fmt.Sprintf("%v\n", field.Interface()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("PrintStructContents completed\n")
|
||||||
|
|
||||||
|
return result.String()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func PrintStructContents(s interface{}, indentLevel int) string {
|
func PrintStructContents(s interface{}, indentLevel int) string {
|
||||||
var result strings.Builder
|
var result strings.Builder
|
||||||
|
|
||||||
@@ -26,8 +70,13 @@ func PrintStructContents(s interface{}, indentLevel int) string {
|
|||||||
|
|
||||||
switch field.Kind() {
|
switch field.Kind() {
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
result.WriteString("\n")
|
if fieldType.Type == reflect.TypeOf(time.Time{}) {
|
||||||
result.WriteString(PrintStructContents(field.Interface(), indentLevel+1))
|
// Handle time.Time field
|
||||||
|
result.WriteString(fmt.Sprintf("%v\n", field.Interface().(time.Time).Format("2006-12-24 15:04:05")))
|
||||||
|
} else {
|
||||||
|
result.WriteString("\n")
|
||||||
|
result.WriteString(PrintStructContents(field.Interface(), indentLevel+1))
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
result.WriteString(fmt.Sprintf("%v\n", field.Interface()))
|
result.WriteString(fmt.Sprintf("%v\n", field.Interface()))
|
||||||
}
|
}
|
||||||
@@ -53,3 +102,20 @@ func AppendIfNotExists[T Identifiable](slice []T, element T) []T {
|
|||||||
// Element with the same Id does not exist, append the new element
|
// Element with the same Id does not exist, append the new element
|
||||||
return append(slice, element)
|
return append(slice, element)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateStruct updates the values in the destination struct with values from the source struct
|
||||||
|
func UpdateStruct(dest interface{}, src interface{}) {
|
||||||
|
destValue := reflect.ValueOf(dest).Elem()
|
||||||
|
srcValue := reflect.ValueOf(src).Elem()
|
||||||
|
|
||||||
|
destType := destValue.Type()
|
||||||
|
|
||||||
|
for i := 0; i < srcValue.NumField(); i++ {
|
||||||
|
srcField := srcValue.Field(i)
|
||||||
|
|
||||||
|
destField := destValue.FieldByName(destType.Field(i).Name)
|
||||||
|
if destField.IsValid() && destField.Type() == srcField.Type() && destField.CanSet() {
|
||||||
|
destField.Set(srcField)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -8,8 +8,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
jwt "github.com/dgrijalva/jwt-go"
|
//jwt "github.com/dgrijalva/jwt-go"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
jwt "github.com/golang-jwt/jwt/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GenerateToken(user_id uint) (string, error) {
|
func GenerateToken(user_id uint) (string, error) {
|
||||||
|
BIN
www/database.png
BIN
www/database.png
Binary file not shown.
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 86 KiB |
Reference in New Issue
Block a user