mirror of
https://github.com/h44z/wg-portal.git
synced 2025-04-19 08:55:12 +00:00
Public REST API implementation to handle peers, interfaces and users. It also includes some simple provisioning endpoints. The Swagger API documentation is available under /api/v1/doc.html
175 lines
4.8 KiB
Go
175 lines
4.8 KiB
Go
package backend
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/h44z/wg-portal/internal/app/api/v1/models"
|
|
"github.com/h44z/wg-portal/internal/config"
|
|
"github.com/h44z/wg-portal/internal/domain"
|
|
)
|
|
|
|
type ProvisioningServiceUserManagerRepo interface {
|
|
GetUser(ctx context.Context, id domain.UserIdentifier) (*domain.User, error)
|
|
GetUserByEmail(ctx context.Context, email string) (*domain.User, error)
|
|
}
|
|
|
|
type ProvisioningServicePeerManagerRepo interface {
|
|
GetPeer(ctx context.Context, id domain.PeerIdentifier) (*domain.Peer, error)
|
|
GetUserPeers(context.Context, domain.UserIdentifier) ([]domain.Peer, error)
|
|
PreparePeer(ctx context.Context, id domain.InterfaceIdentifier) (*domain.Peer, error)
|
|
CreatePeer(ctx context.Context, p *domain.Peer) (*domain.Peer, error)
|
|
}
|
|
|
|
type ProvisioningServiceConfigFileManagerRepo interface {
|
|
GetPeerConfig(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error)
|
|
GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error)
|
|
}
|
|
|
|
type ProvisioningService struct {
|
|
cfg *config.Config
|
|
|
|
users ProvisioningServiceUserManagerRepo
|
|
peers ProvisioningServicePeerManagerRepo
|
|
configFiles ProvisioningServiceConfigFileManagerRepo
|
|
}
|
|
|
|
func NewProvisioningService(
|
|
cfg *config.Config,
|
|
users ProvisioningServiceUserManagerRepo,
|
|
peers ProvisioningServicePeerManagerRepo,
|
|
configFiles ProvisioningServiceConfigFileManagerRepo,
|
|
) *ProvisioningService {
|
|
return &ProvisioningService{
|
|
cfg: cfg,
|
|
|
|
users: users,
|
|
peers: peers,
|
|
configFiles: configFiles,
|
|
}
|
|
}
|
|
|
|
func (p ProvisioningService) GetUserAndPeers(
|
|
ctx context.Context,
|
|
userId domain.UserIdentifier,
|
|
email string,
|
|
) (*domain.User, []domain.Peer, error) {
|
|
// first fetch user
|
|
var user *domain.User
|
|
switch {
|
|
case userId != "":
|
|
u, err := p.users.GetUser(ctx, userId)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
user = u
|
|
case email != "":
|
|
u, err := p.users.GetUserByEmail(ctx, email)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
user = u
|
|
default:
|
|
return nil, nil, fmt.Errorf("either UserId or Email must be set: %w", domain.ErrInvalidData)
|
|
}
|
|
|
|
if err := domain.ValidateUserAccessRights(ctx, user.Identifier); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
peers, err := p.peers.GetUserPeers(ctx, user.Identifier)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return user, peers, nil
|
|
}
|
|
|
|
func (p ProvisioningService) GetPeerConfig(ctx context.Context, peerId domain.PeerIdentifier) ([]byte, error) {
|
|
peer, err := p.peers.GetPeer(ctx, peerId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := domain.ValidateUserAccessRights(ctx, peer.UserIdentifier); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
peerCfgReader, err := p.configFiles.GetPeerConfig(ctx, peer.Identifier)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
peerCfgData, err := io.ReadAll(peerCfgReader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return peerCfgData, nil
|
|
}
|
|
|
|
func (p ProvisioningService) GetPeerQrPng(ctx context.Context, peerId domain.PeerIdentifier) ([]byte, error) {
|
|
peer, err := p.peers.GetPeer(ctx, peerId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := domain.ValidateUserAccessRights(ctx, peer.UserIdentifier); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
peerCfgQrReader, err := p.configFiles.GetPeerConfigQrCode(ctx, peer.Identifier)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
peerCfgQrData, err := io.ReadAll(peerCfgQrReader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return peerCfgQrData, nil
|
|
}
|
|
|
|
func (p ProvisioningService) NewPeer(ctx context.Context, req models.ProvisioningRequest) (*domain.Peer, error) {
|
|
if req.UserIdentifier == "" {
|
|
req.UserIdentifier = string(domain.GetUserInfo(ctx).Id) // use authenticated user id if not set
|
|
}
|
|
|
|
// check permissions
|
|
if err := domain.ValidateUserAccessRights(ctx, domain.UserIdentifier(req.UserIdentifier)); err != nil {
|
|
return nil, err
|
|
}
|
|
if !p.cfg.Core.SelfProvisioningAllowed {
|
|
// only admins can create new peers if self-provisioning is disabled
|
|
if err := domain.ValidateAdminAccessRights(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// prepare new peer
|
|
peer, err := p.peers.PreparePeer(ctx, domain.InterfaceIdentifier(req.InterfaceIdentifier))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to prepare new peer: %w", err)
|
|
}
|
|
peer.UserIdentifier = domain.UserIdentifier(req.UserIdentifier) // overwrite context user id with the one from the request
|
|
if req.PublicKey != "" {
|
|
peer.Identifier = domain.PeerIdentifier(req.PublicKey)
|
|
peer.Interface.PublicKey = req.PublicKey
|
|
peer.Interface.PrivateKey = "" // clear private key if public key is set, WireGuard Portal does not know the private key in that case
|
|
}
|
|
if req.PresharedKey != "" {
|
|
peer.PresharedKey = domain.PreSharedKey(req.PresharedKey)
|
|
}
|
|
peer.GenerateDisplayName("API")
|
|
|
|
// save new peer
|
|
peer, err = p.peers.CreatePeer(ctx, peer)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create new peer: %w", err)
|
|
}
|
|
|
|
return peer, nil
|
|
}
|