Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
A
accountserver
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
silver-platter
accountserver
Commits
e98a25d1
Commit
e98a25d1
authored
5 years ago
by
ale
Browse files
Options
Downloads
Patches
Plain Diff
Shelve off temporary changes to a separate branch
parent
b0fd19b7
No related branches found
No related tags found
No related merge requests found
Changes
5
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
backend/ldap/model.go
+17
-0
17 additions, 0 deletions
backend/ldap/model.go
service.go
+1
-0
1 addition, 0 deletions
service.go
template.go
+225
-0
225 additions, 0 deletions
template.go
validation_helpers.go
+126
-0
126 additions, 0 deletions
validation_helpers.go
validators.go
+428
-487
428 additions, 487 deletions
validators.go
with
797 additions
and
487 deletions
backend/ldap/model.go
+
17
−
0
View file @
e98a25d1
...
@@ -3,6 +3,7 @@ package ldapbackend
...
@@ -3,6 +3,7 @@ package ldapbackend
import
(
import
(
"bytes"
"bytes"
"context"
"context"
"errors"
"fmt"
"fmt"
"math/rand"
"math/rand"
"strconv"
"strconv"
...
@@ -525,6 +526,22 @@ func (tx *backendTX) searchResourcesByType(ctx context.Context, pattern, resourc
...
@@ -525,6 +526,22 @@ func (tx *backendTX) searchResourcesByType(ctx context.Context, pattern, resourc
return
out
,
nil
return
out
,
nil
}
}
// FindResource fetches a specific resource by type and name.
func
(
tx
*
backendTX
)
FindResource
(
ctx
context
.
Context
,
spec
as
.
FindResourceRequest
)
(
*
as
.
RawResource
,
error
)
{
result
,
err
:=
tx
.
searchResourcesByType
(
ctx
,
spec
.
Name
,
spec
.
Type
)
if
err
!=
nil
{
return
nil
,
err
}
switch
len
(
result
)
{
case
0
:
return
nil
,
nil
case
1
:
return
result
[
0
],
nil
default
:
return
nil
,
errors
.
New
(
"too many results"
)
}
}
// SearchResource returns all the resources matching the pattern.
// SearchResource returns all the resources matching the pattern.
func
(
tx
*
backendTX
)
SearchResource
(
ctx
context
.
Context
,
pattern
string
)
([]
*
as
.
RawResource
,
error
)
{
func
(
tx
*
backendTX
)
SearchResource
(
ctx
context
.
Context
,
pattern
string
)
([]
*
as
.
RawResource
,
error
)
{
// Aggregate results for all known resource types.
// Aggregate results for all known resource types.
...
...
This diff is collapsed.
Click to expand it.
service.go
+
1
−
0
View file @
e98a25d1
...
@@ -51,6 +51,7 @@ type TX interface {
...
@@ -51,6 +51,7 @@ type TX interface {
UpdateResource
(
context
.
Context
,
*
Resource
)
error
UpdateResource
(
context
.
Context
,
*
Resource
)
error
CreateResources
(
context
.
Context
,
*
User
,
[]
*
Resource
)
([]
*
Resource
,
error
)
CreateResources
(
context
.
Context
,
*
User
,
[]
*
Resource
)
([]
*
Resource
,
error
)
SetResourcePassword
(
context
.
Context
,
*
Resource
,
string
)
error
SetResourcePassword
(
context
.
Context
,
*
Resource
,
string
)
error
FindResource
(
context
.
Context
,
FindResourceRequest
)
(
*
RawResource
,
error
)
HasAnyResource
(
context
.
Context
,
[]
FindResourceRequest
)
(
bool
,
error
)
HasAnyResource
(
context
.
Context
,
[]
FindResourceRequest
)
(
bool
,
error
)
GetUser
(
context
.
Context
,
string
)
(
*
RawUser
,
error
)
GetUser
(
context
.
Context
,
string
)
(
*
RawUser
,
error
)
...
...
This diff is collapsed.
Click to expand it.
template.go
0 → 100644
+
225
−
0
View file @
e98a25d1
package
accountserver
import
(
"context"
"errors"
"fmt"
"math/rand"
"path/filepath"
"strings"
"time"
)
// Templating, in this context, means filling up derived attributes in a resource.
//
// Derived attributes are always enforced (i.e. we don't check if the
// attribute already has a value), but currently this is only done at
// resource creation time. The plan is to extend this to cover any
// Update call as well.
type
templateContext
struct
{
shards
shardBackend
webroot
string
}
func
(
c
*
templateContext
)
pickShard
(
ctx
context
.
Context
,
r
*
Resource
)
(
string
,
error
)
{
avail
:=
c
.
shards
.
GetAvailableShards
(
ctx
,
r
.
Type
)
if
len
(
avail
)
==
0
{
return
""
,
fmt
.
Errorf
(
"no available shards for resource type %s"
,
r
.
Type
)
}
return
avail
[
rand
.
Intn
(
len
(
avail
))],
nil
}
func
(
c
*
templateContext
)
setResourceShard
(
ctx
context
.
Context
,
r
*
Resource
,
ref
*
Resource
)
error
{
if
r
.
Shard
==
""
{
if
ref
!=
nil
{
r
.
Shard
=
ref
.
Shard
}
else
{
s
,
err
:=
c
.
pickShard
(
ctx
,
r
)
if
err
!=
nil
{
return
err
}
r
.
Shard
=
s
}
}
if
r
.
OriginalShard
==
""
{
r
.
OriginalShard
=
r
.
Shard
}
return
nil
}
func
(
c
*
templateContext
)
setResourceStatus
(
r
*
Resource
)
{
if
r
.
Status
==
""
{
r
.
Status
=
ResourceStatusActive
}
}
func
(
c
*
templateContext
)
setCommonResourceAttrs
(
ctx
context
.
Context
,
r
*
Resource
,
ref
*
Resource
,
user
*
User
)
error
{
// If we reference another resource, ensure it has been templated.
if
ref
!=
nil
{
if
err
:=
c
.
applyTemplate
(
ctx
,
ref
,
user
);
err
!=
nil
{
return
err
}
}
r
.
CreatedAt
=
time
.
Now
()
.
UTC
()
.
Format
(
"2006-01-02"
)
c
.
setResourceStatus
(
r
)
return
c
.
setResourceShard
(
ctx
,
r
,
ref
)
}
// Apply default values to an Email resource.
func
(
c
*
templateContext
)
emailResourceTemplate
(
ctx
context
.
Context
,
r
*
Resource
,
_
*
User
)
error
{
// Force the email address to lowercase.
r
.
Name
=
strings
.
ToLower
(
r
.
Name
)
if
r
.
Email
==
nil
{
r
.
Email
=
new
(
Email
)
}
addrParts
:=
strings
.
Split
(
r
.
Name
,
"@"
)
if
len
(
addrParts
)
!=
2
{
return
errors
.
New
(
"malformed name"
)
}
r
.
Email
.
Maildir
=
fmt
.
Sprintf
(
"%s/%s"
,
addrParts
[
1
],
addrParts
[
0
])
r
.
Email
.
QuotaLimit
=
4096
return
c
.
setCommonResourceAttrs
(
ctx
,
r
,
nil
,
nil
)
}
// Apply default values to a Website or Domain resource.
func
(
c
*
templateContext
)
websiteResourceTemplate
(
ctx
context
.
Context
,
r
*
Resource
,
user
*
User
)
error
{
if
user
==
nil
{
return
errors
.
New
(
"website resource needs owner"
)
}
// Force the website address to lowercase.
r
.
Name
=
strings
.
ToLower
(
r
.
Name
)
if
r
.
Website
==
nil
{
r
.
Website
=
new
(
Website
)
}
// If the client did not specify a DocumentRoot, find a DAV resource
// and associate the website with it.
if
r
.
Website
.
DocumentRoot
==
""
{
dav
:=
user
.
GetSingleResourceByType
(
ResourceTypeDAV
)
if
dav
==
nil
{
return
errors
.
New
(
"user has no DAV accounts"
)
}
// The DAV resource may not have been templatized yet.
if
dav
.
DAV
==
nil
||
dav
.
DAV
.
Homedir
==
""
{
if
err
:=
c
.
davResourceTemplate
(
ctx
,
dav
,
user
);
err
!=
nil
{
return
err
}
}
r
.
Website
.
DocumentRoot
=
filepath
.
Join
(
dav
.
DAV
.
Homedir
,
"html-"
+
r
.
Name
)
}
r
.
Website
.
DocumentRoot
=
filepath
.
Clean
(
r
.
Website
.
DocumentRoot
)
if
len
(
r
.
Website
.
Options
)
==
0
{
r
.
Website
.
Options
=
[]
string
{
"nomail"
}
}
r
.
Website
.
UID
=
user
.
UID
dav
:=
findMatchingDAVAccount
(
user
,
r
)
if
dav
==
nil
{
return
fmt
.
Errorf
(
"no DAV resources matching website %s"
,
r
.
String
())
}
return
c
.
setCommonResourceAttrs
(
ctx
,
r
,
dav
,
user
)
}
// Apply default values to a DAV resource.
func
(
c
*
templateContext
)
davResourceTemplate
(
ctx
context
.
Context
,
r
*
Resource
,
user
*
User
)
error
{
if
user
==
nil
{
return
errors
.
New
(
"dav resource needs owner"
)
}
// Force the account name to lowercase.
r
.
Name
=
strings
.
ToLower
(
r
.
Name
)
if
r
.
DAV
==
nil
{
r
.
DAV
=
new
(
WebDAV
)
}
if
r
.
DAV
.
Homedir
==
""
{
r
.
DAV
.
Homedir
=
filepath
.
Join
(
c
.
webroot
,
r
.
Name
)
}
r
.
DAV
.
Homedir
=
filepath
.
Clean
(
r
.
DAV
.
Homedir
)
r
.
DAV
.
UID
=
user
.
UID
return
c
.
setCommonResourceAttrs
(
ctx
,
r
,
nil
,
user
)
}
// Apply default values to a Database resource.
func
(
c
*
templateContext
)
databaseResourceTemplate
(
ctx
context
.
Context
,
r
*
Resource
,
user
*
User
)
error
{
if
user
==
nil
{
return
errors
.
New
(
"database resource needs owner"
)
}
// Force the database name to lowercase.
r
.
Name
=
strings
.
ToLower
(
r
.
Name
)
if
r
.
Database
==
nil
{
r
.
Database
=
new
(
Database
)
}
if
r
.
Database
.
DBUser
==
""
{
r
.
Database
.
DBUser
=
r
.
Name
}
return
c
.
setCommonResourceAttrs
(
ctx
,
r
,
user
.
GetResourceByID
(
r
.
ParentID
),
user
)
}
// Apply default values to a MailingList resource.
func
(
c
*
templateContext
)
listResourceTemplate
(
ctx
context
.
Context
,
r
*
Resource
,
user
*
User
)
error
{
// Force the list address to lowercase.
r
.
Name
=
strings
.
ToLower
(
r
.
Name
)
if
r
.
List
==
nil
{
r
.
List
=
new
(
MailingList
)
}
// As a convenience, if a user is passed in the context, we add it to
// the list admins.
if
user
!=
nil
&&
len
(
r
.
List
.
Admins
)
==
0
{
r
.
List
.
Admins
=
[]
string
{
user
.
Name
}
}
return
c
.
setCommonResourceAttrs
(
ctx
,
r
,
nil
,
nil
)
}
// Apply default values to a Newsletter resource.
func
(
c
*
templateContext
)
newsletterResourceTemplate
(
ctx
context
.
Context
,
r
*
Resource
,
user
*
User
)
error
{
// Force the list address to lowercase.
r
.
Name
=
strings
.
ToLower
(
r
.
Name
)
if
r
.
Newsletter
==
nil
{
r
.
Newsletter
=
new
(
Newsletter
)
}
// As a convenience, if a user is passed in the context, we add it to
// the list admins.
if
user
!=
nil
&&
len
(
r
.
Newsletter
.
Admins
)
==
0
{
r
.
Newsletter
.
Admins
=
[]
string
{
user
.
Name
}
}
return
c
.
setCommonResourceAttrs
(
ctx
,
r
,
nil
,
nil
)
}
// Apply default values to a resource.
func
(
c
*
templateContext
)
applyTemplate
(
ctx
context
.
Context
,
r
*
Resource
,
user
*
User
)
error
{
switch
r
.
Type
{
case
ResourceTypeEmail
:
return
c
.
emailResourceTemplate
(
ctx
,
r
,
user
)
case
ResourceTypeWebsite
,
ResourceTypeDomain
:
return
c
.
websiteResourceTemplate
(
ctx
,
r
,
user
)
case
ResourceTypeDAV
:
return
c
.
davResourceTemplate
(
ctx
,
r
,
user
)
case
ResourceTypeDatabase
:
return
c
.
databaseResourceTemplate
(
ctx
,
r
,
user
)
case
ResourceTypeMailingList
:
return
c
.
listResourceTemplate
(
ctx
,
r
,
user
)
case
ResourceTypeNewsletter
:
return
c
.
newsletterResourceTemplate
(
ctx
,
r
,
user
)
}
return
nil
}
This diff is collapsed.
Click to expand it.
validation_helpers.go
0 → 100644
+
126
−
0
View file @
e98a25d1
package
accountserver
import
(
"bufio"
"context"
"os"
"strings"
)
// Read non-empty lines from a file, ignoring comments and
// leading/trailing whitespace.
func
loadStringListFromFile
(
path
string
)
([]
string
,
error
)
{
f
,
err
:=
os
.
Open
(
path
)
// #nosec
if
err
!=
nil
{
return
nil
,
err
}
defer
f
.
Close
()
// nolint: errcheck
var
list
[]
string
scanner
:=
bufio
.
NewScanner
(
f
)
for
scanner
.
Scan
()
{
line
:=
scanner
.
Text
()
if
n
:=
strings
.
Index
(
line
,
"#"
);
n
>=
0
{
line
=
line
[
:
n
]
}
line
=
strings
.
TrimSpace
(
line
)
if
line
!=
""
{
list
=
append
(
list
,
line
)
}
}
return
list
,
scanner
.
Err
()
}
// A stringSet is just a list of strings with a quick membership test.
type
stringSet
struct
{
set
map
[
string
]
struct
{}
list
[]
string
}
func
newStringSetFromList
(
list
[]
string
)
*
stringSet
{
set
:=
make
(
map
[
string
]
struct
{})
for
_
,
s
:=
range
list
{
set
[
s
]
=
struct
{}{}
}
return
&
stringSet
{
set
:
set
,
list
:
list
}
}
func
newStringSetFromFileAndList
(
list
[]
string
,
path
string
)
(
*
stringSet
,
error
)
{
if
path
!=
""
{
more
,
err
:=
loadStringListFromFile
(
path
)
if
err
!=
nil
{
return
nil
,
err
}
list
=
append
(
list
,
more
...
)
}
return
newStringSetFromList
(
list
),
nil
}
func
(
s
stringSet
)
Contains
(
needle
string
)
bool
{
_
,
ok
:=
s
.
set
[
needle
]
return
ok
}
func
(
s
stringSet
)
Check
(
_
*
RequestContext
,
value
string
)
bool
{
_
,
ok
:=
s
.
set
[
value
]
return
ok
}
func
(
s
stringSet
)
List
(
_
context
.
Context
)
[]
string
{
return
s
.
list
}
// List of email domains backed by our database (with ACLs).
type
emailDomainsDataset
struct
{
backend
Backend
}
func
(
s
*
emailDomainsDataset
)
Check
(
rctx
*
RequestContext
,
value
string
)
bool
{
tx
,
err
:=
s
.
backend
.
NewTransaction
()
if
err
!=
nil
{
// Fail close on database errors.
return
false
}
// Ignore the error in FindResource() since we're going to fail close anyway.
// nolint: errcheck
rsrc
,
_
:=
tx
.
FindResource
(
rctx
.
Context
,
FindResourceRequest
{
Type
:
ResourceTypeDomain
,
Name
:
value
})
if
rsrc
==
nil
{
return
false
}
// Only allow domains that have acceptMail=true IF the
// requesting user is an admin.
//
// TODO: relax the ACL check allowing domain owners to create
// accounts?
if
rsrc
.
Website
.
AcceptMail
&&
rctx
.
Auth
.
IsAdmin
{
return
true
}
return
false
}
func
(
s
*
emailDomainsDataset
)
List
(
_
context
.
Context
)
[]
string
{
return
nil
}
// Combine multiple datasets.
type
multiDataset
struct
{
datasets
[]
validationDataset
}
func
(
m
*
multiDataset
)
Check
(
rctx
*
RequestContext
,
value
string
)
bool
{
for
_
,
d
:=
range
m
.
datasets
{
if
d
.
Check
(
rctx
,
value
)
{
return
true
}
}
return
false
}
func
(
m
*
multiDataset
)
List
(
ctx
context
.
Context
)
[]
string
{
var
out
[]
string
for
_
,
d
:=
range
m
.
datasets
{
out
=
append
(
out
,
d
.
List
(
ctx
)
...
)
}
return
out
}
This diff is collapsed.
Click to expand it.
validators.go
+
428
−
487
View file @
e98a25d1
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment