Моя цель здесь — получить диспетчер вложений, который (1) эффективно использует SQL FILESTREAM в том виде, в котором он был разработан, (2) ограничивает доступ к SQL FileTable и его базовому каталогу FILESTREAM Windows одной из двух учетных записей Active Directory (учетные данные предоставляются файлом web/app.config), и (3) эффективно игнорирует текущего пользователя Windows Auth для любого сайта или приложения, в которое включена эта библиотека.
Я уже видел несколько сообщений об этой проблеме, и я полагаю, что испробовал почти все предложения, которые я нашел во всех них, все еще без особого успеха.
У меня есть:
- Активирована встроенная защита в моей строке подключения:
"Server={server};Database=AttachmentsGlobal;User ID={domain}\\{user};Password={password};Trusted_Connection=False;Integrated Security=true;"
- Учитывая тот же самый пользовательский доступ RW к каталогу FileTable на Windows Server 2012, где работает SQL Server 2012, путем изменения разрешений для папки.
- Предоставление пользователю прав на выбор, изменение, обновление, вставку и удаление файловой таблицы и столбца
filestream
. - Реализован пользовательский класс олицетворения, который я нашел в Интернете, чтобы олицетворять того же пользователя при вызове объекта
SqlFileStream
(см. ниже). - Пытался найти ошибки на стороне SQL Server. Все, что я могу найти в средстве просмотра событий, это то, что олицетворение, которое я пытаюсь выполнить ниже, успешно регистрирует пользователя.
- Убедитесь, что разрешение удаленных подключений включено в настройках FILESTREAM в SQL Server.
Он работает всякий раз, когда я отключаю блок using(Impersonator){}
в SaveFile
ниже, но в противном случае он не работает. Также кажется, что строка подключения игнорируется при локальной отладке: моя учетная запись пользователя имеет доступ к папке вложений, потому что я администратор, но когда я изменяю строку подключения на пользователя, который явно НЕ имеет доступа, он по-прежнему загружается нормально . Я предполагаю, что это параметр интегрированной безопасности, который передает учетные данные текущего пользователя приложения на сервер и игнорирует предоставленного пользователя. Я не уверен, как это хорошо проверить, если не считать запуска Visual Studio от имени другого пользователя (что я пробовал, и это не удалось).
Пожалуйста, скажите мне, что я делаю что-то глупое и глупое, и что я достаточно близок к решению. Эта невероятно расплывчатая ошибка отказа в доступе сводит меня с ума! Спасибо.
C#:
// SqlFileItem is just a simple model mapping all the columns of the file
// table (except the filestream itself)
public SqlFileItem UploadFile(Stream fileStream, string fileName,
SqlFileItem parent, AttachmentType restricted) {
const string uploadSproc = "InsertAttachment";
const string metadataSproc = "GetMetadata";
SqlFileItem rt;
using (TransactionScope ts = new TransactionScope()) {
using (SqlConnection conn = GetConnection(restricted)) {
conn.Open();
string serverPath;
byte[] serverXfer;
Guid streamIdOfUploadedFile;
using (SqlCommand cmd = conn.CreateCommand()) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = uploadSproc;
cmd.Parameters.Add("@fName", SqlDbType.VarChar).Value =
fileName;
cmd.Parameters.Add("@parent", SqlDbType.VarChar).Value =
parent.path_locator.ToString();
using (SqlDataReader rdr = cmd.ExecuteReader()) {
rdr.Read();
serverPath = rdr.GetSqlString(0).Value;
serverXfer = rdr.GetSqlBinary(1).Value;
streamIdOfUploadedFile = rdr.GetGuid(2);
rdr.Close();
}
}
// See below for SaveFile
SaveFile(fileStream, serverPath, serverXfer, exportRestriction);
using (SqlCommand cmd = conn.CreateCommand()) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = metadataSproc;
cmd.Parameters.Add("@streamId",
SqlDbType.UniqueIdentifier).Value =
streamIdOfUploadedFile;
using (SqlDataReader rdr = cmd.ExecuteReader()) {
rt =
DataReaderToList<SqlFileItem>(rdr)
.FirstOrDefault();
}
}
}
ts.Complete();
}
return rt;
}
// Performs the actual streaming/saving operation.
private static void SaveFile(Stream src, string serverPath,
byte[] serverXfer, AttachmentType restricted) {
const int blockSize = 1024*512;
// My attempt at impersonating
Impersonator imp = restricted == AttachmentType.Open
? new Impersonator(@"{openActiveDirectoryUser}", @"{domain}",
@"{password}")
: new Impersonator(@"{restrictedActiveDirectoryUser}", @"{domain}",
@"{password}");
using (imp) {
using (src) {
using (
// The line below is where it breaks with a Win32Exception.
// The full text of it from VS2015 is provided below.
SqlFileStream dest = new SqlFileStream(serverPath,
serverXfer, FileAccess.Write)) {
byte[] buffer = new byte[blockSize];
int bytesRead;
while ((bytesRead = src.Read(buffer, 0, buffer.Length)) >
0) {
dest.Write(buffer, 0, bytesRead);
dest.Flush();
}
dest.Close();
}
src.Close();
}
}
}
Класс подражателя найден здесь
SQL:
ALTER PROCEDURE [dbo].[InsertAttachment]
@fileName varchar(255),
@parent hierarchyid
AS
BEGIN
SET NOCOUNT ON;
DECLARE @returns TABLE (stream_id UNIQUEIDENTIFIER)
INSERT INTO Attachments(file_stream, name, is_directory, path_locator)
OUTPUT inserted.stream_id INTO @returns
VALUES (0x, @fileName, 0, dbo.GetCustomPathLocator(@parent.ToString()))
SELECT file_stream.PathName()
,GET_FILESTREAM_TRANSACTION_CONTEXT()
,aft.stream_id
FROM Attachments att
INNER JOIN @returns rt on att.stream_id = rt.stream_id
END
ALTER PROCEDURE [dbo].[GetMetadata]
@streamId uniqueidentifier
AS
BEGIN
SELECT stream_id
-- omit file_stream varbinary(max)
,name
,path_locator
,parent_path_locator
,file_type
,cached_file_size
,creation_time
,last_write_time
,last_access_time
,is_directory
-- omit remaining is_* flags
FROM Attachments
WHERE stream_id = @streamId
END
Исключение:
System.ComponentModel.Win32Exception was unhandled
ErrorCode=-2147467259
HResult=-2147467259
Message=Access is denied
NativeErrorCode=5
Source=System.Data
StackTrace:
at System.Data.SqlTypes.SqlFileStream.OpenSqlFileStream(String path, Byte[] transactionContext, FileAccess access, FileOptions options, Int64 allocationSize)
at System.Data.SqlTypes.SqlFileStream..ctor(String path, Byte[] transactionContext, FileAccess access, FileOptions options, Int64 allocationSize)
at System.Data.SqlTypes.SqlFileStream..ctor(String path, Byte[] transactionContext, FileAccess access)
at FileTableAttachmentLibrary.AttachmentManager.SaveFile(Stream src, String serverPath, Byte[] serverXfer, AttachmentScope exportRestricted) in C:\_projects\FileTableAttachmentLibrary\FileTableAttachmentLibrary\FileTableAttachmentLibrary\AttachmentManager.cs:line 498
at FileTableAttachmentLibrary.AttachmentManager.UploadFile(Stream fileStream, String fileName, SqlFileItem parent, AttachmentScope exportRestriction) in C:\_projects\FileTableAttachmentLibrary\FileTableAttachmentLibrary\FileTableAttachmentLibrary\AttachmentManager.cs:line 210
at TestAttachments.Program.AttachmentManagerTest() in C:\_projects\FileTableAttachmentLibrary\FileTableAttachmentLibrary\TestAttachments\Program.cs:line 69
at TestAttachments.Program.Main(String[] args) in C:\_projects\FileTableAttachmentLibrary\FileTableAttachmentLibrary\TestAttachments\Program.cs:line 26
InnerException: null