Skip to main content

Overview

DefDrive implements a sophisticated access control system that allows file owners to create shareable links with fine-grained restrictions including IP whitelisting, subnet restrictions, time-to-live (TTL), expiration dates, and one-time use.

IP Restrictions

Whitelist specific IPs or subnets

Time-Based

Set expiration dates for links

TTL (Hops)

Limit number of access attempts

One-Time Use

Links that expire after first use

Access Model

The Access model defines the structure for shareable file links with various restrictions.

Model Definition

// From models/access.go:7-22
type Access struct {
    gorm.Model
    Name       string
    Link       string   `gorm:"uniqueIndex"` // Unique index to ensure the link is unique
    Subnets    []string `gorm:"type:text[]"` // Array of subnets
    IPs        []string `gorm:"type:text[]"` // Array of IPs
    Expires    string
    Public     bool `gorm:"default:false"` // Flag indicating if access is public or restricted
    OneTimeUse bool `gorm:"default:false"` // Flag indicating if the link is one-time use
    Used       bool `gorm:"default:false"` // Flag indicating if the link has been used
    TTL        int  `gorm:"default:0"`     // Time to live (number of hops)
    EnableTTL  bool `gorm:"default:false"` // Flag to enable or disable TTL

    FileID uint `gorm:"index"`
    File   File `gorm:"foreignKey:FileID;references:ID"`
}
Each Access record is linked to a specific File via the FileID foreign key. Multiple access links can be created for the same file with different restrictions.

Key Fields

FieldTypeDescription
LinkstringUnique identifier for the access link (uniqueIndex)
IPs[]stringArray of allowed IP addresses
Subnets[]stringArray of allowed CIDR subnets
ExpiresstringRFC3339 timestamp for expiration
PublicboolWhether the access link is public
OneTimeUseboolIf true, link becomes invalid after first use
UsedboolTracks if a one-time link has been used
TTLintNumber of remaining access attempts (hops)
EnableTTLboolWhether TTL enforcement is active

Public vs Private Access

Both the File and Access must be public for unrestricted access.

Public Access Logic

// From middleware/access_restrictions.go:36-41
// Return an error if neither the file nor the access is public
if !(file.Public && access.Public) {
    c.JSON(http.StatusForbidden, gin.H{"error": "Access denied: file or access is not public"})
    c.Abort()
    return
}
Both conditions must be true: The File must have Public = true AND the Access must have Public = true for unrestricted access. This dual-check ensures explicit permission at both levels.

Public File + Public Access

Fully accessible (subject to other restrictions)

Private File OR Private Access

Access denied regardless of other settings

IP and Subnet Restrictions

Restrict file access to specific IP addresses or CIDR subnets.

IP Whitelist

Only clients with whitelisted IP addresses can access the file:
// From middleware/access_restrictions.go:98-111
func checkIPRestriction(access models.Access, c *gin.Context) bool {
    if len(access.IPs) > 0 {
        ip := c.ClientIP()
        for _, allowedIP := range access.IPs {
            if ip == allowedIP {
                return true
            }
        }
        c.JSON(http.StatusForbidden, gin.H{"error": "Access restricted to specific IPs"})
        c.Abort()
        return false
    }
    return true
}

Subnet Whitelist

Allow access from entire subnets using CIDR notation:
// From middleware/access_restrictions.go:82-96
func checkSubnetRestriction(access models.Access, c *gin.Context) bool {
    if len(access.Subnets) > 0 {
        ip := c.ClientIP()
        for _, subnet := range access.Subnets {
            _, parsedSubnet, err := net.ParseCIDR(subnet)
            if err == nil && parsedSubnet.Contains(net.ParseIP(ip)) {
                return true
            }
        }
        c.JSON(http.StatusForbidden, gin.H{"error": "Access restricted to specific subnets"})
        c.Abort()
        return false
    }
    return true
}
CIDR Examples:
  • 192.168.1.0/24 - Allows all IPs from 192.168.1.0 to 192.168.1.255
  • 10.0.0.0/8 - Allows all IPs from 10.0.0.0 to 10.255.255.255
  • 172.16.0.0/12 - Allows all IPs from 172.16.0.0 to 172.31.255.255

Time-Based Restrictions

Expiration Time

