stdlib/os: add isAdmin (#17012)
* stdlib/os: add isAdmin * uint8 -> cuchar, assert isAdmin on Azure Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Update lib/pure/os.nim docs Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Address comments on #17012 * Raise on errors in #17012 * Check the result of FreeSid in #17012 * Change case in #17012 * Fix memory leak in #17012 * Address comments in #17012 Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> Co-authored-by: Timothee Cour <timothee.cour2@gmail.com>
This commit is contained in:
parent
d1e093207a
commit
31424b3808
4 changed files with 84 additions and 0 deletions
|
|
@ -148,6 +148,9 @@ provided by the operating system.
|
||||||
(instead of skipping them sometimes as it was before).
|
(instead of skipping them sometimes as it was before).
|
||||||
- Added optional `followSymlinks` argument to `setFilePermissions`.
|
- Added optional `followSymlinks` argument to `setFilePermissions`.
|
||||||
|
|
||||||
|
- Added `os.isAdmin` to tell whether the caller's process is a member of the
|
||||||
|
Administrators local group (on Windows) or a root (on POSIX).
|
||||||
|
|
||||||
- Added `random.initRand()` overload with no argument which uses the current time as a seed.
|
- Added `random.initRand()` overload with no argument which uses the current time as a seed.
|
||||||
|
|
||||||
- Added experimental `linenoise.readLineStatus` to get line and status (e.g. ctrl-D or ctrl-C).
|
- Added experimental `linenoise.readLineStatus` to get line and status (e.g. ctrl-D or ctrl-C).
|
||||||
|
|
|
||||||
|
|
@ -1688,6 +1688,36 @@ proc setFilePermissions*(filename: string, permissions: set[FilePermission],
|
||||||
var res2 = setFileAttributesA(filename, res)
|
var res2 = setFileAttributesA(filename, res)
|
||||||
if res2 == - 1'i32: raiseOSError(osLastError(), $(filename, permissions))
|
if res2 == - 1'i32: raiseOSError(osLastError(), $(filename, permissions))
|
||||||
|
|
||||||
|
proc isAdmin*: bool {.noWeirdTarget.} =
|
||||||
|
## Returns whether the caller's process is a member of the Administrators local
|
||||||
|
## group (on Windows) or a root (on POSIX), via `geteuid() == 0`.
|
||||||
|
when defined(windows):
|
||||||
|
# Rewrite of the example from Microsoft Docs:
|
||||||
|
# https://docs.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-checktokenmembership#examples
|
||||||
|
# and corresponding PostgreSQL function:
|
||||||
|
# https://doxygen.postgresql.org/win32security_8c.html#ae6b61e106fa5d6c5d077a9d14ee80569
|
||||||
|
var ntAuthority = SID_IDENTIFIER_AUTHORITY(value: SECURITY_NT_AUTHORITY)
|
||||||
|
var administratorsGroup: PSID
|
||||||
|
if not isSuccess(allocateAndInitializeSid(addr ntAuthority,
|
||||||
|
BYTE(2),
|
||||||
|
SECURITY_BUILTIN_DOMAIN_RID,
|
||||||
|
DOMAIN_ALIAS_RID_ADMINS,
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
addr administratorsGroup)):
|
||||||
|
raiseOSError(osLastError(), "could not get SID for Administrators group")
|
||||||
|
|
||||||
|
defer:
|
||||||
|
if freeSid(administratorsGroup) != nil:
|
||||||
|
raiseOSError(osLastError(), "failed to free SID for Administrators group")
|
||||||
|
|
||||||
|
var b: WINBOOL
|
||||||
|
if not isSuccess(checkTokenMembership(0, administratorsGroup, addr b)):
|
||||||
|
raiseOSError(osLastError(), "could not check access token membership")
|
||||||
|
|
||||||
|
return isSuccess(b)
|
||||||
|
else:
|
||||||
|
return geteuid() == 0
|
||||||
|
|
||||||
proc createSymlink*(src, dest: string) {.noWeirdTarget.} =
|
proc createSymlink*(src, dest: string) {.noWeirdTarget.} =
|
||||||
## Create a symbolic link at `dest` which points to the item specified
|
## Create a symbolic link at `dest` which points to the item specified
|
||||||
## by `src`. On most operating systems, will fail if a link already exists.
|
## by `src`. On most operating systems, will fail if a link already exists.
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ when useWinUnicode:
|
||||||
else:
|
else:
|
||||||
type WinChar* = char
|
type WinChar* = char
|
||||||
|
|
||||||
|
# See https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
||||||
type
|
type
|
||||||
Handle* = int
|
Handle* = int
|
||||||
LONG* = int32
|
LONG* = int32
|
||||||
|
|
@ -33,6 +34,7 @@ type
|
||||||
WINBOOL* = int32
|
WINBOOL* = int32
|
||||||
## `WINBOOL` uses opposite convention as posix, !=0 meaning success.
|
## `WINBOOL` uses opposite convention as posix, !=0 meaning success.
|
||||||
# xxx this should be distinct int32, distinct would make code less error prone
|
# xxx this should be distinct int32, distinct would make code less error prone
|
||||||
|
PBOOL* = ptr WINBOOL
|
||||||
DWORD* = int32
|
DWORD* = int32
|
||||||
PDWORD* = ptr DWORD
|
PDWORD* = ptr DWORD
|
||||||
LPINT* = ptr int32
|
LPINT* = ptr int32
|
||||||
|
|
@ -40,6 +42,7 @@ type
|
||||||
PULONG_PTR* = ptr uint
|
PULONG_PTR* = ptr uint
|
||||||
HDC* = Handle
|
HDC* = Handle
|
||||||
HGLRC* = Handle
|
HGLRC* = Handle
|
||||||
|
BYTE* = cuchar
|
||||||
|
|
||||||
SECURITY_ATTRIBUTES* {.final, pure.} = object
|
SECURITY_ATTRIBUTES* {.final, pure.} = object
|
||||||
nLength*: int32
|
nLength*: int32
|
||||||
|
|
@ -136,6 +139,10 @@ const
|
||||||
|
|
||||||
HANDLE_FLAG_INHERIT* = 0x00000001'i32
|
HANDLE_FLAG_INHERIT* = 0x00000001'i32
|
||||||
|
|
||||||
|
proc isSuccess*(a: WINBOOL): bool {.inline.} =
|
||||||
|
## Returns true if `a != 0`. Windows uses a different convention than POSIX,
|
||||||
|
## where `a == 0` is commonly used on success.
|
||||||
|
a != 0
|
||||||
proc getVersionExW*(lpVersionInfo: ptr OSVERSIONINFO): WINBOOL {.
|
proc getVersionExW*(lpVersionInfo: ptr OSVERSIONINFO): WINBOOL {.
|
||||||
stdcall, dynlib: "kernel32", importc: "GetVersionExW", sideEffect.}
|
stdcall, dynlib: "kernel32", importc: "GetVersionExW", sideEffect.}
|
||||||
proc getVersionExA*(lpVersionInfo: ptr OSVERSIONINFO): WINBOOL {.
|
proc getVersionExA*(lpVersionInfo: ptr OSVERSIONINFO): WINBOOL {.
|
||||||
|
|
@ -1129,5 +1136,42 @@ proc setFileTime*(hFile: Handle, lpCreationTime: LPFILETIME,
|
||||||
lpLastAccessTime: LPFILETIME, lpLastWriteTime: LPFILETIME): WINBOOL
|
lpLastAccessTime: LPFILETIME, lpLastWriteTime: LPFILETIME): WINBOOL
|
||||||
{.stdcall, dynlib: "kernel32", importc: "SetFileTime".}
|
{.stdcall, dynlib: "kernel32", importc: "SetFileTime".}
|
||||||
|
|
||||||
|
type
|
||||||
|
# https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid_identifier_authority
|
||||||
|
SID_IDENTIFIER_AUTHORITY* {.importc, header: "<windows.h>".} = object
|
||||||
|
value* {.importc: "Value"}: array[6, BYTE]
|
||||||
|
# https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid
|
||||||
|
SID* {.importc, header: "<windows.h>".} = object
|
||||||
|
Revision: BYTE
|
||||||
|
SubAuthorityCount: BYTE
|
||||||
|
IdentifierAuthority: SID_IDENTIFIER_AUTHORITY
|
||||||
|
SubAuthority: ptr ptr DWORD
|
||||||
|
PSID* = ptr SID
|
||||||
|
|
||||||
|
const
|
||||||
|
# https://docs.microsoft.com/en-us/windows/win32/secauthz/sid-components
|
||||||
|
# https://github.com/mirror/mingw-w64/blob/84c950bdab7c999ace49fe8383856be77f88c4a8/mingw-w64-headers/include/winnt.h#L2994
|
||||||
|
SECURITY_NT_AUTHORITY* = [BYTE(0), BYTE(0), BYTE(0), BYTE(0), BYTE(0), BYTE(5)]
|
||||||
|
SECURITY_BUILTIN_DOMAIN_RID* = 32
|
||||||
|
DOMAIN_ALIAS_RID_ADMINS* = 544
|
||||||
|
|
||||||
|
proc allocateAndInitializeSid*(pIdentifierAuthority: ptr SID_IDENTIFIER_AUTHORITY,
|
||||||
|
nSubAuthorityCount: BYTE,
|
||||||
|
nSubAuthority0: DWORD,
|
||||||
|
nSubAuthority1: DWORD,
|
||||||
|
nSubAuthority2: DWORD,
|
||||||
|
nSubAuthority3: DWORD,
|
||||||
|
nSubAuthority4: DWORD,
|
||||||
|
nSubAuthority5: DWORD,
|
||||||
|
nSubAuthority6: DWORD,
|
||||||
|
nSubAuthority7: DWORD,
|
||||||
|
pSid: ptr PSID): WINBOOL
|
||||||
|
{.stdcall, dynlib: "Advapi32", importc: "AllocateAndInitializeSid".}
|
||||||
|
proc checkTokenMembership*(tokenHandle: Handle, sidToCheck: PSID,
|
||||||
|
isMember: PBOOL): WINBOOL
|
||||||
|
{.stdcall, dynlib: "Advapi32", importc: "CheckTokenMembership".}
|
||||||
|
proc freeSid*(pSid: PSID): PSID
|
||||||
|
{.stdcall, dynlib: "Advapi32", importc: "FreeSid".}
|
||||||
|
|
||||||
when defined(nimHasStyleChecks):
|
when defined(nimHasStyleChecks):
|
||||||
{.pop.} # {.push styleChecks: off.}
|
{.pop.} # {.push styleChecks: off.}
|
||||||
|
|
|
||||||
|
|
@ -655,3 +655,10 @@ block: # normalizeExe
|
||||||
doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar"
|
doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar"
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
doAssert "foo".dup(normalizeExe) == "foo"
|
doAssert "foo".dup(normalizeExe) == "foo"
|
||||||
|
|
||||||
|
block: # isAdmin
|
||||||
|
let isAzure = existsEnv("TF_BUILD") # xxx factor with testament.specs.isAzure
|
||||||
|
# In Azure on Windows tests run as an admin user
|
||||||
|
if isAzure and defined(windows): doAssert isAdmin()
|
||||||
|
# In Azure on POSIX tests run as a normal user
|
||||||
|
if isAzure and defined(posix): doAssert not isAdmin()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue