Files
netmaker/models/enrollment_key.go
Abhishek Kondur edda2868fc NM-163: Users, Groups, Roles, Networks and Hosts Table Migration (#3910)
* feat(go): add user schema;

* feat(go): migrate to user schema;

* feat(go): add audit fields;

* feat(go): remove unused fields from the network model;

* feat(go): add network schema;

* feat(go): migrate to network schema;

* refactor(go): add comment to clarify migration logic;

* fix(go): test failures;

* fix(go): test failures;

* feat(go): change membership table to store memberships at all scopes;

* feat(go): add schema for access grants;

* feat(go): remove nameservers from new networks table; ensure db passed for schema functions;

* feat(go): set max conns for sqlite to 1;

* fix(go): issues updating user account status;

* refactor(go): remove converters and access grants;

* refactor(go): add json tags in schema models;

* refactor(go): rename file to migrate_v1_6_0.go;

* refactor(go): add user groups and user roles tables; use schema tables;

* refactor(go): inline get and list from schema package;

* refactor(go): inline get network and list users from schema package;

* fix(go): staticcheck issues;

* fix(go): remove test not in use; fix test case;

* fix(go): validate network;

* fix(go): resolve static checks;

* fix(go): new models errors;

* fix(go): test errors;

* fix(go): handle no records;

* fix(go): add validations for user object;

* fix(go): set correct extclient status;

* fix(go): test error;

* feat(go): make schema the base package;

* feat(go): add host schema;

* feat(go): use schema host everywhere;

* feat(go): inline get host, list hosts and delete host;

* feat(go): use non-ptr value;

* feat(go): use save to upsert all fields;

* feat(go): use save to upsert all fields;

* feat(go): save turn endpoint as string;

* feat(go): check for gorm error record not found;

* fix(go): test failures;

* fix(go): update all network fields;

* fix(go): update all network fields;

* feat(go): add paginated list networks api;

* feat(go): add paginated list users api;

* feat(go): add paginated list hosts api;

* feat(go): add pagination to list groups api;

* fix(go): comment;

* fix(go): implement marshal and unmarshal text for custom types;

* fix(go): implement marshal and unmarshal json for custom types;

* fix(go): just use the old model for unmarshalling;

* fix(go): implement marshal and unmarshal json for custom types;

* feat(go): remove paginated list networks api;

* feat(go): use custom paginated response object;

* fix(go): ensure default values for page and per_page are used when not passed;

* fix(go): rename v1.6.0 to v1.5.1;

* fix(go): check for gorm.ErrRecordNotFound instead of database.IsEmptyRecord;

* fix(go): use host id, not pending host id;

* feat(go): add filters to paginated apis;

* feat(go): add filters to paginated apis;

* feat(go): remove check for max username length;

* feat(go): add filters to count as well;

* feat(go): use library to check email address validity;

* feat(go): ignore pagination if params not passed;

* fix(go): pagination issues;

* fix(go): check exists before using;

* fix(go): remove debug log;

* fix(go): use gorm err record not found;

* fix(go): use gorm err record not found;

* fix(go): use user principal name when creating pending user;

* fix(go): use schema package for consts;

* fix(go): prevent disabling superadmin user;

Co-authored-by: tenki-reviewer[bot] <262613592+tenki-reviewer[bot]@users.noreply.github.com>

* fix(go): swap is admin and is superadmin;

Co-authored-by: tenki-reviewer[bot] <262613592+tenki-reviewer[bot]@users.noreply.github.com>

* fix(go): remove dead code block;

https://github.com/gravitl/netmaker/pull/3910#discussion_r2928837937

* fix(go): incorrect message when trying to disable self;

https://github.com/gravitl/netmaker/pull/3910#discussion_r2928837934

* fix(go): use correct header;

Co-authored-by: tenki-reviewer[bot] <262613592+tenki-reviewer[bot]@users.noreply.github.com>

* fix(go): return after error response;

Co-authored-by: tenki-reviewer[bot] <262613592+tenki-reviewer[bot]@users.noreply.github.com>

* fix(go): use correct order of params;

https://github.com/gravitl/netmaker/pull/3910#discussion_r2929593036

* fix(go): set default values for page and page size; use v2 instead of /list;

* Update logic/auth.go

Co-authored-by: tenki-reviewer[bot] <262613592+tenki-reviewer[bot]@users.noreply.github.com>

* Update schema/user_roles.go

Co-authored-by: tenki-reviewer[bot] <262613592+tenki-reviewer[bot]@users.noreply.github.com>

* fix(go): syntax error;

* fix(go): set default values when page and per_page are not passed or 0;

* fix(go): use uuid.parse instead of uuid.must parse;

* fix(go): review errors;

* fix(go): review errors;

* Update controllers/user.go

Co-authored-by: tenki-reviewer[bot] <262613592+tenki-reviewer[bot]@users.noreply.github.com>

* Update controllers/user.go

Co-authored-by: tenki-reviewer[bot] <262613592+tenki-reviewer[bot]@users.noreply.github.com>

* NM-163: fix errors:

* Update db/types/options.go

Co-authored-by: tenki-reviewer[bot] <262613592+tenki-reviewer[bot]@users.noreply.github.com>

* fix(go): persist return user in event;

* Update db/types/options.go

Co-authored-by: tenki-reviewer[bot] <262613592+tenki-reviewer[bot]@users.noreply.github.com>

* NM-163: duplicate lines of code

* NM-163: fix(go): fix missing return and filter parsing in user controller

- Add missing return after error response in updateUserAccountStatus
  to prevent double-response and spurious ext-client side-effects
- Use switch statements in listUsers to skip unrecognized
  account_status and mfa_status filter values

* fix(go): check for both min and max page size;

* fix(go): enclose transfer superadmin in transaction;

* fix(go): review errors;

* fix(go): remove free tier checks;

* fix(go): review fixes;

---------

Co-authored-by: VishalDalwadi <dalwadivishal26@gmail.com>
Co-authored-by: Vishal Dalwadi <51291657+VishalDalwadi@users.noreply.github.com>
Co-authored-by: tenki-reviewer[bot] <262613592+tenki-reviewer[bot]@users.noreply.github.com>
2026-03-17 19:36:52 +05:30

117 lines
3.5 KiB
Go

package models
import (
"errors"
"fmt"
"time"
"github.com/google/uuid"
"github.com/gravitl/netmaker/schema"
)
const (
Undefined KeyType = iota
TimeExpiration
Uses
Unlimited
)
var (
ErrNilEnrollmentKey = errors.New("enrollment key is nil")
ErrNilNetworksEnrollmentKey = errors.New("enrollment key networks is nil")
ErrNilTagsEnrollmentKey = errors.New("enrollment key tags is nil")
ErrInvalidEnrollmentKey = errors.New("enrollment key is not valid")
ErrInvalidEnrollmentKeyValue = errors.New("enrollment key value is not valid")
)
// KeyType - the type of enrollment key
type KeyType int
// String - returns the string representation of a KeyType
func (k KeyType) String() string {
return [...]string{"Undefined", "TimeExpiration", "Uses", "Unlimited"}[k]
}
// EnrollmentToken - the tokenized version of an enrollmentkey;
// to be used for host registration
type EnrollmentToken struct {
Server string `json:"server"`
Value string `json:"value"`
}
// EnrollmentKeyLength - the length of an enrollment key - 62^16 unique possibilities
const EnrollmentKeyLength = 32
// EnrollmentKey - the key used to register hosts and join them to specific networks
type EnrollmentKey struct {
Expiration time.Time `json:"expiration"`
UsesRemaining int `json:"uses_remaining"`
Value string `json:"value"`
Networks []string `json:"networks"`
Unlimited bool `json:"unlimited"`
Tags []string `json:"tags"`
Token string `json:"token,omitempty"` // B64 value of EnrollmentToken
Type KeyType `json:"type"`
Relay uuid.UUID `json:"relay"`
Groups []TagID `json:"groups"`
Default bool `json:"default"`
AutoEgress bool `json:"auto_egress"`
AutoAssignGateway bool `json:"auto_assign_gw"`
}
// APIEnrollmentKey - used to create enrollment keys via API
type APIEnrollmentKey struct {
Expiration int64 `json:"expiration" swaggertype:"primitive,integer" format:"int64"`
UsesRemaining int `json:"uses_remaining"`
Networks []string `json:"networks"`
Unlimited bool `json:"unlimited"`
Tags []string `json:"tags" validate:"required,dive,min=3,max=32"`
Type KeyType `json:"type"`
Relay string `json:"relay"`
Groups []TagID `json:"groups"`
AutoEgress bool `json:"auto_egress"`
AutoAssignGateway bool `json:"auto_assign_gw"`
}
// RegisterResponse - the response to a successful enrollment register
type RegisterResponse struct {
ServerConf ServerConfig `json:"server_config"`
RequestedHost schema.Host `json:"requested_host"`
}
// EnrollmentKey.IsValid - checks if the key is still valid to use
func (k *EnrollmentKey) IsValid() bool {
if k == nil {
return false
}
if k.UsesRemaining > 0 {
return true
}
if !k.Expiration.IsZero() && time.Now().Before(k.Expiration) {
return true
}
if k.Type == Undefined {
return false
}
return k.Unlimited
}
// EnrollmentKey.Validate - validate's an EnrollmentKey
// should be used during creation
func (k *EnrollmentKey) Validate() error {
if k == nil {
return ErrNilEnrollmentKey
}
if k.Tags == nil {
return ErrNilTagsEnrollmentKey
}
if len(k.Value) != EnrollmentKeyLength {
return fmt.Errorf("%w: length not %d characters", ErrInvalidEnrollmentKeyValue, EnrollmentKeyLength)
}
if !k.IsValid() {
return fmt.Errorf("%w: uses remaining: %d, expiration: %s, unlimited: %t", ErrInvalidEnrollmentKey, k.UsesRemaining, k.Expiration, k.Unlimited)
}
return nil
}