Skip to content

Commit c4ac0c6

Browse files
Merge pull request #485 from IBM/PET-349
Pet 349 - add UUID param to user org sshkey image API and add build version
2 parents b92a25a + f07aff9 commit c4ac0c6

File tree

10 files changed

+107
-19
lines changed

10 files changed

+107
-19
lines changed

web/src/apis/image.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
. "web/src/common"
1616
"web/src/model"
1717
"web/src/routes"
18+
"web/src/utils"
1819

1920
"github.com/gin-gonic/gin"
2021
)
@@ -43,6 +44,7 @@ type ImageListResponse struct {
4344
}
4445

4546
type ImagePayload struct {
47+
UUID string `json:"uuid,omitempty" binding:"omitempty"`
4648
Name string `json:"name" binding:"required,min=2,max=32"`
4749
OSCode string `json:"os_code" binding:"required,oneof=linux windows other"`
4850
DownloadURL string `json:"download_url" binding:"required,http_url"`
@@ -157,8 +159,13 @@ func (v *ImageAPI) Create(c *gin.Context) {
157159
}
158160
instanceID = instance.ID
159161
}
162+
if payload.UUID != "" && !utils.IsUUID(payload.UUID) {
163+
logger.Errorf("Invalid input UUID %s", payload.UUID)
164+
ErrorResponse(c, http.StatusBadRequest, "Invalid input UUID", nil)
165+
return
166+
}
160167
logger.Debugf("Creating image with payload %+v", payload)
161-
image, err := imageAdmin.Create(ctx, payload.OSCode, payload.Name, payload.OSVersion, "kvm-x86_64", payload.User, payload.DownloadURL, "x86_64", true, instanceID)
168+
image, err := imageAdmin.Create(ctx, payload.OSCode, payload.Name, payload.OSVersion, "kvm-x86_64", payload.User, payload.DownloadURL, "x86_64", true, instanceID, payload.UUID)
162169
if err != nil {
163170
logger.Errorf("Not able to create image %+v", err)
164171
ErrorResponse(c, http.StatusBadRequest, "Not able to create", err)

web/src/apis/key.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
. "web/src/common"
1616
"web/src/model"
1717
"web/src/routes"
18+
"web/src/utils"
1819

1920
"github.com/gin-gonic/gin"
2021
)
@@ -40,6 +41,7 @@ type KeyListResponse struct {
4041
type KeyPayload struct {
4142
Name string `json:"name" binding:"required,min=2,max=32"`
4243
PublicKey string `json:"public_key" binding:"required,min=4,max=4096"`
44+
UUID string `json:"uuid,omitempty" binding:"omitempty"`
4345
}
4446

4547
type KeyPatchPayload struct {
@@ -129,7 +131,12 @@ func (v *KeyAPI) Create(c *gin.Context) {
129131
ErrorResponse(c, http.StatusBadRequest, "Invalid input JSON", err)
130132
return
131133
}
132-
key, err := keyAdmin.Create(ctx, payload.Name, payload.PublicKey)
134+
if payload.UUID != "" && !utils.IsUUID(payload.UUID) {
135+
logger.Errorf("Invalid input UUID %s", payload.UUID)
136+
ErrorResponse(c, http.StatusBadRequest, "Invalid input UUID", nil)
137+
return
138+
}
139+
key, err := keyAdmin.Create(ctx, payload.Name, payload.PublicKey, payload.UUID)
133140
if err != nil {
134141
ErrorResponse(c, http.StatusBadRequest, "Not able to create", err)
135142
return

web/src/apis/user.go

+21-5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
. "web/src/common"
1616
"web/src/model"
1717
"web/src/routes"
18+
"web/src/utils"
1819

1920
"github.com/gin-gonic/gin"
2021
)
@@ -25,13 +26,14 @@ var userAdmin = &routes.UserAdmin{}
2526
type UserAPI struct{}
2627

2728
type UserPayload struct {
28-
Username string `json:"username,required" binding:"required,min=2"`
29-
Password string `json:"password,required" binding:"required,min=8,max=32"`
29+
Username string `json:"username" binding:"required,min=2"`
30+
Password string `json:"password" binding:"required,min=8,max=32"`
31+
ID string `json:"id,omitempty" binding:"omitempty"`
3032
Org *BaseReference `json:"org,omitempty" binding:"omitempty"`
3133
}
3234

3335
type UserPatchPayload struct {
34-
Password string `json:"password,required" binding:"required,min=6"`
36+
Password string `json:"password" binding:"required,min=6"`
3537
}
3638

3739
type UserResponse struct {
@@ -171,13 +173,27 @@ func (v *UserAPI) Create(c *gin.Context) {
171173
logger.Debugf("Creating user with %+v", payload)
172174
username := payload.Username
173175
password := payload.Password
174-
user, err := userAdmin.Create(ctx, username, password)
176+
// PET-349 in case want to have same uuid for user and org for all regions
177+
userUUID := payload.ID // optional, if not provided, will generate a new one
178+
if userUUID != "" && !utils.IsUUID(userUUID) {
179+
logger.Errorf("Invalid user uuid: %s", userUUID)
180+
ErrorResponse(c, http.StatusBadRequest, "Invalid user uuid", nil)
181+
return
182+
}
183+
184+
user, err := userAdmin.Create(ctx, username, password, userUUID)
175185
if err != nil {
176186
logger.Errorf("Failed to create user: %+v", err)
177187
ErrorResponse(c, http.StatusInternalServerError, "Failed to create user", err)
178188
return
179189
}
180-
org, err := orgAdmin.Create(ctx, username, username)
190+
orgUUID := payload.Org.ID // optional, if not provided, will generate a new one
191+
if orgUUID != "" && !utils.IsUUID(orgUUID) {
192+
logger.Errorf("Invalid org uuid: %s", orgUUID)
193+
ErrorResponse(c, http.StatusBadRequest, "Invalid org uuid", nil)
194+
return
195+
}
196+
org, err := orgAdmin.Create(ctx, username, username, orgUUID)
181197
if err != nil {
182198
logger.Errorf("Failed to create org: %+v", err)
183199
ErrorResponse(c, http.StatusInternalServerError, "Failed to create org", err)

web/src/routes/admin.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ func adminInit() {
4545
defer func() { DB = dbFunc }()
4646
DB = func() *gorm.DB { return db }
4747
password := adminPassword()
48-
user, err = userAdmin.Create(ctx, username, password)
48+
user, err = userAdmin.Create(ctx, username, password, "")
4949
if err != nil {
5050
return
5151
}
52-
org, err = orgAdmin.Create(ctx, username, username)
52+
org, err = orgAdmin.Create(ctx, username, username, "")
5353
if err != nil {
5454
return
5555
}

web/src/routes/image.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
. "web/src/common"
1919
"web/src/dbs"
2020
"web/src/model"
21+
"web/src/utils"
2122

2223
"github.com/go-macaron/session"
2324
macaron "gopkg.in/macaron.v1"
@@ -36,7 +37,7 @@ func FileExist(filename string) bool {
3637
return !os.IsNotExist(err)
3738
}
3839

39-
func (a *ImageAdmin) Create(ctx context.Context, osCode, name, osVersion, virtType, userName, url, architecture string, qaEnabled bool, instID int64) (image *model.Image, err error) {
40+
func (a *ImageAdmin) Create(ctx context.Context, osCode, name, osVersion, virtType, userName, url, architecture string, qaEnabled bool, instID int64, uuid string) (image *model.Image, err error) {
4041
logger.Debugf("Creating image %s %s %s %s %s %s %s %t %d", osCode, name, osVersion, virtType, userName, url, architecture, qaEnabled, instID)
4142
memberShip := GetMemberShip(ctx)
4243
ctx, db, newTransaction := StartTransaction(ctx)
@@ -78,6 +79,10 @@ func (a *ImageAdmin) Create(ctx context.Context, osCode, name, osVersion, virtTy
7879
Architecture: architecture,
7980
QAEnabled: qaEnabled,
8081
}
82+
if uuid != "" {
83+
logger.Debugf("Creating image with UUID %s", uuid)
84+
image.UUID = uuid
85+
}
8186
}
8287
logger.Debugf("Creating image %+v", image)
8388
err = db.Create(image).Error
@@ -344,6 +349,12 @@ func (v *ImageView) Create(c *macaron.Context, store session.Store) {
344349
}
345350
redirectTo := "../images"
346351
osCode := c.QueryTrim("osCode")
352+
uuid := c.QueryTrim("uuid")
353+
if uuid != "" && !utils.IsUUID(uuid) {
354+
c.Data["ErrorMsg"] = "Invalid UUID"
355+
c.HTML(http.StatusBadRequest, "error")
356+
return
357+
}
347358
name := c.QueryTrim("name")
348359
url := c.QueryTrim("url")
349360
instance := c.QueryInt64("instance")
@@ -352,7 +363,7 @@ func (v *ImageView) Create(c *macaron.Context, store session.Store) {
352363
userName := c.QueryTrim("userName")
353364
qaEnabled := true
354365
architecture := "x86_64"
355-
_, err := imageAdmin.Create(c.Req.Context(), osCode, name, osVersion, virtType, userName, url, architecture, qaEnabled, instance)
366+
_, err := imageAdmin.Create(c.Req.Context(), osCode, name, osVersion, virtType, userName, url, architecture, qaEnabled, instance, uuid)
356367
if err != nil {
357368
logger.Error("Create image failed", err)
358369
c.Data["ErrorMsg"] = err.Error()

web/src/routes/key.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func (a *KeyAdmin) CreateKeyPair(ctx context.Context) (publicKey, fingerPrint, p
6262
return
6363
}
6464

65-
func (a *KeyAdmin) Create(ctx context.Context, name, publicKey string) (key *model.Key, err error) {
65+
func (a *KeyAdmin) Create(ctx context.Context, name, publicKey, uuid string) (key *model.Key, err error) {
6666
memberShip := GetMemberShip(ctx)
6767
permit := memberShip.CheckPermission(model.Writer)
6868
if !permit {
@@ -84,6 +84,10 @@ func (a *KeyAdmin) Create(ctx context.Context, name, publicKey string) (key *mod
8484
}
8585
}()
8686
key = &model.Key{Model: model.Model{Creater: memberShip.UserID}, Owner: memberShip.OrgID, Name: name, PublicKey: publicKey, FingerPrint: fingerPrint}
87+
if uuid != "" {
88+
logger.Infof("Creating new ssh key with uuid %s", uuid)
89+
key.UUID = uuid
90+
}
8791
err = db.Create(key).Error
8892
if err != nil {
8993
logger.Error("DB failed to create key, %v", err)
@@ -313,7 +317,7 @@ func (v *KeyView) Confirm(c *macaron.Context, store session.Store) {
313317
ctx := c.Req.Context()
314318
name := c.QueryTrim("name")
315319
publicKey := c.QueryTrim("pubkey")
316-
_, err := keyAdmin.Create(ctx, name, publicKey)
320+
_, err := keyAdmin.Create(ctx, name, publicKey, "")
317321
if err != nil {
318322
logger.Error("Failed to create key ", err)
319323
c.Data["ErrorMsg"] = err.Error()
@@ -407,7 +411,7 @@ func (v *KeyView) Create(c *macaron.Context, store session.Store) {
407411
name := c.QueryTrim("name")
408412
if c.QueryTrim("pubkey") != "" {
409413
publicKey := c.QueryTrim("pubkey")
410-
_, err := keyAdmin.Create(ctx, name, publicKey)
414+
_, err := keyAdmin.Create(ctx, name, publicKey, "")
411415
if err != nil {
412416
logger.Error("failed to create key")
413417
c.Data["ErrorMsg"] = err.Error()

web/src/routes/org.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type OrgAdmin struct {
3737

3838
type OrgView struct{}
3939

40-
func (a *OrgAdmin) Create(ctx context.Context, name, owner string) (org *model.Organization, err error) {
40+
func (a *OrgAdmin) Create(ctx context.Context, name, owner, uuid string) (org *model.Organization, err error) {
4141
memberShip := GetMemberShip(ctx)
4242
permit := memberShip.CheckPermission(model.Admin)
4343
if name != "admin" && !permit {
@@ -62,6 +62,10 @@ func (a *OrgAdmin) Create(ctx context.Context, name, owner string) (org *model.O
6262
Owner: user.ID,
6363
Name: name,
6464
}
65+
if uuid != "" {
66+
org.UUID = uuid
67+
logger.Infof("Create org with uuid: %s", uuid)
68+
}
6569
err = db.Create(org).Error
6670
if err != nil {
6771
logger.Error("DB failed to create organization ", err)
@@ -488,7 +492,7 @@ func (v *OrgView) Create(c *macaron.Context, store session.Store) {
488492
redirectTo := "../orgs"
489493
name := c.QueryTrim("orgname")
490494
owner := c.QueryTrim("owner")
491-
_, err := orgAdmin.Create(c.Req.Context(), name, owner)
495+
_, err := orgAdmin.Create(c.Req.Context(), name, owner, "")
492496
if err != nil {
493497
logger.Error("Failed to create organization, %v", err)
494498
c.Data["ErrorMsg"] = err.Error()

web/src/routes/user.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type UserAdmin struct{}
3737

3838
type UserView struct{}
3939

40-
func (a *UserAdmin) Create(ctx context.Context, username, password string) (user *model.User, err error) {
40+
func (a *UserAdmin) Create(ctx context.Context, username, password, uuid string) (user *model.User, err error) {
4141
memberShip := GetMemberShip(ctx)
4242
permit := memberShip.CheckPermission(model.Admin)
4343
if username != "admin" && !permit {
@@ -55,6 +55,10 @@ func (a *UserAdmin) Create(ctx context.Context, username, password string) (user
5555
return
5656
}
5757
user = &model.User{Model: model.Model{Creater: memberShip.UserID}, Username: username, Password: password}
58+
if uuid != "" {
59+
logger.Infof("Creating new user with uuid %s", uuid)
60+
user.UUID = uuid
61+
}
5862
err = db.Create(user).Error
5963
if err != nil {
6064
logger.Error("DB failed to create user, %v", err)
@@ -565,13 +569,13 @@ func (v *UserView) Create(c *macaron.Context, store session.Store) {
565569
c.HTML(http.StatusBadRequest, "error")
566570
return
567571
}
568-
_, err := userAdmin.Create(c.Req.Context(), username, password)
572+
_, err := userAdmin.Create(c.Req.Context(), username, password, "")
569573
if err != nil {
570574
logger.Error("Failed to create user, %v", err)
571575
c.HTML(500, "500")
572576
return
573577
}
574-
_, err = orgAdmin.Create(c.Req.Context(), username, username)
578+
_, err = orgAdmin.Create(c.Req.Context(), username, username, "")
575579
if err != nil {
576580
logger.Error("Failed to create organization, %v", err)
577581
c.HTML(500, "500")

web/src/utils/uuid.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
Copyright <holder> All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
6+
*/
7+
8+
package utils
9+
10+
import (
11+
"regexp"
12+
)
13+
14+
// ValidateUUID base on RFC 4122(uuid v4)
15+
func ValidateUUID(uuid string) bool {
16+
r := regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$")
17+
return r.MatchString(uuid)
18+
}
19+
20+
// validUUID is used to check if a given string looks like a UUID
21+
var validUUID = regexp.MustCompile(`(?i)^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$`)
22+
23+
// IsUUID returns true if the given string is a valid UUID.
24+
func IsUUID(str string) bool {
25+
const uuidLen = 36
26+
if len(str) != uuidLen {
27+
return false
28+
}
29+
30+
return validUUID.MatchString(str)
31+
}

web/templates/images_new.tmpl

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
<label for="name">{{.i18n.Tr "Name"}}</label>
2727
<input id="name" name="name" autofocus required>
2828
</div>
29+
<div class="inline field">
30+
<label for="uuid">{{.i18n.Tr "UUID"}}</label>
31+
<input id="uuid" name="uuid" autocomplete="off">
32+
</div>
2933
<div class="inline field">
3034
<label for="instance">{{.i18n.Tr "From Instance"}}</label>
3135
<select name="instance" id="instance" class="ui selection dropdown">

0 commit comments

Comments
 (0)