mirror of
https://git.openldap.org/openldap/openldap.git
synced 2025-01-06 10:46:21 +08:00
Access control section now includes all relevant FAQ items and is completed as far as content. Just need to verify http://www.openldap.org/faq/data/cache/1005.html is up to date for 2.4 etc. and the rest of the section.
This commit is contained in:
parent
c55a06254f
commit
20e24e1517
@ -4,10 +4,30 @@
|
||||
|
||||
H1: Access Control
|
||||
|
||||
H2: Introduction
|
||||
|
||||
As the directory gets populated with more and more data of varying sensitivity,
|
||||
controlling the kinds of access granted to the directory becomes more and more
|
||||
critical. For instance, the directory may contain data of a confidential nature
|
||||
that you may need to protect by contract or by law. Or, if using the directory
|
||||
to control access to other services, inappropriate access to the directory may
|
||||
create avenues of attack to your sites security that result in devastating
|
||||
damage to your assets.
|
||||
|
||||
Access to your directory can be configured via two methods, the first using
|
||||
{{SECT:The slapd Configuration File}} and the second using the {{slapd-config}}(5)
|
||||
format ({{SECT:Configuring slapd}}).
|
||||
|
||||
The default access control policy is allow read by all clients. Regardless of
|
||||
what access control policy is defined, the {{rootdn}} is always allowed full
|
||||
rights (i.e. auth, search, compare, read and write) on everything and anything.
|
||||
|
||||
As a consequence, it's useless (and results in a performance penalty) to explicitly
|
||||
list the {{rootdn}} among the {{<by>}} clauses.
|
||||
|
||||
The following sections will describe Access Control Lists in more details and
|
||||
follow with some examples and recommendations.
|
||||
|
||||
H2: Access Control via Static Configuration
|
||||
|
||||
Access to entries and attributes is controlled by the
|
||||
@ -948,11 +968,394 @@ E: 51. olcDbIndex: objectClass eq
|
||||
E: 52. olcAccess: to * by users read
|
||||
|
||||
|
||||
H3: Converting from slapd.conf(8) to a {{B:cn=config}} directory format
|
||||
H3: Converting from {{slapd.conf}}(5) to a {{B:cn=config}} directory format
|
||||
|
||||
Discuss slap* -f slapd.conf -F slapd.d/ (man slapd-config)
|
||||
|
||||
|
||||
H2: Access Control Common Examples
|
||||
|
||||
H3: Basic ACLs
|
||||
|
||||
Generally one should start with some basic ACLs such as:
|
||||
|
||||
> access to attr=userPassword
|
||||
> by self =xw
|
||||
> by anonymous auth
|
||||
> by * none
|
||||
>
|
||||
>
|
||||
> access to *
|
||||
> by self write
|
||||
> by users read
|
||||
> by * none
|
||||
|
||||
The first ACL allows users to update (but not read) their passwords, anonymous
|
||||
users to authenticate against this attribute, and (implicitly) denying all
|
||||
access to others.
|
||||
|
||||
The second ACL allows users full access to their entry, authenticated users read
|
||||
access to anything, and (implicitly) denying all access to others (in this case,
|
||||
anonymous users).
|
||||
|
||||
|
||||
H3: Matching Anonymous and Authenticated users
|
||||
|
||||
An anonymous user has a empty DN. While the {{dn.exact=""}} or {{dn.regex="^$"}}
|
||||
could be used, {{slapd}}(8)) offers an anonymous shorthand which should be
|
||||
used instead.
|
||||
|
||||
> access to *
|
||||
> by anonymous none
|
||||
> by * read
|
||||
|
||||
denies all access to anonymous users while granting others read.
|
||||
|
||||
Authenticated users have a subject DN. While {{dn.regex=".+"}} will match any
|
||||
authenticated user, OpenLDAP provides the users short hand which should be used
|
||||
instead.
|
||||
|
||||
> access to *
|
||||
> by users read
|
||||
> by * none
|
||||
|
||||
This ACL grants read permissions to authenticated users while denying others
|
||||
(i.e.: anonymous users).
|
||||
|
||||
|
||||
H3: Controlling rootdn access
|
||||
|
||||
You could specify the {{rootdn}} in {{slapd.conf}}(5) or {[slapd.d}} without
|
||||
specifying a {{rootpw}}. Then you have to add an actual directory entry with
|
||||
the same dn, e.g.:
|
||||
|
||||
> dn: cn=Manager,o=MyOrganization
|
||||
> cn: Manager
|
||||
> sn: Manager
|
||||
> objectClass: person
|
||||
> objectClass: top
|
||||
> userPassword: {SSHA}someSSHAdata
|
||||
|
||||
Then binding as the {{rootdn}} will require a regular bind to that DN, which
|
||||
in turn requires auth access to that entry's DN and {{userPassword}}, and this
|
||||
can be restricted via ACLs. E.g.:
|
||||
|
||||
> access to dn.base="cn=Manager,o=MyOrganization"
|
||||
> by peername.regex=127\.0\.0\.1 auth
|
||||
> by peername.regex=192\.168\.0\..* auth
|
||||
> by users none
|
||||
> by * none
|
||||
|
||||
The ACLs above will only allow binding using rootdn from localhost and
|
||||
192.168.0.0/24.
|
||||
|
||||
|
||||
H3: Managing access with Groups
|
||||
|
||||
There are a few ways to do this. One approach is illustrated here. Consider the
|
||||
following DIT layout:
|
||||
|
||||
> +-dc=example,dc=com
|
||||
> +---cn=administrators,dc=example,dc=com
|
||||
> +---cn=fred blogs,dc=example,dc=com
|
||||
|
||||
and the following group object (in LDIF format):
|
||||
|
||||
> dn: cn=administrators,dc=example,dc=com
|
||||
> cn: administrators of this region
|
||||
> objectclass: groupOfNames (important for the group acl feature)
|
||||
> member: cn=fred blogs,dc=example,dc=com
|
||||
> member: cn=somebody else,dc=example,dc=com
|
||||
|
||||
One can then grant access to the members of this this group by adding appropriate
|
||||
{{by group}} clause to an access directive in {{slapd.conf}}(5). For instance,
|
||||
|
||||
> access to dn.children="dc=example,dc=com"
|
||||
> by self write
|
||||
> by group.exact="cn=Administrators,dc=example,dc=com" write
|
||||
> by * auth
|
||||
|
||||
Like by {[dn}} clauses, one can also use {{expand}} to expand the group name
|
||||
based upon the regular expression matching of the target, that is, the to {{dn.regex}}).
|
||||
For instance,
|
||||
|
||||
> access to dn.regex="(.+,)?ou=People,(dc=[^,]+,dc=[^,]+)$"
|
||||
> attrs=children,entry,uid
|
||||
> by group.expand="cn=Managers,$2" write
|
||||
> by users read
|
||||
> by * auth
|
||||
|
||||
|
||||
The above illustration assumed that the group members are to be found in the
|
||||
{{member}} attribute type of the {{groupOfNames}} object class. If you need to
|
||||
use a different group object and/or a different attribute type then use the
|
||||
following {{slapd.conf}}(5) (abbreviated) syntax:
|
||||
|
||||
> access to <what>
|
||||
> by group/<objectclass>/<attributename>=<DN> <access>
|
||||
|
||||
For example:
|
||||
|
||||
> access to *
|
||||
> by group/organizationalRole/roleOccupant="cn=Administrator,dc=example,dc=com" write
|
||||
|
||||
In this case, we have an ObjectClass {{organizationalRole}} which contains the
|
||||
administrator DN's in the {{roleOccupant}} attribute. For instance:
|
||||
|
||||
> dn: cn=Administrator,dc=example,dc=com
|
||||
> cn: Administrator
|
||||
> objectclass: organizationalRole
|
||||
> roleOccupant: cn=Jane Doe,dc=example,dc=com
|
||||
|
||||
Note: the specified member attribute type MUST be of DN or {{NameAndOptionalUID}} syntax,
|
||||
and the specified object class SHOULD allow the attribute type.
|
||||
|
||||
Dynamic Groups are also supported in Access Control. Please see {{slapo-dynlist}}(5)
|
||||
and the {{SECT:Dynamic Lists}} overlay section.
|
||||
|
||||
|
||||
H3: Granting access to a subset of attributes
|
||||
|
||||
You can grant access to a set of attributes by specifying a list of attribute names
|
||||
in the ACL {{to}} clause. To be useful, you also need to grant access to the
|
||||
{{entry}} itself. Also note how {{children}} controls the ability to add, delete,
|
||||
and rename entries.
|
||||
|
||||
> # mail: self may write, authenticated users may read
|
||||
> access to attrs=mail
|
||||
> by self write
|
||||
> by users read
|
||||
> by * none
|
||||
>
|
||||
> # cn, sn: self my write, all may read
|
||||
> access to attrs=cn,sn
|
||||
> by self write
|
||||
> by * read
|
||||
>
|
||||
> # immediate children: only self can add/delete entries under this entry
|
||||
> access to attrs=children
|
||||
> by self write
|
||||
>
|
||||
> # entry itself: self may write, all may read
|
||||
> access to attrs=entry
|
||||
> by self write
|
||||
> by * read
|
||||
>
|
||||
> # other attributes: self may write, others have no access
|
||||
> access to *
|
||||
> by self write
|
||||
> by * none
|
||||
|
||||
ObjectClass names may also be specified in this list, which will affect
|
||||
all the attributes that are required and/or allowed by that {{objectClass}}.
|
||||
Actually, names in {{attrlist}} that are prefixed by {{@}} are directly treated
|
||||
as objectClass names. A name prefixed by {{!}} is also treated as an objectClass,
|
||||
but in this case the access rule affects the attributes that are not required
|
||||
nor allowed by that {{objectClass}}.
|
||||
|
||||
|
||||
H3: Allowing a user write to all entries below theirs
|
||||
|
||||
For a setup where a user can write to its own record and to all of its children:
|
||||
|
||||
> access to dn.regex="(.+,)?(uid=[^,]+,o=Company)$"
|
||||
> by dn.exact,expand="$2" write
|
||||
> by anonymous auth
|
||||
|
||||
(Add more examples for above)
|
||||
|
||||
|
||||
H3: Allowing entry creation
|
||||
|
||||
Let's say, you have it like this:
|
||||
|
||||
> o=<basedn>
|
||||
> ou=domains
|
||||
> associatedDomain=<somedomain>
|
||||
> ou=users
|
||||
> uid=<someuserid>
|
||||
> uid=<someotheruserid>
|
||||
> ou=addressbooks
|
||||
> uid=<someuserid>
|
||||
> cn=<someone>
|
||||
> cn=<someoneelse>
|
||||
|
||||
and, for another domain <someotherdomain>:
|
||||
|
||||
> o=<basedn>
|
||||
> ou=domains
|
||||
> associatedDomain=<someotherdomain>
|
||||
> ou=users
|
||||
> uid=<someuserid>
|
||||
> uid=<someotheruserid>
|
||||
> ou=addressbooks
|
||||
> uid=<someotheruserid>
|
||||
> cn=<someone>
|
||||
> cn=<someoneelse>
|
||||
|
||||
then, if you wanted user {{uid=<someuserid>}} to {{B:ONLY}} create an entry
|
||||
for its own thing, you could write an ACL like this:
|
||||
|
||||
> # this rule lets users of "associatedDomain=<matcheddomain>"
|
||||
> # write under "ou=addressbook,associatedDomain=<matcheddomain>,ou=domains,o=<basedn>",
|
||||
> # i.e. a user can write ANY entry below its domain's address book;
|
||||
> # this permission is necessary, but not sufficient, the next
|
||||
> # will restrict this permission further
|
||||
>
|
||||
>
|
||||
> access to dn.regex="^ou=addressbook,associatedDomain=([^,]+),ou=domains,o=<basedn>$" attrs=children
|
||||
> by dn.regex="^uid=([^,]+),ou=users,associatedDomain=$1,ou=domains,o=<basedn>$$" write
|
||||
> by * none
|
||||
>
|
||||
>
|
||||
> # Note that above the "by" clause needs a "regex" style to make sure
|
||||
> # it expands to a DN that starts with a "uid=<someuserid>" pattern
|
||||
> # while substituting the associatedDomain submatch from the "what" clause.
|
||||
>
|
||||
>
|
||||
> # This rule lets a user with "uid=<matcheduid>" of "<associatedDomain=matcheddomain>"
|
||||
> # write (i.e. add, modify, delete) the entry whose DN is exactly
|
||||
> # "uid=<matcheduid>,ou=addressbook,associatedDomain=<matcheddomain>,ou=domains,o=<basedn>"
|
||||
> # and ANY entry as subtree of it
|
||||
>
|
||||
>
|
||||
> access to dn.regex="^(.+,)?uid=([^,]+),ou=addressbook,associatedDomain=([^,]+),ou=domains,o=<basedn>$"
|
||||
> by dn.exact,expand="uid=$2,ou=users,associatedDomain=$3,ou=domains,o=<basedn>" write
|
||||
> by * none
|
||||
>
|
||||
>
|
||||
> # Note that above the "by" clause uses the "exact" style with the "expand"
|
||||
> # modifier because now the whole pattern can be rebuilt by means of the
|
||||
> # submatches from the "what" clause, so a "regex" compilation and evaluation
|
||||
> # is no longer required.
|
||||
|
||||
|
||||
H3: Tips for using regular expressions in Access Control
|
||||
|
||||
Always use {{dn.regex=<pattern>}} when you intend to use regular expression
|
||||
matching. {{dn=<pattern>}} alone defaults to {{dn.exact<pattern>}}.
|
||||
|
||||
Use {{(.+)}} instead of {{(.*)}} when you want at least one char to be matched.
|
||||
{{(.*)}} matches the empty string as well.
|
||||
|
||||
Don't use regular expressions for matches that can be done otherwise in a safer
|
||||
and cheaper manner. Examples:
|
||||
|
||||
> dn.regex=".*dc=example,dc=com"
|
||||
|
||||
is unsafe and expensive:
|
||||
|
||||
* unsafe because any string containing {{dc=example,dc=com }}will match,
|
||||
not only those that end with the desired pattern; use {{.*dc=example,dc=com$}} instead.
|
||||
* unsafe also because it would allow any {{attributeType}} ending with {{dc}}
|
||||
as naming attribute for the first RDN in the string, e.g. a custom attributeType
|
||||
{{mydc}} would match as well. If you really need a regular expression that allows
|
||||
just {{dc=example,dc=com}} or any of its subtrees, use {{^(.+,)?dc=example,dc=com$}},
|
||||
which means: anything to the left of dc=..., if any (the question mark after the
|
||||
pattern within brackets), must end with a comma;
|
||||
* expensive because if you don't need submatches, you could use scoping styles, e.g.
|
||||
|
||||
> dn.subtree="dc=example,dc=com"
|
||||
|
||||
to include {{dc=example,dc=com}} in the matching patterns,
|
||||
|
||||
> dn.children="dc=example,dc=com"
|
||||
|
||||
to exclude {{dc=example,dc=com}} from the matching patterns, or
|
||||
|
||||
> dn.onelevel="dc=example,dc=com"
|
||||
|
||||
to allow exactly one sublevel matches only.
|
||||
|
||||
Always use {{^}} and {{$}} in regexes, whenever appropriate, because
|
||||
{{ou=(.+),ou=(.+),ou=addressbooks,o=basedn}} will match
|
||||
{{something=bla,ou=xxx,ou=yyy,ou=addressbooks,o=basedn,ou=addressbooks,o=basedn,dc=some,dc=org}}
|
||||
|
||||
Always use {{([^,]+)}} to indicate exactly one RDN, because {{(.+)}} can
|
||||
include any number of RDNs; e.g. {{ou=(.+),dc=example,dc=com}} will match
|
||||
{{ou=My,o=Org,dc=example,dc=com}}, which might not be what you want.
|
||||
|
||||
Never add the rootdn to the by clauses. ACLs are not even processed for operations
|
||||
performed with rootdn identity (otherwise there would be no reason to define a
|
||||
rootdn at all).
|
||||
|
||||
Use shorthands. The user directive matches authenticated users and the anonymous
|
||||
directive matches anonymous users.
|
||||
|
||||
Don't use the {{dn.regex}} form for <by> clauses if all you need is scoping
|
||||
and/or substring replacement; use scoping styles (e.g. {{exact}}, {{onelevel}},
|
||||
{{children}} or {{subtree}}) and the style modifier expand to cause substring expansion.
|
||||
|
||||
For instance,
|
||||
|
||||
> access to dn.regex=".+,dc=([^,]+),dc=([^,]+)$"
|
||||
> by dn.regex="^[^,],ou=Admin,dc=$1,dc=$2$$" write
|
||||
|
||||
although correct, can be safely and efficiently replaced by
|
||||
|
||||
> access to dn.regex=".+,(dc=[^,]+,dc=[^,]+)$"
|
||||
> by dn.onelevel,expand="ou=Admin,$1" write
|
||||
|
||||
where the regex in the {{<what>}} clause is more compact, and the one in the {{<by>}}
|
||||
clause is replaced by a much more efficient scoping style of onelevel with substring expansion.
|
||||
|
||||
|
||||
H3: Granting and Denying access based on security strength factors (ssf)
|
||||
|
||||
You can restrict access based on the security strength factor (SSF)
|
||||
|
||||
> access to dn="cn=example,cn=edu"
|
||||
> by * ssf=256 read
|
||||
|
||||
0 (zero) implies no protection,
|
||||
1 implies integrity protection only,
|
||||
56 DES or other weak ciphers,
|
||||
112 triple DES and other strong ciphers,
|
||||
128 RC4, Blowfish and other modern strong ciphers.
|
||||
|
||||
Other possibilities:
|
||||
|
||||
> transport_ssf=<n>
|
||||
> tls_ssf=<n>
|
||||
> sasl_ssf=<n>
|
||||
|
||||
256 is recommended.
|
||||
|
||||
See {{slapd.conf}}(5) for information on {{ssf}}.
|
||||
|
||||
|
||||
H3: When things aren't working as expected
|
||||
|
||||
Consider this example:
|
||||
|
||||
> access to *
|
||||
> by anonymous auth
|
||||
>
|
||||
> access to *
|
||||
> by self write
|
||||
>
|
||||
> access to *
|
||||
> by users read
|
||||
|
||||
You may think this will allow any user to login, to read everything and change
|
||||
his own data if he is logged in. But in this example only the login works and
|
||||
an ldapsearch returns no data. The Problem is that SLAPD goes through its access
|
||||
config line by line and stops as soon as it finds a match in the part of the
|
||||
access rule.(here: {{to *}})
|
||||
|
||||
To get what we wanted the file has to read:
|
||||
|
||||
> access to *
|
||||
> by anonymous auth
|
||||
> by self write
|
||||
> by users read
|
||||
|
||||
The general rule is: "special access rules first, generic access rules last"
|
||||
|
||||
See also {{slapd.access}}(8), loglevel 128 and {{slapacl}}(8) for debugging
|
||||
information.
|
||||
|
||||
|
||||
H2: Sets - Granting rights based on relationships
|
||||
|
||||
Sets are best illustrated via examples. The following sections will present
|
||||
|
@ -1,5 +1,6 @@
|
||||
personal_ws-1.1 en 1567
|
||||
personal_ws-1.1 en 1590
|
||||
commonName
|
||||
bla
|
||||
Masarati
|
||||
subjectAltName
|
||||
api
|
||||
@ -201,6 +202,7 @@ OpenSSL
|
||||
openssl
|
||||
LOF
|
||||
AVAs
|
||||
associatedDomain
|
||||
organizationalRole
|
||||
initgroups
|
||||
olcDbCachesize
|
||||
@ -277,9 +279,9 @@ pos
|
||||
sbi
|
||||
PRD
|
||||
pre
|
||||
sudoadm
|
||||
stringal
|
||||
retoidp
|
||||
sudoadm
|
||||
sdf
|
||||
efgh
|
||||
accesslog
|
||||
@ -463,6 +465,7 @@ ip
|
||||
referralsRequired
|
||||
ld
|
||||
Matic
|
||||
regexes
|
||||
subfinal
|
||||
pseudorootpw
|
||||
md
|
||||
@ -484,9 +487,11 @@ mr
|
||||
ok
|
||||
mv
|
||||
LTVERSION
|
||||
someotheruserid
|
||||
rc
|
||||
realdn
|
||||
ou
|
||||
yyy
|
||||
sb
|
||||
enum
|
||||
auditContext
|
||||
@ -519,12 +524,14 @@ attrlist
|
||||
Vu
|
||||
Za
|
||||
PDkzODdASFxOQ
|
||||
MyOrganization
|
||||
ws
|
||||
cacert
|
||||
notAllowedOnNonLeaf
|
||||
attrname
|
||||
olcTLSCipherSuite
|
||||
x's
|
||||
xw
|
||||
octetStringMatch
|
||||
mechs
|
||||
ZZ
|
||||
@ -559,6 +566,7 @@ IANA
|
||||
localhost
|
||||
offsite
|
||||
bindir
|
||||
fred
|
||||
olcUpdateref
|
||||
bindwhen
|
||||
UMLDAP
|
||||
@ -660,12 +668,14 @@ noanonymous
|
||||
LIBVERSION
|
||||
Symas
|
||||
dcedn
|
||||
sublevel
|
||||
chroot
|
||||
posixGroup
|
||||
nretries
|
||||
testgroup
|
||||
ldaphost
|
||||
frontend
|
||||
someotherdomain
|
||||
proxying
|
||||
organisations
|
||||
rewriteMap
|
||||
@ -729,6 +739,7 @@ LTFINISH
|
||||
olcOverlay
|
||||
lber
|
||||
serverID
|
||||
blogs
|
||||
numResponses
|
||||
lang
|
||||
POSIX
|
||||
@ -759,6 +770,7 @@ testTwo
|
||||
ldif
|
||||
entryAlreadyExists
|
||||
plaintext
|
||||
someoneelse
|
||||
errDisconnect
|
||||
username
|
||||
accessee
|
||||
@ -773,6 +785,7 @@ makeinfo
|
||||
chmod
|
||||
auditWriteObject
|
||||
Jong
|
||||
addressbooks
|
||||
setspec
|
||||
syncprov
|
||||
dctree
|
||||
@ -783,6 +796,7 @@ dSAOperation
|
||||
datadir
|
||||
slapadd
|
||||
reqFilter
|
||||
matcheddomain
|
||||
CThreads
|
||||
slapacl
|
||||
requestName
|
||||
@ -803,6 +817,7 @@ slapdconfig
|
||||
entrylimit
|
||||
departmentNumber
|
||||
immSupr
|
||||
addressbook
|
||||
pidfile
|
||||
online
|
||||
logold
|
||||
@ -1033,6 +1048,7 @@ compareFalse
|
||||
lsasl
|
||||
caseIgnoreSubstringsMatch
|
||||
AUTOREMOVE
|
||||
mydc
|
||||
searchResultEntry
|
||||
PIII
|
||||
olcDbShmKey
|
||||
@ -1111,6 +1127,7 @@ reqControls
|
||||
modme
|
||||
shtool
|
||||
aXRoIGEgc
|
||||
RDNs
|
||||
rdns
|
||||
modifyTimestamp
|
||||
objectIdentiferMatch
|
||||
@ -1137,6 +1154,7 @@ george
|
||||
LDAPSyntax
|
||||
apache's
|
||||
scdx
|
||||
someuserid
|
||||
attrtype
|
||||
msgtype
|
||||
pathtest
|
||||
@ -1285,6 +1303,7 @@ invalidDNSyntax
|
||||
zeilenga
|
||||
addAttrDN
|
||||
syncdata
|
||||
somedomain
|
||||
attrsonly
|
||||
attrsOnly
|
||||
numericString
|
||||
@ -1347,6 +1366,7 @@ basedn
|
||||
baseDN
|
||||
bvstr
|
||||
replog
|
||||
adressbooks
|
||||
databasenumber
|
||||
subschema
|
||||
PhotoObject
|
||||
@ -1410,6 +1430,7 @@ unmassaged
|
||||
LDAPMod
|
||||
ldapmod
|
||||
srcdir
|
||||
someSSHAdata
|
||||
whsp
|
||||
exattrs
|
||||
reqOld
|
||||
@ -1418,6 +1439,7 @@ monitorCounter
|
||||
quickstart
|
||||
UUID
|
||||
olcConstraintConfig
|
||||
roleOccupant
|
||||
rootpw
|
||||
veryclean
|
||||
syslogged
|
||||
@ -1439,6 +1461,7 @@ ucdata
|
||||
toolsets
|
||||
builddir
|
||||
builtin
|
||||
matcheduid
|
||||
Locator
|
||||
ldapmaster
|
||||
libldap
|
||||
@ -1521,8 +1544,8 @@ wBDARESEhgVG
|
||||
multi
|
||||
aaa
|
||||
ldaprc
|
||||
UpdateDN
|
||||
updatedn
|
||||
UpdateDN
|
||||
LDAPBASE
|
||||
LDAPAPIFeatureInfo
|
||||
authzTo
|
||||
|
Loading…
Reference in New Issue
Block a user