Merge remote-tracking branch 'hupstream/dev' into dev

This commit is contained in:
Henry Zhang 2016-09-22 23:31:49 +08:00
commit 2136f1ad5a
9 changed files with 564 additions and 45 deletions

View File

@ -12,8 +12,6 @@ import (
"net/http/httptest"
"path/filepath"
"runtime"
// "strconv"
// "strings"
"github.com/astaxie/beego"
"github.com/dghubble/sling"
@ -63,7 +61,9 @@ func init() {
beego.Router("/api/search/", &SearchAPI{})
beego.Router("/api/projects/", &ProjectAPI{}, "get:List;post:Post;head:Head")
beego.Router("/api/projects/:id", &ProjectAPI{}, "delete:Delete;get:Get")
beego.Router("/api/users/?:id", &UserAPI{})
beego.Router("/api/users/:id([0-9]+)/password", &UserAPI{}, "put:ChangePassword")
beego.Router("/api/users/:id/sysadmin", &UserAPI{}, "put:ToggleUserAdminRole")
beego.Router("/api/projects/:id/publicity", &ProjectAPI{}, "put:ToggleProjectPublic")
beego.Router("/api/projects/:id([0-9]+)/logs/filter", &ProjectAPI{}, "post:FilterAccessLog")
beego.Router("/api/projects/:pid([0-9]+)/members/?:mid", &ProjectMemberAPI{}, "get:Get;post:Post;delete:Delete;put:Put")
@ -157,30 +157,6 @@ func (a api) ProjectsPost(prjUsr usrInfo, project apilib.ProjectReq) (int, error
return httpStatusCode, err
}
//Change password
//Implementation Notes
//Change the password on a user that already exists.
//@param userID user ID
//@param password user old and new password
//@return error
//func (a api) UsersUserIDPasswordPut (user usrInfo, userID int32, password apilib.Password) int {
func (a api) UsersUserIDPasswordPut(user usrInfo, userID int32, password apilib.Password) int {
_sling := sling.New().Put(a.basePath)
// create path and map variables
path := "/api/users/" + fmt.Sprintf("%d", userID) + "/password"
fmt.Printf("change passwd path: %s\n", path)
fmt.Printf("password %+v\n", password)
_sling = _sling.Path(path)
// body params
_sling = _sling.BodyJSON(password)
httpStatusCode, _, _ := request(_sling, jsonAcceptHeader, user)
return httpStatusCode
}
func (a api) StatisticGet(user usrInfo) (apilib.StatisticMap, error) {
_sling := sling.New().Get(a.basePath)
@ -741,21 +717,114 @@ func (a api) DeletePolicyByID(authInfo usrInfo, policyID string) (int, error) {
//}
//Get registered users of Harbor.
//func (a HarborApi) UsersGet (userName string) ([]User, error) {
//}
func (a api) UsersGet(userName string, authInfo usrInfo) (int, []apilib.User, error) {
_sling := sling.New().Get(a.basePath)
// create path and map variables
path := "/api/users/"
_sling = _sling.Path(path)
// body params
type QueryParams struct {
UserName string `url:"username, omitempty"`
}
_sling = _sling.QueryStruct(&QueryParams{UserName: userName})
httpStatusCode, body, err := request(_sling, jsonAcceptHeader, authInfo)
var successPayLoad []apilib.User
if 200 == httpStatusCode && nil == err {
err = json.Unmarshal(body, &successPayLoad)
}
return httpStatusCode, successPayLoad, err
}
//Get registered users by userid.
func (a api) UsersGetByID(userName string, authInfo usrInfo, userID int) (int, apilib.User, error) {
_sling := sling.New().Get(a.basePath)
// create path and map variables
path := "/api/users/" + fmt.Sprintf("%d", userID)
_sling = _sling.Path(path)
// body params
type QueryParams struct {
UserName string `url:"username, omitempty"`
}
_sling = _sling.QueryStruct(&QueryParams{UserName: userName})
httpStatusCode, body, err := request(_sling, jsonAcceptHeader, authInfo)
var successPayLoad apilib.User
if 200 == httpStatusCode && nil == err {
err = json.Unmarshal(body, &successPayLoad)
}
return httpStatusCode, successPayLoad, err
}
//Creates a new user account.
//func (a HarborApi) UsersPost (user User) (error) {
//}
func (a api) UsersPost(user apilib.User, authInfo ...usrInfo) (int, error) {
_sling := sling.New().Post(a.basePath)
// create path and map variables
path := "/api/users/"
_sling = _sling.Path(path)
// body params
_sling = _sling.BodyJSON(user)
var httpStatusCode int
var err error
if len(authInfo) > 0 {
httpStatusCode, _, err = request(_sling, jsonAcceptHeader, authInfo[0])
} else {
httpStatusCode, _, err = request(_sling, jsonAcceptHeader)
}
return httpStatusCode, err
}
//Update a registered user to change profile.
func (a api) UsersPut(userID int, profile apilib.UserProfile, authInfo usrInfo) (int, error) {
_sling := sling.New().Put(a.basePath)
// create path and map variables
path := "/api/users/" + fmt.Sprintf("%d", userID)
_sling = _sling.Path(path)
// body params
_sling = _sling.BodyJSON(profile)
httpStatusCode, _, err := request(_sling, jsonAcceptHeader, authInfo)
return httpStatusCode, err
}
//Update a registered user to be an administrator of Harbor.
func (a api) UsersToggleAdminRole(userID int, authInfo usrInfo, hasAdminRole int32) (int, error) {
_sling := sling.New().Put(a.basePath)
// create path and map variables
path := "/api/users/" + fmt.Sprintf("%d", userID) + "/sysadmin"
_sling = _sling.Path(path)
type QueryParams struct {
HasAdminRole int32 `json:"has_admin_role,omitempty"`
}
_sling = _sling.BodyJSON(&QueryParams{HasAdminRole: hasAdminRole})
httpStatusCode, _, err := request(_sling, jsonAcceptHeader, authInfo)
return httpStatusCode, err
}
//Update password of a registered user.
func (a api) UsersUpdatePassword(userID int, password apilib.Password, authInfo usrInfo) (int, error) {
_sling := sling.New().Put(a.basePath)
// create path and map variables
path := "/api/users/" + fmt.Sprintf("%d", userID) + "/password"
_sling = _sling.Path(path)
// body params
_sling = _sling.BodyJSON(password)
httpStatusCode, _, err := request(_sling, jsonAcceptHeader, authInfo)
return httpStatusCode, err
}
//Mark a registered user as be removed.
//func (a HarborApi) UsersUserIdDelete (userId int32) (error) {
//}
//Update a registered user to change to be an administrator of Harbor.
//func (a HarborApi) UsersUserIdPut (userId int32) (error) {
//}
func (a api) UsersDelete(userID int, authInfo usrInfo) (int, error) {
_sling := sling.New().Delete(a.basePath)
// create path and map variables
path := "/api/users/" + fmt.Sprintf("%d", userID)
_sling = _sling.Path(path)
httpStatusCode, _, err := request(_sling, jsonAcceptHeader, authInfo)
return httpStatusCode, err
}
func updateInitPassword(userID int, password string) error {
queryUser := models.User{UserID: userID}
user, err := dao.GetUser(queryUser)

View File

@ -17,7 +17,6 @@ func TestLogGet(t *testing.T) {
//prepare for test
admin := &usrInfo{"admin", "Harbor12345"}
var project apilib.ProjectReq
project.ProjectName = "my_project"
project.Public = 1

View File

@ -21,8 +21,6 @@ func TestStatisticGet(t *testing.T) {
//prepare for test
admin := &usrInfo{"admin", "Harbor12345"}
var myProCount, pubProCount, totalProCount int32
result, err := apiTest.StatisticGet(*admin)
if err != nil {

View File

@ -315,13 +315,13 @@ func (ua *UserAPI) ToggleUserAdminRole() {
// validate only validate when user register
func validate(user models.User) error {
if isIllegalLength(user.Username, 0, 20) {
if isIllegalLength(user.Username, 1, 20) {
return fmt.Errorf("Username with illegal length.")
}
if isContainIllegalChar(user.Username, []string{",", "~", "#", "$", "%"}) {
return fmt.Errorf("Username contains illegal characters.")
}
if isIllegalLength(user.Password, 0, 20) {
if isIllegalLength(user.Password, 7, 20) {
return fmt.Errorf("Password with illegal length.")
}
if err := commonValidate(user); err != nil {

398
api/user_test.go Normal file
View File

@ -0,0 +1,398 @@
package api
import (
"fmt"
"github.com/stretchr/testify/assert"
"github.com/vmware/harbor/tests/apitests/apilib"
"testing"
)
var testUser0002ID, testUser0003ID int
var testUser0002, testUser0003 apilib.User
var testUser0002Auth, testUser0003Auth *usrInfo
func TestUsersPost(t *testing.T) {
fmt.Println("Testing User Add")
assert := assert.New(t)
apiTest := newHarborAPI()
//case 1: register a new user without admin auth, expect 400, because self registration is on
fmt.Println("Register user without admin auth")
code, err := apiTest.UsersPost(testUser0002)
if err != nil {
t.Error("Error occured while add a test User", err.Error())
t.Log(err)
} else {
assert.Equal(400, code, "Add user status should be 400")
}
//case 2: register a new user with admin auth, but username is empty, expect 400
fmt.Println("Register user with admin auth, but username is empty")
code, err = apiTest.UsersPost(testUser0002, *admin)
if err != nil {
t.Error("Error occured while add a user", err.Error())
t.Log(err)
} else {
assert.Equal(400, code, "Add user status should be 400")
}
//case 3: register a new user with admin auth, but bad username format, expect 400
testUser0002.Username = "test@$"
fmt.Println("Register user with admin auth, but bad username format")
code, err = apiTest.UsersPost(testUser0002, *admin)
if err != nil {
t.Error("Error occured while add a user", err.Error())
t.Log(err)
} else {
assert.Equal(400, code, "Add user status should be 400")
}
//case 4: register a new user with admin auth, but bad userpassword format, expect 400
testUser0002.Username = "testUser0002"
fmt.Println("Register user with admin auth, but empty password.")
code, err = apiTest.UsersPost(testUser0002, *admin)
if err != nil {
t.Error("Error occured while add a user", err.Error())
t.Log(err)
} else {
assert.Equal(400, code, "Add user status should be 400")
}
//case 5: register a new user with admin auth, but email is empty, expect 400
testUser0002.Password = "testUser0002"
fmt.Println("Register user with admin auth, but email is empty")
code, err = apiTest.UsersPost(testUser0002, *admin)
if err != nil {
t.Error("Error occured while add a user", err.Error())
t.Log(err)
} else {
assert.Equal(400, code, "Add user status should be 400")
}
//case 6: register a new user with admin auth, but bad email format, expect 400
testUser0002.Email = "test..."
fmt.Println("Register user with admin auth, but bad email format")
code, err = apiTest.UsersPost(testUser0002, *admin)
if err != nil {
t.Error("Error occured while add a user", err.Error())
t.Log(err)
} else {
assert.Equal(400, code, "Add user status should be 400")
}
//case 7: register a new user with admin auth, but userrealname is empty, expect 400
/*
testUser0002.Email = "testUser0002@mydomain.com"
fmt.Println("Register user with admin auth, but user realname is empty")
code, err = apiTest.UsersPost(testUser0002, *admin)
if err != nil {
t.Error("Error occured while add a user", err.Error())
t.Log(err)
} else {
assert.Equal(400, code, "Add user status should be 400")
}
*/
//case 8: register a new user with admin auth, but bad userrealname format, expect 400
testUser0002.Email = "testUser0002@mydomain.com"
testUser0002.Realname = "test$com"
fmt.Println("Register user with admin auth, but bad user realname format")
code, err = apiTest.UsersPost(testUser0002, *admin)
if err != nil {
t.Error("Error occured while add a user", err.Error())
t.Log(err)
} else {
assert.Equal(400, code, "Add user status should be 400")
}
//case 9: register a new user with admin auth, but bad user comment, expect 400
testUser0002.Realname = "testUser0002"
testUser0002.Comment = "vmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm"
fmt.Println("Register user with admin auth, but bad user comment format")
code, err = apiTest.UsersPost(testUser0002, *admin)
if err != nil {
t.Error("Error occured while add a user", err.Error())
t.Log(err)
} else {
assert.Equal(400, code, "Add user status should be 400")
}
//case 10: register a new user with admin auth, expect 201
fmt.Println("Register user with admin auth, right parameters")
testUser0002.Comment = "test user"
code, err = apiTest.UsersPost(testUser0002, *admin)
if err != nil {
t.Error("Error occured while add a user", err.Error())
t.Log(err)
} else {
assert.Equal(201, code, "Add user status should be 201")
}
//case 11: register duplicate user with admin auth, expect 409
fmt.Println("Register duplicate user with admin auth")
code, err = apiTest.UsersPost(testUser0002, *admin)
if err != nil {
t.Error("Error occured while add a user", err.Error())
t.Log(err)
} else {
assert.Equal(409, code, "Add user status should be 409")
}
//case 12: register a new user with admin auth, but duplicate email, expect 409
fmt.Println("Register user with admin auth, but duplicate email")
testUser0002.Username = "testUsertest"
testUser0002.Email = "testUser0002@mydomain.com"
code, err = apiTest.UsersPost(testUser0002, *admin)
if err != nil {
t.Error("Error occured while add a user", err.Error())
t.Log(err)
} else {
assert.Equal(409, code, "Add user status should be 409")
}
}
func TestUsersGet(t *testing.T) {
fmt.Println("Testing User Get")
assert := assert.New(t)
apiTest := newHarborAPI()
testUser0002.Username = "testUser0002"
//case 1: Get user2 with common auth, but no userid in path, expect 403
testUser0002Auth = &usrInfo{"testUser0002", "testUser0002"}
code, users, err := apiTest.UsersGet(testUser0002.Username, *testUser0002Auth)
if err != nil {
t.Error("Error occured while get users", err.Error())
t.Log(err)
} else {
assert.Equal(403, code, "Get users status should be 403")
}
//case 2: Get user2 with admin auth, expect 200
code, users, err = apiTest.UsersGet(testUser0002.Username, *admin)
if err != nil {
t.Error("Error occured while get users", err.Error())
t.Log(err)
} else {
assert.Equal(200, code, "Get users status should be 200")
assert.Equal(1, len(users), "Get users record should be 1 ")
testUser0002ID = users[0].UserId
}
}
func TestUsersGetByID(t *testing.T) {
fmt.Println("Testing User GetByID")
assert := assert.New(t)
apiTest := newHarborAPI()
//case 1: Get user2 with userID and his own auth, expect 200
code, user, err := apiTest.UsersGetByID(testUser0002.Username, *testUser0002Auth, testUser0002ID)
if err != nil {
t.Error("Error occured while get users", err.Error())
t.Log(err)
} else {
assert.Equal(200, code, "Get users status should be 200")
assert.Equal(testUser0002.Username, user.Username, "Get users username should be equal")
assert.Equal(testUser0002.Email, user.Email, "Get users email should be equal")
}
//case 2: Get user2 with user3 auth, expect 403
testUser0003.Username = "testUser0003"
testUser0003.Email = "testUser0003@mydomain.com"
testUser0003.Password = "testUser0003"
testUser0003.Realname = "testUser0003"
code, err = apiTest.UsersPost(testUser0003, *admin)
if err != nil {
t.Error("Error occured while add a user", err.Error())
t.Log(err)
} else {
assert.Equal(201, code, "Add user status should be 201")
}
testUser0003Auth = &usrInfo{"testUser0003", "testUser0003"}
code, user, err = apiTest.UsersGetByID(testUser0002.Username, *testUser0003Auth, testUser0002ID)
if err != nil {
t.Error("Error occured while get users", err.Error())
t.Log(err)
} else {
assert.Equal(403, code, "Get users status should be 403")
}
//case 3: Get user that does not exist with user2 auth, expect 404 not found.
code, user, err = apiTest.UsersGetByID(testUser0002.Username, *testUser0002Auth, 1000)
if err != nil {
t.Error("Error occured while change user profile", err.Error())
t.Log(err)
} else {
assert.Equal(404, code, "Get users status should be 404")
}
// Get user3ID in order to delete at the last of the test
code, users, err := apiTest.UsersGet(testUser0003.Username, *admin)
if err != nil {
t.Error("Error occured while change user profile", err.Error())
t.Log(err)
} else {
assert.Equal(200, code, "Get users status should be 200")
assert.Equal(1, len(users), "Get users record should be 1")
testUser0003ID = users[0].UserId
}
}
func TestUsersPut(t *testing.T) {
fmt.Println("Testing User Put")
assert := assert.New(t)
apiTest := newHarborAPI()
var profile apilib.UserProfile
//case 1: change user2 profile with user3 auth
code, err := apiTest.UsersPut(testUser0002ID, profile, *testUser0003Auth)
if err != nil {
t.Error("Error occured while change user profile", err.Error())
t.Log(err)
} else {
assert.Equal(403, code, "Get users status should be 403")
}
//case 2: change user2 profile with user2 auth, but bad parameters format.
code, err = apiTest.UsersPut(testUser0002ID, profile, *testUser0002Auth)
if err != nil {
t.Error("Error occured while change user profile", err.Error())
t.Log(err)
} else {
assert.Equal(400, code, "Get users status should be 400")
}
//case 3: change user2 profile with user2 auth, but duplicate email.
profile.Realname = "test user"
profile.Email = "testUser0003@mydomain.com"
profile.Comment = "change profile"
code, err = apiTest.UsersPut(testUser0002ID, profile, *testUser0002Auth)
if err != nil {
t.Error("Error occured while change user profile", err.Error())
t.Log(err)
} else {
assert.Equal(409, code, "Get users status should be 409")
}
//case 4: change user2 profile with user2 auth, right parameters format.
profile.Realname = "test user"
profile.Email = "testUser0002@vmware.com"
profile.Comment = "change profile"
code, err = apiTest.UsersPut(testUser0002ID, profile, *testUser0002Auth)
if err != nil {
t.Error("Error occured while change user profile", err.Error())
t.Log(err)
} else {
assert.Equal(200, code, "Get users status should be 200")
}
}
func TestUsersToggleAdminRole(t *testing.T) {
fmt.Println("Testing Toggle User Admin Role")
assert := assert.New(t)
apiTest := newHarborAPI()
//case 1: toggle user2 admin role without admin auth
code, err := apiTest.UsersToggleAdminRole(testUser0002ID, *testUser0002Auth, int32(1))
if err != nil {
t.Error("Error occured while change user profile", err.Error())
t.Log(err)
} else {
assert.Equal(403, code, "Get users status should be 403")
}
//case 2: toggle user2 admin role with admin auth
code, err = apiTest.UsersToggleAdminRole(testUser0002ID, *admin, int32(1))
if err != nil {
t.Error("Error occured while change user profile", err.Error())
t.Log(err)
} else {
assert.Equal(200, code, "Get users status should be 200")
}
}
func TestUsersUpdatePassword(t *testing.T) {
fmt.Println("Testing Update User Password")
assert := assert.New(t)
apiTest := newHarborAPI()
password := apilib.Password{OldPassword: "", NewPassword: ""}
//case 1: update user2 password with user3 auth
code, err := apiTest.UsersUpdatePassword(testUser0002ID, password, *testUser0003Auth)
if err != nil {
t.Error("Error occured while update user password", err.Error())
t.Log(err)
} else {
assert.Equal(403, code, "Update user password status should be 403")
}
//case 2: update user2 password with admin auth, but oldpassword is empty
code, err = apiTest.UsersUpdatePassword(testUser0002ID, password, *admin)
if err != nil {
t.Error("Error occured while change user profile", err.Error())
t.Log(err)
} else {
assert.Equal(400, code, "Get users status should be 400")
}
//case 3: update user2 password with admin auth, but oldpassword is wrong
password.OldPassword = "000"
code, err = apiTest.UsersUpdatePassword(testUser0002ID, password, *admin)
if err != nil {
t.Error("Error occured while change user profile", err.Error())
t.Log(err)
} else {
assert.Equal(403, code, "Get users status should be 403")
}
//case 4: update user2 password with admin auth, but newpassword is empty
password.OldPassword = "testUser0002"
code, err = apiTest.UsersUpdatePassword(testUser0002ID, password, *admin)
if err != nil {
t.Error("Error occured while change user profile", err.Error())
t.Log(err)
} else {
assert.Equal(400, code, "Get users status should be 400")
}
//case 5: update user2 password with admin auth, right parameters
password.NewPassword = "TestUser0002"
code, err = apiTest.UsersUpdatePassword(testUser0002ID, password, *admin)
if err != nil {
t.Error("Error occured while change user profile", err.Error())
t.Log(err)
} else {
assert.Equal(200, code, "Get users status should be 200")
testUser0002.Password = password.NewPassword
testUser0002Auth.Passwd = password.NewPassword
}
}
func TestUsersDelete(t *testing.T) {
fmt.Println("Testing User Delete")
assert := assert.New(t)
apiTest := newHarborAPI()
//case 1:delete user without admin auth
code, err := apiTest.UsersDelete(testUser0002ID, *testUser0003Auth)
if err != nil {
t.Error("Error occured while delete a testUser", err.Error())
t.Log(err)
} else {
assert.Equal(403, code, "Delete testUser status should be 403")
}
//case 2: delete user with admin auth, user2 has already been toggled to admin, but can not delete himself
code, err = apiTest.UsersDelete(testUser0002ID, *testUser0002Auth)
if err != nil {
t.Error("Error occured while delete a testUser", err.Error())
t.Log(err)
} else {
assert.Equal(403, code, "Delete testUser status should be 403")
}
//case 3: delete user with admin auth
code, err = apiTest.UsersDelete(testUser0002ID, *admin)
if err != nil {
t.Error("Error occured while delete a testUser", err.Error())
t.Log(err)
} else {
assert.Equal(200, code, "Delete testUser status should be 200")
}
//delete user3 with admin auth
code, err = apiTest.UsersDelete(testUser0003ID, *admin)
if err != nil {
t.Error("Error occured while delete a testUser", err.Error())
t.Log(err)
} else {
assert.Equal(200, code, "Delete testUser status should be 200")
}
}

View File

@ -77,7 +77,7 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
attrName := os.Getenv("LDAP_UID")
filter := os.Getenv("LDAP_FILTER")
if filter != "" {
filter = "(&(" + filter + ")(" + attrName + "=" + m.Principal + "))"
filter = "(&" + filter + "(" + attrName + "=" + m.Principal + "))"
} else {
filter = "(" + attrName + "=" + m.Principal + ")"
}

View File

@ -1,6 +1,6 @@
# Harbor upgrade and database migration guide
When upgrading your existing Habor instance to a newer version, you may need to migrate the data in your database. Refer to [change log](changelog.md) to find out whether there is any change in the database. If there is, you should go through the database migration process. Since the migration may alter the database schema, you should **always** back up your data before any migration.
When upgrading your existing Habor instance to a newer version, you may need to migrate the data in your database. Refer to [change log](../migration/changelog.md) to find out whether there is any change in the database. If there is, you should go through the database migration process. Since the migration may alter the database schema, you should **always** back up your data before any migration.
*If your install Harbor for the first time, or the database version is the same as that of the lastest version, you do not need any database migration.*

View File

@ -524,7 +524,7 @@ paths:
description: Only email, realname and comment can be modified.
required: true
schema:
$ref: '#/definitions/User'
$ref: '#/definitions/UserProfile'
tags:
- Products
responses:
@ -611,6 +611,12 @@ paths:
format: int
required: true
description: Registered user ID
- name: has_admin_role
in: body
description: Toggle a user to admin or not.
required: true
schema:
$ref: '#/definitions/HasAdminRole'
tags:
- Products
responses:
@ -1682,3 +1688,21 @@ definitions:
password:
type: string
description: The target server password.
HasAdminRole:
type: object
properties:
has_admin_role:
type: integer
description: 1-has admin, 0-not.
UserProfile:
type: object
properties:
email:
type: string
description: The new email.
realname:
type: string
description: The new realname.
comment:
type: string
description: The new comment.

View File

@ -0,0 +1,31 @@
/*
* Harbor API
*
* These APIs provide services for manipulating Harbor project.
*
* OpenAPI spec version: 0.3.0
*
* Generated by: https://github.com/swagger-api/swagger-codegen.git
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package apilib
type UserProfile struct {
Email string `json:"email,omitempty"`
Realname string `json:"realname,omitempty"`
Comment string `json:"comment,omitempty"`
}