Set an absolute expiration date using RFC3339 format:
// From middleware/access_restrictions.go:61-71
func checkExpiration(access models.Access, c *gin.Context) bool {
    if access.Expires != "" {
        expiryTime, err := time.Parse(time.RFC3339, access.Expires)
        if err != nil || time.Now().After(expiryTime) {
            c.JSON(http.StatusForbidden, gin.H{"error": "Access link has expired"})
            c.Abort()
            return false
        }
    }
    return true
}
Example RFC3339 timestamp: 2026-12-31T23:59:59Z
If the Expires field is empty, the link never expires based on time. However, other restrictions (TTL, one-time use) may still apply.

TTL (Time-To-Live)

TTL limits the number of times a link can be accessed, regardless of who accesses it.

TTL Enforcement

// From middleware/access_restrictions.go:113-124
func checkTTL(access models.Access, db *gorm.DB, c *gin.Context) bool {
    if access.EnableTTL && access.TTL > 0 {
        access.TTL--
        if access.TTL == 0 {
            c.JSON(http.StatusForbidden, gin.H{"error": "Access link has reached its TTL limit"})
            c.Abort()
            return false
        }
        db.Save(&access)
    }
    return true
}
TTL Behavior:
  • TTL is decremented before access is granted
  • When TTL reaches 0, the link becomes permanently invalid
  • The final access (when TTL becomes 0) is denied
  • EnableTTL must be true for TTL to be enforced

TTL Example

Initial state: TTL = 3, EnableTTL = true

Access 1: TTL decrements to 2 → Access GRANTED
Access 2: TTL decrements to 1 → Access GRANTED
Access 3: TTL decrements to 0 → Access DENIED
Access 4+: TTL = 0 → Access DENIED
One-time use links become invalid immediately after the first successful access.

One-Time Use Check

// From middleware/access_restrictions.go:73-80
func checkOneTimeUse(access models.Access, c *gin.Context) bool {
    if access.OneTimeUse && access.Used {
        c.JSON(http.StatusForbidden, gin.H{"error": "Access link has already been used"})
        c.Abort()
        return false
    }
    return true
}

Marking as Used

After all checks pass, one-time links are marked as used:
// From middleware/access_restrictions.go:52-55
if access.OneTimeUse {
    access.Used = true
    db.Save(&access)
}
Use Case: One-time links are ideal for:
  • Sending sensitive documents that should only be viewed once
  • Temporary file sharing with time-critical recipients
  • Preventing link forwarding and sharing

Access Restrictions Flow

The middleware validates access in a specific order:
// From middleware/access_restrictions.go:43-50
if !checkSubnetRestriction(access, c) ||
    !checkIPRestriction(access, c) ||
    !checkOneTimeUse(access, c) ||
    !checkTTL(access, db, c) ||
    !checkExpiration(access, c) {
    return
}

Validation Order

  1. Link Lookup - Verify the access link exists
  2. File Lookup - Verify the associated file exists
  3. Public Check - Both file and access must be public
  4. Subnet Restriction - Check if client IP is in allowed subnets
  5. IP Restriction - Check if client IP is in allowed IPs
  6. One-Time Use - Check if link has already been used
  7. TTL - Decrement and check remaining hops
  8. Expiration - Check if link has expired
  9. Mark as Used - If one-time use, mark the link as used
The checks use short-circuit evaluation with OR (||). If any check fails, the remaining checks are skipped and access is denied.

Restriction Combinations

Restrictions can be combined for enhanced security:

Corporate Sharing

IP Restriction + Expiration
Share with office network (subnet) for limited time

Secret Document

One-Time Use + IP Restriction + Expiration
Maximum security for sensitive files

Limited Distribution

TTL + Expiration
Allow specific number of downloads within timeframe

Public with Limits

Public + TTL
Publicly accessible but limited to N downloads
Access links reference both the controller and file:
// From models/access.go:10
Link string `gorm:"uniqueIndex"` // Unique index to ensure the link is unique
The middleware extracts the link from URL parameters:
// From middleware/access_restrictions.go:16-19
link := c.Param("link")
if link == "" {
    link = c.Param("hash")
}
Links are enforced with a unique index at the database level to prevent collisions.

Authentication

Learn about JWT authentication

File Management

Understand file ownership and storage

User Limits

Explore storage and file quotas

Build docs developers (and LLMs) love