Merge lp:~mandel/ubuntuone-windows-installer/add_ubuntu_sso_service_keyring into lp:ubuntuone-windows-installer/beta

Proposed by Manuel de la Peña
Status: Merged
Approved by: Vincenzo Di Somma
Approved revision: 63
Merged at revision: 84
Proposed branch: lp:~mandel/ubuntuone-windows-installer/add_ubuntu_sso_service_keyring
Merge into: lp:ubuntuone-windows-installer/beta
Diff against target: 1686 lines (+1486/-11)
16 files modified
.bzrignore (+2/-0)
main.build (+10/-2)
src/Canonical.Ubuntu.SSO.Tests/Canonical.Ubuntu.SSO.Tests.csproj (+74/-0)
src/Canonical.Ubuntu.SSO.Tests/KeyringFixture.cs (+409/-0)
src/Canonical.Ubuntu.SSO.Tests/Properties/AssemblyInfo.cs (+36/-0)
src/Canonical.Ubuntu.SSO/Canonical.Ubuntu.SSO.csproj (+19/-0)
src/Canonical.Ubuntu.SSO/DPAPIDataProtector.cs (+65/-0)
src/Canonical.Ubuntu.SSO/IDataProtector.cs (+48/-0)
src/Canonical.Ubuntu.SSO/IKeyring.cs (+8/-7)
src/Canonical.Ubuntu.SSO/IRegistryKey.cs (+171/-0)
src/Canonical.Ubuntu.SSO/Keyring.cs (+282/-0)
src/Canonical.Ubuntu.SSO/Properties/AssemblyInfo.cs (+1/-2)
src/Canonical.Ubuntu.SSO/RegistryKeyWrapper.cs (+261/-0)
src/Canonical.UbuntuOne.Common.Tests/Validation/EqualityExtensionsValidation.cs (+38/-0)
src/Canonical.UbuntuOne.Common/Validation/EqualityValidationExtensions.cs (+50/-0)
src/UbuntuOne.sln (+12/-0)
To merge this branch: bzr merge lp:~mandel/ubuntuone-windows-installer/add_ubuntu_sso_service_keyring
Reviewer Review Type Date Requested Status
Vincenzo Di Somma (community) Approve
Rick McBride (community) Approve
Review via email: mp+35276@code.launchpad.net

Description of the change

Adds the implementation of the keyring on windows so that we can securely store the Ubuntu SSO tokens.

To post a comment you must log in.
Revision history for this message
Rick McBride (rmcbride) wrote :
Download full text (16.7 KiB)

I get a whole lot of errors (pasting what I have from my terminal buffer) and the build fails during NAnt.exe tests:

  [msbuild] Copying file from "obj\Debug\ServiceTestApp.pdb" to "bin\Debug\Ser
viceTestApp.pdb".
  [msbuild] Done Building Project "C:\canonical\ubuntuone-windows-installer\revi
ew.aussk\src\ServiceTestApp\ServiceTestApp.csproj" (default targets).
  [msbuild] Project "C:\canonical\ubuntuone-windows-installer\review.aussk\src\U
buntuOne.sln" (1) is building "C:\canonical\ubuntuone-windows-installer\review.a
ussk\src\UbuntuOneClient\UbuntuOneClient.csproj" (13) on node 0 (default targets
).
  [msbuild] Processing 0 EDMX files.
  [msbuild] Finished processing 0 EDMX files.
  [msbuild] PrepareForBuild:
  [msbuild] Creating directory "bin\Debug\".
  [msbuild] Creating directory "obj\Debug\".
  [msbuild] CoreResGen:
  [msbuild] Processing resource file "Properties\Resources.resx" into "obj\Deb
ug\UbuntuOneClient.Properties.Resources.resources".
  [msbuild] _CopyFilesMarkedCopyLocal:
  [msbuild] Copying file from "..\..\lib\WPFContrib\AvalonLibrary.dll" to "bin
\Debug\AvalonLibrary.dll".
  [msbuild] Copying file from "C:\canonical\ubuntuone-windows-installer\review
.aussk\src\Canonical.UbuntuOne.Client\bin\Debug\Canonical.UbuntuOne.Client.dll"
to "bin\Debug\Canonical.UbuntuOne.Client.dll".
  [msbuild] Copying file from "C:\canonical\ubuntuone-windows-installer\review
.aussk\src\Canonical.UbuntuOne.Common\bin\Debug\Canonical.UbuntuOne.Common.dll"
to "bin\Debug\Canonical.UbuntuOne.Common.dll".
  [msbuild] Copying file from "..\..\lib\Spring.Net\Common.Logging.Log4Net.dll
" to "bin\Debug\Common.Logging.Log4Net.dll".
  [msbuild] Copying file from "..\..\lib\log4net\log4net.dll" to "bin\Debug\lo
g4net.dll".
  [msbuild] Copying file from "C:\canonical\ubuntuone-windows-installer\review
.aussk\src\Canonical.UbuntuOne.Common\bin\Debug\Spring.Aop.dll" to "bin\Debug\Sp
ring.Aop.dll".
  [msbuild] Copying file from "..\..\lib\Spring.Net\Common.Logging.dll" to "bi
n\Debug\Common.Logging.dll".
  [msbuild] Copying file from "C:\canonical\ubuntuone-windows-installer\review
.aussk\src\Canonical.UbuntuOne.Common\bin\Debug\DotUpdater.dll" to "bin\Debug\Do
tUpdater.dll".
  [msbuild] Copying file from "C:\canonical\ubuntuone-windows-installer\review
.aussk\src\Canonical.UbuntuOne.Common\bin\Debug\Spring.Core.dll" to "bin\Debug\S
pring.Core.dll".
  [msbuild] Copying file from "C:\canonical\ubuntuone-windows-installer\review
.aussk\src\Canonical.UbuntuOne.Common\bin\Debug\Spring.Aop.xml" to "bin\Debug\Sp
ring.Aop.xml".
  [msbuild] Copying file from "C:\canonical\ubuntuone-windows-installer\review
.aussk\src\Canonical.UbuntuOne.Common\bin\Debug\DotUpdater.pdb" to "bin\Debug\Do
tUpdater.pdb".
  [msbuild] Copying file from "C:\canonical\ubuntuone-windows-installer\review
.aussk\src\Canonical.UbuntuOne.Common\bin\Debug\Spring.Core.xml" to "bin\Debug\S
pring.Core.xml".
  [msbuild] Copying file from "C:\canonical\ubuntuone-windows-installer\review
.aussk\src\Canonical.UbuntuOne.Client\bin\Debug\Canonical.UbuntuOne.Client.pdb"
to "bin\Debug\Canonical.UbuntuOne.Client.pdb".
  [msbuild] Copying file from "C:\canonical\ubuntuo...

Revision history for this message
Rick McBride (rmcbride) wrote :

See above. Looks like something is wrong.

review: Needs Fixing
62. By Manuel de la Peña

Merged with trunk.

63. By Manuel de la Peña

Re-references the NUnit dll.

Revision history for this message
Rick McBride (rmcbride) wrote :

+1

review: Approve
Revision history for this message
Vincenzo Di Somma (vds) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2010-09-08 11:23:58 +0000
3+++ .bzrignore 2010-09-17 16:23:44 +0000
4@@ -8,6 +8,8 @@
5 src/_ReSharper.UbuntuOne
6 src/Canonical.Ubuntu.SSO/bin
7 src/Canonical.Ubuntu.SSO/obj
8+src/Canonical.Ubuntu.SSO.Tests/bin
9+src/Canonical.Ubuntu.SSO.Tests/obj
10 src/Canonical.UbuntuOne.Client/bin
11 src/Canonical.UbuntuOne.Client/obj
12 src/Canonical.UbuntuOne.Client.Test/obj
13
14=== modified file 'main.build'
15--- main.build 2010-09-02 10:27:49 +0000
16+++ main.build 2010-09-17 16:23:44 +0000
17@@ -156,7 +156,7 @@
18 program="nunit-console.exe"
19 commandline="Canonical.UbuntuOne.ProcessDispatcher.Tests.dll /xml=../../../../test-results/Canonical.UbuntuOne.ProcessDispatcher.Tests-Result.xml" />
20
21- <echo>Canonical.UbuntuOne.Client.Test</echo>
22+ <echo>Executing Canonical.UbuntuOne.Client.Test</echo>
23 <exec basedir="tools/NUnit"
24 managed="true"
25 workingdir="src/Canonical.UbuntuOne.Client.Test/bin/${enviroment}"
26@@ -169,12 +169,20 @@
27 <copy file="src/Canonical.UbuntuOne.Client.Views/bin/${enviroment}/AvalonLibrary.dll"
28 tofile="src/UbuntuOneClient.Tests/bin/${enviroment}/AvalonLibrary.dll" />
29
30- <echo>UbuntuOneClient.Tests</echo>
31+ <echo>Executing UbuntuOneClient.Tests</echo>
32 <exec basedir="tools/NUnit"
33 managed="true"
34 workingdir="src/UbuntuOneClient.Tests/bin/${enviroment}"
35 program="nunit-console.exe"
36 commandline="UbuntuOneClient.Tests.dll /xml=../../../../test-results/UbuntuOneClient.Tests-Result.xml" />
37+
38+ <echo>Executing Canonical.Ubuntu.SSO.Tests</echo>
39+ <exec basedir="tools/NUnit"
40+ managed="true"
41+ workingdir="src/Canonical.Ubuntu.SSO.Tests/bin/${enviroment}"
42+ program="nunit-console.exe"
43+ commandline="Canonical.Ubuntu.SSO.Tests.dll /xml=../../../../test-results/Canonical.Ubuntu.SSO.Tests-Result.xml" />
44+
45 </target>
46 <target name="package_python"
47 description="Creates the exe binary that embeds the python libs that will be used to perform the sync operation in the windows platform">
48
49=== added directory 'src/Canonical.Ubuntu.SSO.Tests'
50=== added file 'src/Canonical.Ubuntu.SSO.Tests/Canonical.Ubuntu.SSO.Tests.csproj'
51--- src/Canonical.Ubuntu.SSO.Tests/Canonical.Ubuntu.SSO.Tests.csproj 1970-01-01 00:00:00 +0000
52+++ src/Canonical.Ubuntu.SSO.Tests/Canonical.Ubuntu.SSO.Tests.csproj 2010-09-17 16:23:44 +0000
53@@ -0,0 +1,74 @@
54+<?xml version="1.0" encoding="utf-8"?>
55+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
56+ <PropertyGroup>
57+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
58+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
59+ <ProductVersion>9.0.21022</ProductVersion>
60+ <SchemaVersion>2.0</SchemaVersion>
61+ <ProjectGuid>{17BBBEC2-0F4F-48CE-A585-07AA33B6B2B3}</ProjectGuid>
62+ <OutputType>Library</OutputType>
63+ <AppDesignerFolder>Properties</AppDesignerFolder>
64+ <RootNamespace>Canonical.Ubuntu.SSO.Tests</RootNamespace>
65+ <AssemblyName>Canonical.Ubuntu.SSO.Tests</AssemblyName>
66+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
67+ <FileAlignment>512</FileAlignment>
68+ </PropertyGroup>
69+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
70+ <DebugSymbols>true</DebugSymbols>
71+ <DebugType>full</DebugType>
72+ <Optimize>false</Optimize>
73+ <OutputPath>bin\Debug\</OutputPath>
74+ <DefineConstants>DEBUG;TRACE</DefineConstants>
75+ <ErrorReport>prompt</ErrorReport>
76+ <WarningLevel>4</WarningLevel>
77+ </PropertyGroup>
78+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
79+ <DebugType>pdbonly</DebugType>
80+ <Optimize>true</Optimize>
81+ <OutputPath>bin\Release\</OutputPath>
82+ <DefineConstants>TRACE</DefineConstants>
83+ <ErrorReport>prompt</ErrorReport>
84+ <WarningLevel>4</WarningLevel>
85+ </PropertyGroup>
86+ <ItemGroup>
87+ <Reference Include="nunit.framework, Version=2.5.5.10112, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
88+ <SpecificVersion>False</SpecificVersion>
89+ <HintPath>..\..\lib\Nunit\nunit.framework.dll</HintPath>
90+ </Reference>
91+ <Reference Include="Rhino.Mocks, Version=3.6.0.0, Culture=neutral, PublicKeyToken=0b3305902db7183f, processorArchitecture=MSIL">
92+ <SpecificVersion>False</SpecificVersion>
93+ <HintPath>..\..\lib\RhinoMocks\Rhino.Mocks.dll</HintPath>
94+ </Reference>
95+ <Reference Include="System" />
96+ <Reference Include="System.Core">
97+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
98+ </Reference>
99+ <Reference Include="System.Security" />
100+ <Reference Include="System.Xml.Linq">
101+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
102+ </Reference>
103+ <Reference Include="System.Data.DataSetExtensions">
104+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
105+ </Reference>
106+ <Reference Include="System.Data" />
107+ <Reference Include="System.Xml" />
108+ </ItemGroup>
109+ <ItemGroup>
110+ <Compile Include="KeyringFixture.cs" />
111+ <Compile Include="Properties\AssemblyInfo.cs" />
112+ </ItemGroup>
113+ <ItemGroup>
114+ <ProjectReference Include="..\Canonical.Ubuntu.SSO\Canonical.Ubuntu.SSO.csproj">
115+ <Project>{9460A771-2589-45DA-9618-9FE8BB7D16E8}</Project>
116+ <Name>Canonical.Ubuntu.SSO</Name>
117+ </ProjectReference>
118+ </ItemGroup>
119+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
120+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
121+ Other similar extension points exist, see Microsoft.Common.targets.
122+ <Target Name="BeforeBuild">
123+ </Target>
124+ <Target Name="AfterBuild">
125+ </Target>
126+ -->
127+</Project>
128\ No newline at end of file
129
130=== added file 'src/Canonical.Ubuntu.SSO.Tests/KeyringFixture.cs'
131--- src/Canonical.Ubuntu.SSO.Tests/KeyringFixture.cs 1970-01-01 00:00:00 +0000
132+++ src/Canonical.Ubuntu.SSO.Tests/KeyringFixture.cs 2010-09-17 16:23:44 +0000
133@@ -0,0 +1,409 @@
134+/*
135+ * Copyright 2010 Canonical Ltd.
136+ *
137+ * This file is part of UbuntuOne on Windows.
138+ *
139+ * UbuntuOne on Windows is free software: you can redistribute it and/or modify
140+ * it under the terms of the GNU Lesser General Public License version
141+ * as published by the Free Software Foundation.
142+ *
143+ * Ubuntu One on Windows is distributed in the hope that it will be useful,
144+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
145+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
146+ * GNU Lesser General Public License for more details.
147+ *
148+ * You should have received a copy of the GNU Lesser General Public License
149+ * along with UbuntuOne for Windows. If not, see <http://www.gnu.org/licenses/>.
150+ *
151+ * Authors: Manuel de la Peña <manuel.delapena@canonical.com>
152+ */
153+using System.Linq;
154+using System.Security.Cryptography;
155+using System.Text;
156+using NUnit.Framework;
157+using Rhino.Mocks;
158+
159+namespace Canonical.Ubuntu.SSO.Tests
160+{
161+ [TestFixture]
162+ public class KeyringFixture
163+ {
164+ #region Variables
165+
166+ private MockRepository _mocks;
167+ private IRegistryKey _userRegistry;
168+ private IRegistryKey _keyringsRoot;
169+ private IRegistryKey _keyringKey;
170+ private IDataProtector _dataProtector;
171+ private Keyring _keyring;
172+
173+ #endregion
174+
175+ #region Setup & TearDown
176+
177+ [SetUp]
178+ public void Setup()
179+ {
180+ _mocks = new MockRepository();
181+ _userRegistry = _mocks.StrictMock<IRegistryKey>();
182+ _keyringsRoot = _mocks.StrictMock<IRegistryKey>();
183+ _keyringKey = _mocks.StrictMock<IRegistryKey>();
184+ _dataProtector = _mocks.StrictMock<IDataProtector>();
185+ _keyring = new Keyring(_userRegistry) { DataProtector = _dataProtector };
186+ }
187+
188+ #endregion
189+
190+ #region Tests
191+
192+ [Test]
193+ public void DisposeTest()
194+ {
195+ using (_mocks.Record())
196+ {
197+ Expect.Call(() => _userRegistry.Dispose())
198+ .Repeat.Once();
199+ }
200+ using (_mocks.Playback())
201+ {
202+ // we ensure that we always dispose the registry key to ensure no
203+ // memory leaks.
204+ _keyring.Dispose();
205+ }
206+ }
207+
208+ [TestCase("Default", "UbuntuOne", "myPassword")]
209+ [TestCase("Default", "Mandel", "Secret")]
210+ [TestCase("Test", "Leo", "MySecret")]
211+ public void CreateSecretNoRootPathPresentTest(string keyringName, string applicationName, string secret)
212+ {
213+ using (_mocks.Record())
214+ {
215+ using (_mocks.Ordered())
216+ {
217+ Expect.Call(_userRegistry.GetSubKeyNames())
218+ .Return(new[] {"Blah", "BlahBlah"})
219+ .Repeat.Twice();
220+
221+ Expect.Call(_userRegistry.CreateSubKey(Keyring.RootPath))
222+ .Return(_keyringsRoot)
223+ .Repeat.Once();
224+
225+ Expect.Call(_keyringsRoot.CreateSubKey(keyringName))
226+ .Return(_keyringKey)
227+ .Repeat.Once();
228+
229+ Expect.Call(() => _keyringKey.Dispose())
230+ .Repeat.Any();
231+
232+ Expect.Call(() => _keyringsRoot.Dispose())
233+ .Repeat.Any();
234+
235+ Expect.Call(_userRegistry.OpenSubKey(Keyring.RootPath))
236+ .Return(_keyringsRoot)
237+ .Repeat.Once();
238+
239+ Expect.Call(_keyringsRoot.OpenSubKey(keyringName))
240+ .Return(_keyringKey)
241+ .Repeat.Once();
242+
243+ Expect.Call(_dataProtector.Protect(secret,
244+ Encoding.UTF8.GetBytes(Keyring.Entropy), DataProtectionScope.CurrentUser))
245+ .Return(secret)
246+ .Repeat.Once();
247+
248+ Expect.Call(() => _keyringKey.SetValue(applicationName, secret))
249+ .Repeat.Once();
250+
251+ }
252+
253+ }
254+ using (_mocks.Playback())
255+ {
256+ _keyring.CreateSecret(keyringName, applicationName, secret);
257+ }
258+ }
259+
260+ [TestCase("Default", "UbuntuOne", "myPassword")]
261+ [TestCase("Default", "Mandel", "Secret")]
262+ [TestCase("Test", "Leo", "MySecret")]
263+ public void CreateSecretNoKeyringPresentTest(string keyringName, string applicationName, string secret)
264+ {
265+ using (_mocks.Record())
266+ {
267+ using(_mocks.Ordered())
268+ {
269+ Expect.Call(_userRegistry.GetSubKeyNames())
270+ .Return(new[] { "Blah", "BlahBlah", Keyring.RootPath})
271+ .Repeat.Once();
272+
273+ Expect.Call(() => _keyringsRoot.Dispose())
274+ .Repeat.Any();
275+
276+ Expect.Call(_userRegistry.OpenSubKey(Keyring.RootPath))
277+ .Return(_keyringsRoot)
278+ .Repeat.Once();
279+
280+ Expect.Call(_keyringsRoot.GetSubKeyNames())
281+ .Return(new string[] { })
282+ .Repeat.Once();
283+
284+ Expect.Call(_userRegistry.OpenSubKey(Keyring.RootPath))
285+ .Return(_keyringsRoot)
286+ .Repeat.Once();
287+
288+ Expect.Call(_keyringsRoot.CreateSubKey(keyringName))
289+ .Return(_keyringKey)
290+ .Repeat.Once();
291+
292+ Expect.Call(() => _keyringKey.Dispose())
293+ .Repeat.Any();
294+
295+ Expect.Call(_userRegistry.OpenSubKey(Keyring.RootPath))
296+ .Return(_keyringsRoot)
297+ .Repeat.Once();
298+
299+ Expect.Call(_keyringsRoot.OpenSubKey(keyringName))
300+ .Return(_keyringKey)
301+ .Repeat.Once();
302+
303+ Expect.Call(_dataProtector.Protect(secret,
304+ Encoding.UTF8.GetBytes(Keyring.Entropy), DataProtectionScope.CurrentUser))
305+ .Return(secret)
306+ .Repeat.Once();
307+
308+ Expect.Call(() => _keyringKey.SetValue(applicationName, secret))
309+ .Repeat.Once();
310+
311+ }
312+ }
313+ using (_mocks.Playback())
314+ {
315+ _keyring.CreateSecret(keyringName, applicationName, secret);
316+ }
317+ }
318+
319+ [TestCase("Default", "UbuntuOne", "myPassword")]
320+ [TestCase("Default", "Mandel", "Secret")]
321+ [TestCase("Test", "Leo", "MySecret")]
322+ public void CreateSecretPresentKeyringTest(string keyringName, string applicationName, string secret)
323+ {
324+ using(_mocks.Record())
325+ {
326+ using(_mocks.Ordered())
327+ {
328+ Expect.Call(_userRegistry.GetSubKeyNames())
329+ .Return(new[] { "Blah", "BlahBlah", Keyring.RootPath })
330+ .Repeat.Once();
331+
332+ Expect.Call(() => _keyringsRoot.Dispose())
333+ .Repeat.Any();
334+
335+ Expect.Call(_userRegistry.OpenSubKey(Keyring.RootPath))
336+ .Return(_keyringsRoot)
337+ .Repeat.Once();
338+
339+ Expect.Call(_keyringsRoot.GetSubKeyNames())
340+ .Return(new[] { keyringName})
341+ .Repeat.Once();
342+
343+ Expect.Call(() => _keyringKey.Dispose())
344+ .Repeat.Any();
345+
346+ Expect.Call(_userRegistry.OpenSubKey(Keyring.RootPath))
347+ .Return(_keyringsRoot)
348+ .Repeat.Once();
349+
350+ Expect.Call(_keyringsRoot.OpenSubKey(keyringName))
351+ .Return(_keyringKey)
352+ .Repeat.Once();
353+
354+ Expect.Call(_dataProtector.Protect(secret,
355+ Encoding.UTF8.GetBytes(Keyring.Entropy), DataProtectionScope.CurrentUser))
356+ .Return(secret)
357+ .Repeat.Once();
358+
359+ Expect.Call(() => _keyringKey.SetValue(applicationName, secret))
360+ .Repeat.Once();
361+ }
362+ }
363+ using(_mocks.Playback())
364+ {
365+ _keyring.CreateSecret(keyringName, applicationName, secret);
366+ }
367+ }
368+
369+ [TestCase("Default", "UbuntuOne")]
370+ [TestCase("Keyring", "AppNAme")]
371+ public void GetSecretByNameNotKeyringTest(string keyringName, string applicationName)
372+ {
373+ using(_mocks.Record())
374+ {
375+ Expect.Call(_userRegistry.GetSubKeyNames())
376+ .Return(new[] { "Blah", "BlahBlah" })
377+ .Repeat.Once();
378+ }
379+ using(_mocks.Playback())
380+ {
381+ var secret = _keyring.GetSecretByName(keyringName, applicationName);
382+ Assert.IsEmpty(secret);
383+ }
384+ }
385+
386+ [TestCase("Default", "UbuntuOne")]
387+ [TestCase("Keyring", "AppNAme")]
388+ public void GetSecretByNameKeyringExistsTest(string keyringName, string applicationName)
389+ {
390+ using (_mocks.Record())
391+ {
392+ using (_mocks.Ordered())
393+ {
394+ Expect.Call(_userRegistry.GetSubKeyNames())
395+ .Return(new[] { "Blah", "BlahBlah", Keyring.RootPath })
396+ .Repeat.Once();
397+
398+ Expect.Call(() => _keyringsRoot.Dispose())
399+ .Repeat.Any();
400+
401+ Expect.Call(_userRegistry.OpenSubKey(Keyring.RootPath))
402+ .Return(_keyringsRoot)
403+ .Repeat.Once();
404+
405+ Expect.Call(_keyringsRoot.GetSubKeyNames())
406+ .Return(new[] { keyringName })
407+ .Repeat.Once();
408+
409+ Expect.Call(() => _keyringKey.Dispose())
410+ .Repeat.Any();
411+
412+ Expect.Call(_userRegistry.OpenSubKey(Keyring.RootPath))
413+ .Return(_keyringsRoot)
414+ .Repeat.Once();
415+
416+ Expect.Call(_keyringsRoot.OpenSubKey(keyringName))
417+ .Return(_keyringKey)
418+ .Repeat.Once();
419+
420+ Expect.Call(_keyringKey.GetValue(applicationName))
421+ .Return(applicationName)
422+ .Repeat.Once();
423+
424+ Expect.Call(_dataProtector.Unprotect("",
425+ Encoding.UTF8.GetBytes(Keyring.Entropy), DataProtectionScope.CurrentUser))
426+ .IgnoreArguments()
427+ .Return(applicationName)
428+ .Repeat.Once();
429+
430+ }
431+ }
432+ using (_mocks.Playback())
433+ {
434+ var secret = _keyring.GetSecretByName(keyringName, applicationName);
435+ Assert.AreEqual(applicationName, secret);
436+ }
437+ }
438+
439+ [Test]
440+ public void GetKeyringsTest()
441+ {
442+ var keyrings = new[] {"Default", "Test", "Test1"};
443+
444+ using(_mocks.Record())
445+ {
446+ // make it such that the root is present
447+ Expect.Call(_userRegistry.GetSubKeyNames())
448+ .Return(new[] { "Blah", "BlahBlah", Keyring.RootPath })
449+ .Repeat.Once();
450+
451+ Expect.Call(() => _keyringsRoot.Dispose())
452+ .Repeat.Any();
453+
454+ Expect.Call(_userRegistry.OpenSubKey(Keyring.RootPath))
455+ .Return(_keyringsRoot)
456+ .Repeat.Once();
457+
458+ Expect.Call(_keyringsRoot.GetSubKeyNames())
459+ .Return(keyrings)
460+ .Repeat.Once();
461+
462+ }
463+ using(_mocks.Playback())
464+ {
465+ Assert.AreSame(_keyring.GetKeyrings(), keyrings);
466+ }
467+ }
468+
469+ [Test]
470+ public void GetKeyringsNotRootTest()
471+ {
472+ using (_mocks.Record())
473+ {
474+ // make it such that the root is present
475+ Expect.Call(_userRegistry.GetSubKeyNames())
476+ .Return(new[] { "Blah", "BlahBlah"})
477+ .Repeat.Once();
478+ }
479+ using (_mocks.Playback())
480+ {
481+ Assert.AreEqual(0, _keyring.GetKeyrings().Count());
482+ }
483+ }
484+
485+ [TestCase("Default")]
486+ [TestCase("Test")]
487+ public void GetApplicationsTest(string keyringName)
488+ {
489+ var applications = new[] {"Ubuntu One", "Test", "Default"};
490+ using(_mocks.Record())
491+ {
492+ Expect.Call(_userRegistry.GetSubKeyNames())
493+ .Return(new[] { "Blah", "BlahBlah", Keyring.RootPath })
494+ .Repeat.Once();
495+
496+ Expect.Call(() => _keyringsRoot.Dispose())
497+ .Repeat.Any();
498+
499+ Expect.Call(_userRegistry.OpenSubKey(Keyring.RootPath))
500+ .Return(_keyringsRoot)
501+ .Repeat.Twice();
502+
503+ Expect.Call(_keyringsRoot.GetSubKeyNames())
504+ .Return(new[] { keyringName })
505+ .Repeat.Once();
506+
507+ Expect.Call(_keyringsRoot.OpenSubKey(keyringName))
508+ .Return(_keyringKey)
509+ .Repeat.Once();
510+
511+ Expect.Call(_keyringKey.GetSubKeyNames())
512+ .Return(applications)
513+ .Repeat.Once();
514+
515+ Expect.Call(() => _keyringKey.Dispose())
516+ .Repeat.Any();
517+ }
518+ using(_mocks.Playback())
519+ {
520+ Assert.AreSame(applications, _keyring.GetApplications(keyringName));
521+ }
522+ }
523+
524+ [Test]
525+ public void GetApplicationNotKeyringTest()
526+ {
527+ using (_mocks.Record())
528+ {
529+ // make it such that the root is present
530+ Expect.Call(_userRegistry.GetSubKeyNames())
531+ .Return(new[] { "Blah", "BlahBlah" })
532+ .Repeat.Once();
533+ }
534+ using(_mocks.Playback())
535+ {
536+ Assert.AreEqual(0, _keyring.GetApplications("Test").Count());
537+ }
538+ }
539+
540+ #endregion
541+ }
542+}
543
544=== added directory 'src/Canonical.Ubuntu.SSO.Tests/Properties'
545=== added file 'src/Canonical.Ubuntu.SSO.Tests/Properties/AssemblyInfo.cs'
546--- src/Canonical.Ubuntu.SSO.Tests/Properties/AssemblyInfo.cs 1970-01-01 00:00:00 +0000
547+++ src/Canonical.Ubuntu.SSO.Tests/Properties/AssemblyInfo.cs 2010-09-17 16:23:44 +0000
548@@ -0,0 +1,36 @@
549+using System.Reflection;
550+using System.Runtime.CompilerServices;
551+using System.Runtime.InteropServices;
552+
553+// General Information about an assembly is controlled through the following
554+// set of attributes. Change these attribute values to modify the information
555+// associated with an assembly.
556+[assembly: AssemblyTitle("Canonical.Ubuntu.SSO.Tests")]
557+[assembly: AssemblyDescription("")]
558+[assembly: AssemblyConfiguration("")]
559+[assembly: AssemblyCompany("")]
560+[assembly: AssemblyProduct("Canonical.Ubuntu.SSO.Tests")]
561+[assembly: AssemblyCopyright("Copyright © 2010")]
562+[assembly: AssemblyTrademark("")]
563+[assembly: AssemblyCulture("")]
564+
565+// Setting ComVisible to false makes the types in this assembly not visible
566+// to COM components. If you need to access a type in this assembly from
567+// COM, set the ComVisible attribute to true on that type.
568+[assembly: ComVisible(false)]
569+
570+// The following GUID is for the ID of the typelib if this project is exposed to COM
571+[assembly: Guid("055a2726-2682-4018-8c48-e1795da59ab2")]
572+
573+// Version information for an assembly consists of the following four values:
574+//
575+// Major Version
576+// Minor Version
577+// Build Number
578+// Revision
579+//
580+// You can specify all the values or you can default the Build and Revision Numbers
581+// by using the '*' as shown below:
582+// [assembly: AssemblyVersion("1.0.*")]
583+[assembly: AssemblyVersion("1.0.0.0")]
584+[assembly: AssemblyFileVersion("1.0.0.0")]
585
586=== modified file 'src/Canonical.Ubuntu.SSO/Canonical.Ubuntu.SSO.csproj'
587--- src/Canonical.Ubuntu.SSO/Canonical.Ubuntu.SSO.csproj 2010-09-08 11:23:58 +0000
588+++ src/Canonical.Ubuntu.SSO/Canonical.Ubuntu.SSO.csproj 2010-09-17 16:23:44 +0000
589@@ -31,10 +31,15 @@
590 <WarningLevel>4</WarningLevel>
591 </PropertyGroup>
592 <ItemGroup>
593+ <Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
594+ <SpecificVersion>False</SpecificVersion>
595+ <HintPath>..\..\lib\log4net\log4net.dll</HintPath>
596+ </Reference>
597 <Reference Include="System" />
598 <Reference Include="System.Core">
599 <RequiredTargetFramework>3.5</RequiredTargetFramework>
600 </Reference>
601+ <Reference Include="System.Security" />
602 <Reference Include="System.Xml.Linq">
603 <RequiredTargetFramework>3.5</RequiredTargetFramework>
604 </Reference>
605@@ -45,17 +50,31 @@
606 <Reference Include="System.Xml" />
607 </ItemGroup>
608 <ItemGroup>
609+ <Compile Include="..\Version.cs">
610+ <Link>Properties\Version.cs</Link>
611+ </Compile>
612 <Compile Include="CredentialsDeniedEventArgs.cs" />
613 <Compile Include="CredentialsErrorEventArgs.cs" />
614 <Compile Include="CredentialsFoundEventArgs.cs" />
615+ <Compile Include="DPAPIDataProtector.cs" />
616+ <Compile Include="IDataProtector.cs" />
617 <Compile Include="ILoginOrRegisterView.cs" />
618 <Compile Include="ILoginView.cs" />
619+ <Compile Include="IRegistryKey.cs" />
620+ <Compile Include="Keyring.cs" />
621 <Compile Include="LoginCredentialsEventArgs.cs" />
622+ <Compile Include="RegistryKeyWrapper.cs" />
623 <Compile Include="SSOCredentialsProvider.cs" />
624 <Compile Include="IKeyring.cs" />
625 <Compile Include="ISSOCredentialsProvider.cs" />
626 <Compile Include="Properties\AssemblyInfo.cs" />
627 </ItemGroup>
628+ <ItemGroup>
629+ <ProjectReference Include="..\Canonical.UbuntuOne.Common\Canonical.UbuntuOne.Common.csproj">
630+ <Project>{11353FF8-8E5A-488E-9CB1-873DADD232B9}</Project>
631+ <Name>Canonical.UbuntuOne.Common</Name>
632+ </ProjectReference>
633+ </ItemGroup>
634 <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
635 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
636 Other similar extension points exist, see Microsoft.Common.targets.
637
638=== added file 'src/Canonical.Ubuntu.SSO/DPAPIDataProtector.cs'
639--- src/Canonical.Ubuntu.SSO/DPAPIDataProtector.cs 1970-01-01 00:00:00 +0000
640+++ src/Canonical.Ubuntu.SSO/DPAPIDataProtector.cs 2010-09-17 16:23:44 +0000
641@@ -0,0 +1,65 @@
642+/*
643+ * Copyright 2010 Canonical Ltd.
644+ *
645+ * This file is part of UbuntuOne on Windows.
646+ *
647+ * UbuntuOne on Windows is free software: you can redistribute it and/or modify
648+ * it under the terms of the GNU Lesser General Public License version
649+ * as published by the Free Software Foundation.
650+ *
651+ * Ubuntu One on Windows is distributed in the hope that it will be useful,
652+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
653+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
654+ * GNU Lesser General Public License for more details.
655+ *
656+ * You should have received a copy of the GNU Lesser General Public License
657+ * along with UbuntuOne for Windows. If not, see <http://www.gnu.org/licenses/>.
658+ *
659+ * Authors: Manuel de la Peña <manuel.delapena@canonical.com>
660+ */
661+
662+using System.Security.Cryptography;
663+using System.Text;
664+
665+namespace Canonical.Ubuntu.SSO
666+{
667+ /// <summary>
668+ /// Implementation of the IDataProtector that uses the DAPI api to perform the protection.
669+ /// </summary>
670+ public class DPAPIDataProtector : IDataProtector
671+ {
672+ #region Implementation of IDataProtector
673+
674+ /// <summary>
675+ /// Protects the userData parameter and returns a string.
676+ /// </summary>
677+ /// <param name="userData">A string containing data to protect. </param>
678+ /// <param name="optionalEntropy">An additional byte array used to encrypt the data. </param>
679+ /// <param name="scope">One of the DataProtectionScope values.</param>
680+ /// <returns>A string representing the encrypted data.</returns>
681+ public string Protect(string userData, byte[] optionalEntropy, DataProtectionScope scope)
682+ {
683+ var userDataBytes = Encoding.UTF8.GetBytes(userData);
684+ var encryptedBytes = ProtectedData.Protect(userDataBytes, optionalEntropy, scope);
685+ var enc = new UTF8Encoding(false);
686+ return enc.GetString(encryptedBytes);
687+ }
688+
689+ /// <summary>
690+ /// Unprotects the encryptedData parameter and returns a string.
691+ /// </summary>
692+ /// <param name="encryptedData">A string containing data encrypted using the Protect method.</param>
693+ /// <param name="optionalEntropy">An additional byte array used to encrypt the data.</param>
694+ /// <param name="scope">One of the DataProtectionScope values. </param>
695+ /// <returns>A string representing the unprotected data.</returns>
696+ public string Unprotect(string encryptedData, byte[] optionalEntropy, DataProtectionScope scope)
697+ {
698+ var encryptedDataBytes = Encoding.UTF8.GetBytes(encryptedData);
699+ var userDataBytes = ProtectedData.Unprotect(encryptedDataBytes, optionalEntropy, scope);
700+ var enc = new UTF8Encoding(false);
701+ return enc.GetString(userDataBytes);
702+ }
703+
704+ #endregion
705+ }
706+}
707
708=== added file 'src/Canonical.Ubuntu.SSO/IDataProtector.cs'
709--- src/Canonical.Ubuntu.SSO/IDataProtector.cs 1970-01-01 00:00:00 +0000
710+++ src/Canonical.Ubuntu.SSO/IDataProtector.cs 2010-09-17 16:23:44 +0000
711@@ -0,0 +1,48 @@
712+/*
713+ * Copyright 2010 Canonical Ltd.
714+ *
715+ * This file is part of UbuntuOne on Windows.
716+ *
717+ * UbuntuOne on Windows is free software: you can redistribute it and/or modify
718+ * it under the terms of the GNU Lesser General Public License version
719+ * as published by the Free Software Foundation.
720+ *
721+ * Ubuntu One on Windows is distributed in the hope that it will be useful,
722+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
723+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
724+ * GNU Lesser General Public License for more details.
725+ *
726+ * You should have received a copy of the GNU Lesser General Public License
727+ * along with UbuntuOne for Windows. If not, see <http://www.gnu.org/licenses/>.
728+ *
729+ * Authors: Manuel de la Peña <manuel.delapena@canonical.com>
730+ */
731+using System.Security.Cryptography;
732+
733+namespace Canonical.Ubuntu.SSO
734+{
735+ /// <summary>
736+ /// Interface to be implemented by those objects that are able to encryp and decrypt users
737+ /// data to protect it.
738+ /// </summary>
739+ public interface IDataProtector
740+ {
741+ /// <summary>
742+ /// Protects the userData parameter and returns a string.
743+ /// </summary>
744+ /// <param name="userData">A string containing data to protect. </param>
745+ /// <param name="optionalEntropy">An additional byte array used to encrypt the data. </param>
746+ /// <param name="scope">One of the DataProtectionScope values.</param>
747+ /// <returns>A string representing the encrypted data.</returns>
748+ string Protect(string userData, byte[] optionalEntropy, DataProtectionScope scope);
749+
750+ /// <summary>
751+ /// Unprotects the encryptedData parameter and returns a string.
752+ /// </summary>
753+ /// <param name="encryptedData">A string containing data encrypted using the Protect method.</param>
754+ /// <param name="optionalEntropy">An additional byte array used to encrypt the data.</param>
755+ /// <param name="scope">One of the DataProtectionScope values. </param>
756+ /// <returns>A string representing the unprotected data.</returns>
757+ string Unprotect(string encryptedData, byte[] optionalEntropy, DataProtectionScope scope);
758+ }
759+}
760
761=== modified file 'src/Canonical.Ubuntu.SSO/IKeyring.cs'
762--- src/Canonical.Ubuntu.SSO/IKeyring.cs 2010-09-09 09:11:07 +0000
763+++ src/Canonical.Ubuntu.SSO/IKeyring.cs 2010-09-17 16:23:44 +0000
764@@ -17,6 +17,7 @@
765 *
766 * Authors: Manuel de la Peña <manuel.delapena@canonical.com>
767 */
768+using System;
769 using System.Collections.Generic;
770
771 namespace Canonical.Ubuntu.SSO
772@@ -25,7 +26,7 @@
773 /// Interface to be implemented by a class that allows to store data in the registry of the
774 /// current user and makes the class behave like a keyring.
775 /// </summary>
776- public interface IKeyring
777+ public interface IKeyring : IDisposable
778 {
779 /// <summary>
780 /// Creates a new secret in the the keyring with the given name for a given applciation.
781@@ -36,11 +37,11 @@
782 void CreateSecret(string keyringName, string applicationName, string secret);
783
784 /// <summary>
785- ///
786+ /// Gets the secret froma keyring using the name of the application that stored it.
787 /// </summary>
788- /// <param name="keyringName"></param>
789- /// <param name="applicationName"></param>
790- /// <returns></returns>
791+ /// <param name="keyringName">The name of the keyring where the secret was stored.</param>
792+ /// <param name="applicationName">the name of the application that stored the secret.</param>
793+ /// <returns>An empty string if the secret does not exist, the secret otherwhise.</returns>
794 string GetSecretByName(string keyringName, string applicationName);
795
796 /// <summary>
797@@ -52,8 +53,8 @@
798 /// <summary>
799 /// Gets a list with all the applications in a keyring.
800 /// </summary>
801- /// <param name="keyring">The name of the keyring that is queried.</param>
802+ /// <param name="keyringName">The name of the keyring that is queried.</param>
803 /// <returns>An enumerable with all the applications in the keyring.</returns>
804- IEnumerable<string> GetApplications(string keyring);
805+ IEnumerable<string> GetApplications(string keyringName);
806 }
807 }
808
809=== added file 'src/Canonical.Ubuntu.SSO/IRegistryKey.cs'
810--- src/Canonical.Ubuntu.SSO/IRegistryKey.cs 1970-01-01 00:00:00 +0000
811+++ src/Canonical.Ubuntu.SSO/IRegistryKey.cs 2010-09-17 16:23:44 +0000
812@@ -0,0 +1,171 @@
813+/*
814+ * Copyright 2010 Canonical Ltd.
815+ *
816+ * This file is part of UbuntuOne on Windows.
817+ *
818+ * UbuntuOne on Windows is free software: you can redistribute it and/or modify
819+ * it under the terms of the GNU Lesser General Public License version
820+ * as published by the Free Software Foundation.
821+ *
822+ * Ubuntu One on Windows is distributed in the hope that it will be useful,
823+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
824+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
825+ * GNU Lesser General Public License for more details.
826+ *
827+ * You should have received a copy of the GNU Lesser General Public License
828+ * along with UbuntuOne for Windows. If not, see <http://www.gnu.org/licenses/>.
829+ *
830+ * Authors: Manuel de la Peña <manuel.delapena@canonical.com>
831+ */
832+
833+using System;
834+using Microsoft.Win32;
835+
836+namespace Canonical.Ubuntu.SSO
837+{
838+ /// <summary>
839+ /// Interface that represents a class that allows to perform the access to
840+ /// the registry keys of the machine.
841+ /// </summary>
842+ public interface IRegistryKey : IDisposable
843+ {
844+ #region Properties
845+
846+ /// <summary>
847+ /// Retrieves the name of the key.
848+ /// </summary>
849+ string Name { get; }
850+
851+ /// <summary>
852+ /// Retrieves the count of subkeys of the current key.
853+ /// </summary>
854+ int SubKeyCount { get; }
855+
856+ /// <summary>
857+ /// Retrieves the count of values in the key.
858+ /// </summary>
859+ int ValueCount { get; }
860+
861+ #endregion
862+
863+ #region Methods
864+
865+ /// <summary>
866+ /// Closes the key and flushes it to disk if its contents have been modified.
867+ /// </summary>
868+ void Close();
869+
870+ /// <summary>
871+ /// Creates a new subkey or opens an existing subkey for write access.
872+ /// The string subkey is not case-sensitive.
873+ /// </summary>
874+ /// <param name="name">The name or path of the subkey to create or open. </param>
875+ /// <returns>A sub key to work with.</returns>
876+ IRegistryKey CreateSubKey(string name);
877+
878+ /// <summary>
879+ /// Creates a new subkey or opens an existing subkey for write access, using the specified
880+ /// permission check option. The string subkey is not case-sensitive.
881+ /// </summary>
882+ /// <param name="subkey">The name or path of the subkey to create or open.</param>
883+ /// <param name="permissionCheck">One of the RegistryKeyPermissionCheck values that
884+ /// specifies whether the key is opened for read or read/write access.</param>
885+ /// <returns>A subkey to work with.</returns>
886+ IRegistryKey CreateSubKey(string subkey, RegistryKeyPermissionCheck permissionCheck);
887+
888+ /// <summary>
889+ /// Deletes the specified subkey. The string subkey is not case-sensitive.
890+ /// </summary>
891+ /// <param name="subkey">The name of the subkey to delete. </param>
892+ void DeleteSubKey(string subkey);
893+
894+ /// <summary>
895+ /// Deletes the specified value from this key.
896+ /// </summary>
897+ /// <param name="name">The name of the value to delete.</param>
898+ void DeleteValue(string name);
899+
900+ /// <summary>
901+ /// Writes all the attributes of the specified open registry
902+ /// key into the registry.
903+ /// </summary>
904+ void Flush();
905+
906+ /// <summary>
907+ /// Retrieves an array of strings that contains all the subkey names.
908+ /// </summary>
909+ /// <returns>An array of strings that contains the names of the
910+ /// subkeys for the current key.</returns>
911+ string[] GetSubKeyNames();
912+
913+ /// <summary>
914+ /// Retrieves the value associated with the specified name.
915+ /// Returns null if the name/value pair does not exist in the registry.
916+ /// </summary>
917+ /// <param name="name">The name of the value to retrieve. </param>
918+ /// <returns>The value associated with name, or null if name is not found.</returns>
919+ object GetValue(string name);
920+
921+ /// <summary>
922+ /// Retrieves the value associated with the specified name.
923+ /// If the name is not found, returns the default value that you provide.
924+ /// </summary>
925+ /// <param name="name">The name of the value to retrieve.</param>
926+ /// <param name="defaultValue">The value to return if name does not exist.</param>
927+ /// <returns>The value associated with name, with any embedded
928+ /// environment variables left unexpanded, or defaultValue if name is not found.</returns>
929+ object GetValue(string name, object defaultValue);
930+
931+ /// <summary>
932+ /// Retrieves the registry data type of the value associated with the specified name.
933+ /// </summary>
934+ /// <param name="name">The name of the value whose registry data type is to be retrieved. </param>
935+ /// <returns>A RegistryValueKind value representing the registry
936+ /// data type of the value associated with name.</returns>
937+ RegistryValueKind GetValueKind(string name);
938+
939+ /// <summary>
940+ /// Retrieves an array of strings that contains all the
941+ /// value names associated with this key.
942+ /// </summary>
943+ /// <returns>An array of strings that contains
944+ /// the value names for the current key.</returns>
945+ string[] GetValueNames();
946+
947+ /// <summary>
948+ /// Retrieves a subkey as read-only.
949+ /// </summary>
950+ /// <param name="name">The name or path of the subkey to open read-only. </param>
951+ /// <returns>The subkey requested, or null if the operation failed.</returns>
952+ IRegistryKey OpenSubKey(string name);
953+
954+ /// <summary>
955+ /// Retrieves the specified subkey for read or read/write access.
956+ /// </summary>
957+ /// <param name="name">The name or path of the subkey to create or open.</param>
958+ /// <param name="permissionCheck">One of the RegistryKeyPermissionCheck values
959+ /// that specifies whether the key is opened for read or read/write access.</param>
960+ /// <returns></returns>
961+ IRegistryKey OpenSubKey(string name, RegistryKeyPermissionCheck permissionCheck);
962+
963+ /// <summary>
964+ /// Sets the specified name/value pair.
965+ /// </summary>
966+ /// <param name="name">The name of the value to store. </param>
967+ /// <param name="value">The data to be stored.</param>
968+ void SetValue(string name, Object value);
969+
970+ /// <summary>
971+ /// Sets the value of a name/value pair in the registry key, using the specified registry data type.
972+ /// </summary>
973+ /// <param name="name">The name of the value to be stored. </param>
974+ /// <param name="value">The data to be stored. </param>
975+ /// <param name="valueKind">The registry data type to use when storing the data</param>
976+ void SetValue(string name, Object value, RegistryValueKind valueKind);
977+
978+ #endregion
979+
980+
981+
982+ }
983+}
984
985=== added file 'src/Canonical.Ubuntu.SSO/Keyring.cs'
986--- src/Canonical.Ubuntu.SSO/Keyring.cs 1970-01-01 00:00:00 +0000
987+++ src/Canonical.Ubuntu.SSO/Keyring.cs 2010-09-17 16:23:44 +0000
988@@ -0,0 +1,282 @@
989+/*
990+ * Copyright 2010 Canonical Ltd.
991+ *
992+ * This file is part of UbuntuOne on Windows.
993+ *
994+ * UbuntuOne on Windows is free software: you can redistribute it and/or modify
995+ * it under the terms of the GNU Lesser General Public License version
996+ * as published by the Free Software Foundation.
997+ *
998+ * Ubuntu One on Windows is distributed in the hope that it will be useful,
999+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1000+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1001+ * GNU Lesser General Public License for more details.
1002+ *
1003+ * You should have received a copy of the GNU Lesser General Public License
1004+ * along with UbuntuOne for Windows. If not, see <http://www.gnu.org/licenses/>.
1005+ *
1006+ * Authors: Manuel de la Peña <manuel.delapena@canonical.com>
1007+ */
1008+using System.Collections.Generic;
1009+using System.Linq;
1010+using System.Security.Cryptography;
1011+using System.Text;
1012+using Canonical.UbuntuOne.Common.Validation;
1013+using log4net;
1014+
1015+namespace Canonical.Ubuntu.SSO
1016+{
1017+ /// <summary>
1018+ /// Implementation of the IKeyring interface that will store encripted secrets in the users current keyring.
1019+ /// </summary>
1020+ public class Keyring : IKeyring
1021+ {
1022+ #region Variables
1023+
1024+ internal const string RootPath = "Canonical\\Keyrings";
1025+ internal const string Entropy = "bdc97980-bbfa-11df-851a-0800200c9a66";
1026+ private const int KeyringMaxNameLength = 255;
1027+ private ILog _logger;
1028+ private readonly object _loggerLock = new object();
1029+ private bool _keyringsRootExists = false;
1030+
1031+ #endregion
1032+
1033+ #region DI properties
1034+
1035+ /// <summary>
1036+ /// Gets and sets the users registry key.
1037+ /// </summary>
1038+ public IRegistryKey UserRegistry { get; set; }
1039+
1040+ /// <summary>
1041+ /// Gets and sets the data protector to secure the data in the registry.
1042+ /// </summary>
1043+ public IDataProtector DataProtector { get; set; }
1044+
1045+ #endregion
1046+
1047+ #region Properties
1048+
1049+ /// <summary>
1050+ /// Gets and sets the logger that will be used to log the operation of the
1051+ /// class.
1052+ /// </summary>
1053+ public ILog Logger
1054+ {
1055+ get
1056+ {
1057+ if (_logger == null)
1058+ {
1059+ lock (_loggerLock)
1060+ {
1061+ _logger = LogManager.GetLogger(typeof(Keyring));
1062+ }
1063+ }
1064+ return _logger;
1065+ }
1066+ set
1067+ {
1068+ _logger = value;
1069+ }
1070+ }
1071+
1072+ #endregion
1073+
1074+ #region Constructors
1075+
1076+ /// <summary>
1077+ /// Creates a new instance of the class that will be using the users registry to store the data.
1078+ /// </summary>
1079+ public Keyring()
1080+ : this(new RegistryKeyWrapper())
1081+ {
1082+
1083+ }
1084+
1085+ /// <summary>
1086+ /// Creates a new instance of the class that will be storing the keyring in the provided registry key.
1087+ /// </summary>
1088+ /// <param name="key"></param>
1089+ public Keyring(IRegistryKey key)
1090+ {
1091+ UserRegistry = key;
1092+ }
1093+
1094+ #endregion
1095+
1096+ #region Helpers
1097+
1098+ private bool KeyringsRootExists()
1099+ {
1100+ if (!_keyringsRootExists)
1101+ {
1102+ var rootKey = from key in UserRegistry.GetSubKeyNames()
1103+ where key == RootPath
1104+ select key;
1105+ _keyringsRootExists = rootKey.Count() == 1;
1106+ }
1107+ return _keyringsRootExists;
1108+ }
1109+
1110+ private bool KeyringExist(string keyring)
1111+ {
1112+ // open the root keyring and then the subkeyring to ensure that the
1113+ // length of the string is not over 255 chars
1114+ if (!KeyringsRootExists())
1115+ return false;
1116+
1117+ using (var rootKeyring = UserRegistry.OpenSubKey(RootPath))
1118+ {
1119+ var presentKeyrings = from key in rootKeyring.GetSubKeyNames()
1120+ where key == keyring
1121+ select key;
1122+ return presentKeyrings.Count() == 1;
1123+ }
1124+ }
1125+
1126+ private IRegistryKey CreateKeyringRootPath()
1127+ {
1128+ // create a key with the root path to be used
1129+ return UserRegistry.CreateSubKey(RootPath);
1130+ }
1131+
1132+ private void CreateKeyring(string keyring)
1133+ {
1134+ using(var rootKey = (KeyringsRootExists())?
1135+ UserRegistry.OpenSubKey(RootPath) : CreateKeyringRootPath())
1136+ {
1137+ var subkey = rootKey.CreateSubKey(keyring);
1138+ subkey.Dispose();
1139+ }
1140+ }
1141+
1142+ private IRegistryKey OpenKeyring(string keyring)
1143+ {
1144+ using(var rootKey = UserRegistry.OpenSubKey(RootPath))
1145+ {
1146+ return rootKey.OpenSubKey(keyring);
1147+ }
1148+ }
1149+
1150+ private void SaveValue(string keyringName, string applicationName, string secret)
1151+ {
1152+ using(var keyring = OpenKeyring(keyringName))
1153+ {
1154+ var encryptedSecret = DataProtector.Protect(secret, Encoding.UTF8.GetBytes(Entropy),
1155+ DataProtectionScope.CurrentUser);
1156+ keyring.SetValue(applicationName, encryptedSecret);
1157+ }
1158+ }
1159+
1160+ private string GetValue(string keyringName, string applicationName)
1161+ {
1162+ using (var keyring = OpenKeyring(keyringName))
1163+ {
1164+ var secret = keyring.GetValue(applicationName) as string;
1165+ return DataProtector.Unprotect(secret, Encoding.UTF8.GetBytes(Entropy), DataProtectionScope.CurrentUser);
1166+ }
1167+ }
1168+
1169+
1170+ #endregion
1171+
1172+ #region Implementation of IKeyring
1173+
1174+ /// <summary>
1175+ /// Creates a new secret in the the keyring with the given name for a given applciation.
1176+ /// </summary>
1177+ /// <param name="keyringName">The name of the keyring where the secret will be stored.</param>
1178+ /// <param name="applicationName">The name of the application whose secret will be stored.</param>
1179+ /// <param name="secret">The secret to store in the keyring.</param>
1180+ public void CreateSecret(string keyringName, string applicationName, string secret)
1181+ {
1182+ ValidateArgs.Begin()
1183+ .IsNotNullOrEmpty(keyringName, "keyringName")
1184+ .IsShorterThan(keyringName, KeyringMaxNameLength, "keyringName")
1185+ .IsNotNullOrEmpty(applicationName, "applicationName")
1186+ .IsNotNullOrEmpty(secret, "secret")
1187+ .Check();
1188+
1189+ if (!KeyringExist(keyringName))
1190+ {
1191+ CreateKeyring(keyringName);
1192+ Logger.InfoFormat("Creating keyring with name {0}", keyringName);
1193+ }
1194+ SaveValue(keyringName, applicationName, secret);
1195+ }
1196+
1197+ /// <summary>
1198+ /// Gets the secret froma keyring using the name of the application that stored it.
1199+ /// </summary>
1200+ /// <param name="keyringName">The name of the keyring where the secret was stored.</param>
1201+ /// <param name="applicationName">the name of the application that stored the secret.</param>
1202+ /// <returns>An empty string if the secret does not exist, the secret otherwhise.</returns>
1203+ public string GetSecretByName(string keyringName, string applicationName)
1204+ {
1205+ ValidateArgs.Begin()
1206+ .IsNotNullOrEmpty(keyringName, "keyringName")
1207+ .IsShorterThan(keyringName, KeyringMaxNameLength, "keyringName")
1208+ .IsNotNullOrEmpty(applicationName, "applicationName")
1209+ .Check();
1210+
1211+ return KeyringExist(keyringName) ? GetValue(keyringName, applicationName) : string.Empty;
1212+ }
1213+
1214+ /// <summary>
1215+ /// Gets a lists with all the names of the available keyrings.
1216+ /// </summary>
1217+ /// <returns>An enumerable with all the names of the different keyrings.</returns>
1218+ public IEnumerable<string> GetKeyrings()
1219+ {
1220+ if(KeyringsRootExists())
1221+ {
1222+ using(var keyringRoot = UserRegistry.OpenSubKey(RootPath))
1223+ {
1224+ return keyringRoot.GetSubKeyNames();
1225+ }
1226+ }
1227+ return new string[0];
1228+ }
1229+
1230+ /// <summary>
1231+ /// Gets a list with all the applications in a keyring.
1232+ /// </summary>
1233+ /// <param name="keyringName">The name of the keyring that is queried.</param>
1234+ /// <returns>An enumerable with all the applications in the keyring.</returns>
1235+ public IEnumerable<string> GetApplications(string keyringName)
1236+ {
1237+ ValidateArgs.Begin()
1238+ .IsNotNullOrEmpty(keyringName, "keyring")
1239+ .IsShorterThan(keyringName, KeyringMaxNameLength, "keyring")
1240+ .Check();
1241+ if(KeyringExist(keyringName))
1242+ {
1243+ using(var keyringRoot = UserRegistry.OpenSubKey(RootPath))
1244+ {
1245+ using(var keyring = keyringRoot.OpenSubKey(keyringName))
1246+ {
1247+ return keyring.GetSubKeyNames();
1248+ }
1249+ }
1250+ }
1251+ return new string[0];
1252+ }
1253+
1254+ #endregion
1255+
1256+ #region Implementation of IDisposable
1257+
1258+ /// <summary>
1259+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
1260+ /// </summary>
1261+ /// <filterpriority>2</filterpriority>
1262+ public void Dispose()
1263+ {
1264+ if (UserRegistry != null)
1265+ UserRegistry.Dispose();
1266+ }
1267+
1268+ #endregion
1269+ }
1270+}
1271
1272=== modified file 'src/Canonical.Ubuntu.SSO/Properties/AssemblyInfo.cs'
1273--- src/Canonical.Ubuntu.SSO/Properties/AssemblyInfo.cs 2010-09-08 11:23:58 +0000
1274+++ src/Canonical.Ubuntu.SSO/Properties/AssemblyInfo.cs 2010-09-17 16:23:44 +0000
1275@@ -32,5 +32,4 @@
1276 // You can specify all the values or you can default the Build and Revision Numbers
1277 // by using the '*' as shown below:
1278 // [assembly: AssemblyVersion("1.0.*")]
1279-[assembly: AssemblyVersion("1.0.0.0")]
1280-[assembly: AssemblyFileVersion("1.0.0.0")]
1281+[assembly: InternalsVisibleTo("Canonical.Ubuntu.SSO.Tests")]
1282
1283=== added file 'src/Canonical.Ubuntu.SSO/RegistryKeyWrapper.cs'
1284--- src/Canonical.Ubuntu.SSO/RegistryKeyWrapper.cs 1970-01-01 00:00:00 +0000
1285+++ src/Canonical.Ubuntu.SSO/RegistryKeyWrapper.cs 2010-09-17 16:23:44 +0000
1286@@ -0,0 +1,261 @@
1287+/*
1288+ * Copyright 2010 Canonical Ltd.
1289+ *
1290+ * This file is part of UbuntuOne on Windows.
1291+ *
1292+ * UbuntuOne on Windows is free software: you can redistribute it and/or modify
1293+ * it under the terms of the GNU Lesser General Public License version
1294+ * as published by the Free Software Foundation.
1295+ *
1296+ * Ubuntu One on Windows is distributed in the hope that it will be useful,
1297+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1298+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1299+ * GNU Lesser General Public License for more details.
1300+ *
1301+ * You should have received a copy of the GNU Lesser General Public License
1302+ * along with UbuntuOne for Windows. If not, see <http://www.gnu.org/licenses/>.
1303+ *
1304+ * Authors: Manuel de la Peña <manuel.delapena@canonical.com>
1305+ */
1306+
1307+using System;
1308+using Microsoft.Win32;
1309+
1310+namespace Canonical.Ubuntu.SSO
1311+{
1312+ /// <summary>
1313+ /// Simple wrapper around the registry class that allows the class to be mocked for testing
1314+ /// purposes
1315+ /// </summary>
1316+ public class RegistryKeyWrapper : IRegistryKey
1317+ {
1318+ #region Variables
1319+
1320+ private readonly RegistryKey _key;
1321+
1322+ #endregion
1323+
1324+ #region Constructors
1325+
1326+ /// <summary>
1327+ /// Creates a new registry key that contains information about the default user configuration.
1328+ /// </summary>
1329+ public RegistryKeyWrapper()
1330+ : this(Registry.Users)
1331+ {
1332+
1333+ }
1334+
1335+ /// <summary>
1336+ /// Creates a new registry key wrapper with the given key.
1337+ /// </summary>
1338+ /// <param name="key">The key to be wrapped.</param>
1339+ public RegistryKeyWrapper(RegistryKey key)
1340+ {
1341+ _key = key;
1342+ }
1343+
1344+ #endregion
1345+
1346+ #region Implementation of IDisposable
1347+
1348+ /// <summary>
1349+ /// Performs application-defined tasks associated with freeing, releasing,
1350+ /// or resetting unmanaged resources.
1351+ /// </summary>
1352+ /// <filterpriority>2</filterpriority>
1353+ public void Dispose()
1354+ {
1355+ var disposable = (IDisposable)_key;
1356+ disposable.Dispose();
1357+ }
1358+
1359+ #endregion
1360+
1361+ #region Implementation of IRegistryKey
1362+
1363+ /// <summary>
1364+ /// Retrieves the name of the key.
1365+ /// </summary>
1366+ public string Name
1367+ {
1368+ get { return _key.Name; }
1369+ }
1370+
1371+ /// <summary>
1372+ /// Retrieves the count of subkeys of the current key.
1373+ /// </summary>
1374+ public int SubKeyCount
1375+ {
1376+ get { return _key.SubKeyCount; }
1377+ }
1378+
1379+ /// <summary>
1380+ /// Retrieves the count of values in the key.
1381+ /// </summary>
1382+ public int ValueCount
1383+ {
1384+ get { return _key.ValueCount; }
1385+ }
1386+
1387+ /// <summary>
1388+ /// Closes the key and flushes it to disk if its contents have been modified.
1389+ /// </summary>
1390+ public void Close()
1391+ {
1392+ _key.Close();
1393+ }
1394+
1395+ /// <summary>
1396+ /// Creates a new subkey or opens an existing subkey for write access.
1397+ /// The string subkey is not case-sensitive.
1398+ /// </summary>
1399+ /// <param name="name">The name or path of the subkey to create or open. </param>
1400+ /// <returns>A sub key to work with.</returns>
1401+ public IRegistryKey CreateSubKey(string name)
1402+ {
1403+ return new RegistryKeyWrapper(_key.CreateSubKey(name));
1404+ }
1405+
1406+ /// <summary>
1407+ /// Creates a new subkey or opens an existing subkey for write access, using the specified
1408+ /// permission check option. The string subkey is not case-sensitive.
1409+ /// </summary>
1410+ /// <param name="subkey">The name or path of the subkey to create or open.</param>
1411+ /// <param name="permissionCheck">One of the RegistryKeyPermissionCheck values that
1412+ /// specifies whether the key is opened for read or read/write access.</param>
1413+ /// <returns>A subkey to work with.</returns>
1414+ public IRegistryKey CreateSubKey(string subkey, RegistryKeyPermissionCheck permissionCheck)
1415+ {
1416+ return new RegistryKeyWrapper(_key.CreateSubKey(subkey, permissionCheck));
1417+ }
1418+
1419+ /// <summary>
1420+ /// Deletes the specified subkey. The string subkey is not case-sensitive.
1421+ /// </summary>
1422+ /// <param name="subkey">The name of the subkey to delete. </param>
1423+ public void DeleteSubKey(string subkey)
1424+ {
1425+ _key.DeleteSubKey(subkey);
1426+ }
1427+
1428+ /// <summary>
1429+ /// Deletes the specified value from this key.
1430+ /// </summary>
1431+ /// <param name="name">The name of the value to delete.</param>
1432+ public void DeleteValue(string name)
1433+ {
1434+ _key.DeleteValue(name);
1435+ }
1436+
1437+ /// <summary>
1438+ /// Writes all the attributes of the specified open registry
1439+ /// key into the registry.
1440+ /// </summary>
1441+ public void Flush()
1442+ {
1443+ _key.Flush();
1444+ }
1445+
1446+ /// <summary>
1447+ /// Retrieves an array of strings that contains all the subkey names.
1448+ /// </summary>
1449+ /// <returns>An array of strings that contains the names of the
1450+ /// subkeys for the current key.</returns>
1451+ public string[] GetSubKeyNames()
1452+ {
1453+ return _key.GetSubKeyNames();
1454+ }
1455+
1456+ /// <summary>
1457+ /// Retrieves the value associated with the specified name.
1458+ /// Returns null if the name/value pair does not exist in the registry.
1459+ /// </summary>
1460+ /// <param name="name">The name of the value to retrieve. </param>
1461+ /// <returns>The value associated with name, or null if name is not found.</returns>
1462+ public object GetValue(string name)
1463+ {
1464+ return _key.GetValue(name);
1465+ }
1466+
1467+ /// <summary>
1468+ /// Retrieves the value associated with the specified name.
1469+ /// If the name is not found, returns the default value that you provide.
1470+ /// </summary>
1471+ /// <param name="name">The name of the value to retrieve.</param>
1472+ /// <param name="defaultValue">The value to return if name does not exist.</param>
1473+ /// <returns>The value associated with name, with any embedded
1474+ /// environment variables left unexpanded, or defaultValue if name is not found.</returns>
1475+ public object GetValue(string name, object defaultValue)
1476+ {
1477+ return _key.GetValue(name, defaultValue);
1478+ }
1479+
1480+ /// <summary>
1481+ /// Retrieves the registry data type of the value associated with the specified name.
1482+ /// </summary>
1483+ /// <param name="name">The name of the value whose registry data type is to be retrieved. </param>
1484+ /// <returns>A RegistryValueKind value representing the registry
1485+ /// data type of the value associated with name.</returns>
1486+ public RegistryValueKind GetValueKind(string name)
1487+ {
1488+ return _key.GetValueKind(name);
1489+ }
1490+
1491+ /// <summary>
1492+ /// Retrieves an array of strings that contains all the
1493+ /// value names associated with this key.
1494+ /// </summary>
1495+ /// <returns>An array of strings that contains
1496+ /// the value names for the current key.</returns>
1497+ public string[] GetValueNames()
1498+ {
1499+ return _key.GetValueNames();
1500+ }
1501+
1502+ /// <summary>
1503+ /// Retrieves a subkey as read-only.
1504+ /// </summary>
1505+ /// <param name="name">The name or path of the subkey to open read-only. </param>
1506+ /// <returns>The subkey requested, or null if the operation failed.</returns>
1507+ public IRegistryKey OpenSubKey(string name)
1508+ {
1509+ return new RegistryKeyWrapper(_key.OpenSubKey(name));
1510+ }
1511+
1512+ /// <summary>
1513+ /// Retrieves the specified subkey for read or read/write access.
1514+ /// </summary>
1515+ /// <param name="name">The name or path of the subkey to create or open.</param>
1516+ /// <param name="permissionCheck">One of the RegistryKeyPermissionCheck values
1517+ /// that specifies whether the key is opened for read or read/write access.</param>
1518+ /// <returns></returns>
1519+ public IRegistryKey OpenSubKey(string name, RegistryKeyPermissionCheck permissionCheck)
1520+ {
1521+ return new RegistryKeyWrapper(_key.OpenSubKey(name, permissionCheck));
1522+ }
1523+
1524+ /// <summary>
1525+ /// Sets the specified name/value pair.
1526+ /// </summary>
1527+ /// <param name="name">The name of the value to store. </param>
1528+ /// <param name="value">The data to be stored.</param>
1529+ public void SetValue(string name, object value)
1530+ {
1531+ _key.SetValue(name, value);
1532+ }
1533+
1534+ /// <summary>
1535+ /// Sets the value of a name/value pair in the registry key, using the specified registry data type.
1536+ /// </summary>
1537+ /// <param name="name">The name of the value to be stored. </param>
1538+ /// <param name="value">The data to be stored. </param>
1539+ /// <param name="valueKind">The registry data type to use when storing the data</param>
1540+ public void SetValue(string name, object value, RegistryValueKind valueKind)
1541+ {
1542+ _key.SetValue(name, value, valueKind);
1543+ }
1544+
1545+ #endregion
1546+ }
1547+}
1548
1549=== modified file 'src/Canonical.UbuntuOne.Common.Tests/Validation/EqualityExtensionsValidation.cs'
1550--- src/Canonical.UbuntuOne.Common.Tests/Validation/EqualityExtensionsValidation.cs 2010-07-23 15:50:15 +0000
1551+++ src/Canonical.UbuntuOne.Common.Tests/Validation/EqualityExtensionsValidation.cs 2010-09-17 16:23:44 +0000
1552@@ -236,6 +236,44 @@
1553 .Check();
1554 }
1555
1556+ [TestCase("Robert", 255)]
1557+ [TestCase("true", 5)]
1558+ public void IsShorterThanTest(string value, int length)
1559+ {
1560+ ValidateArgs.Begin()
1561+ .IsShorterThan(value, length, "")
1562+ .Check();
1563+ }
1564+
1565+
1566+ [TestCase("Robert", 2)]
1567+ [TestCase("true", 1)]
1568+ [ExpectedException(typeof(ValidationException))]
1569+ public void IsShorterThanFailTest(string value, int length)
1570+ {
1571+ ValidateArgs.Begin()
1572+ .IsShorterThan(value, length, "")
1573+ .Check();
1574+ }
1575+
1576+ [TestCase("true", 4)]
1577+ [TestCase("Robert", 255)]
1578+ public void IsShorterOrEqualToTest(string value, int length)
1579+ {
1580+ ValidateArgs.Begin()
1581+ .IsShorterOrEqualTo(value, length, "")
1582+ .Check();
1583+ }
1584+
1585+ [TestCase("Robert", 2)]
1586+ [TestCase("true", 1)]
1587+ [ExpectedException(typeof(ValidationException))]
1588+ public void IsShorterOrEqualToFailTest(string value, int length)
1589+ {
1590+ ValidateArgs.Begin()
1591+ .IsShorterOrEqualTo(value, length, "")
1592+ .Check();
1593+ }
1594 #endregion
1595
1596 }
1597
1598=== modified file 'src/Canonical.UbuntuOne.Common/Validation/EqualityValidationExtensions.cs'
1599--- src/Canonical.UbuntuOne.Common/Validation/EqualityValidationExtensions.cs 2010-08-11 08:05:19 +0000
1600+++ src/Canonical.UbuntuOne.Common/Validation/EqualityValidationExtensions.cs 2010-09-17 16:23:44 +0000
1601@@ -242,5 +242,55 @@
1602 (argumentValidation ?? new ArgumentValidation()).AddException(new ArgumentException(
1603 string.Format("'{0}' cannot be null or empty.", parameterName)));
1604 }
1605+
1606+ /// <summary>
1607+ /// Ensures that the string that was passed as argument is not too long.
1608+ /// </summary>
1609+ /// <param name="argumentValidation">
1610+ /// A <see cref="ArgumentValidation"/> with the current result of the validations.
1611+ /// </param>
1612+ /// <param name="value">
1613+ /// A <see cref="System.String"/> with the argument to validate.
1614+ /// </param>
1615+ /// <param name="length">
1616+ /// An int with the length not to be excided by the string.
1617+ /// </param>
1618+ /// <param name="parameterName">The name of the parameter under test.</param>
1619+ /// <returns>
1620+ /// A <see cref="ArgumentValidation"/> the result of the validation.
1621+ /// </returns>
1622+ public static ArgumentValidation IsShorterThan(this ArgumentValidation argumentValidation, string value,
1623+ int length, string parameterName)
1624+ {
1625+ return (value.Length < length)
1626+ ? argumentValidation :
1627+ (argumentValidation ?? new ArgumentValidation()).AddException(new ArgumentException(
1628+ string.Format("'{0}' has to be smaller than {1} chars.",parameterName, length)));
1629+ }
1630+
1631+ /// <summary>
1632+ /// Ensures that the string that was passed as argument is not too long.
1633+ /// </summary>
1634+ /// <param name="argumentValidation">
1635+ /// A <see cref="ArgumentValidation"/> with the current result of the validations.
1636+ /// </param>
1637+ /// <param name="value">
1638+ /// A <see cref="System.String"/> with the argument to validate.
1639+ /// </param>
1640+ /// <param name="length">
1641+ /// An int with the length not to be excided by the string.
1642+ /// </param>
1643+ /// <param name="parameterName">The name of the parameter under test.</param>
1644+ /// <returns>
1645+ /// A <see cref="ArgumentValidation"/> the result of the validation.
1646+ /// </returns>
1647+ public static ArgumentValidation IsShorterOrEqualTo(this ArgumentValidation argumentValidation, string value,
1648+ int length, string parameterName)
1649+ {
1650+ return (value.Length <= length)
1651+ ? argumentValidation :
1652+ (argumentValidation ?? new ArgumentValidation()).AddException(new ArgumentException(
1653+ string.Format("'{0}' has to be smaller or equalt to {1} chars.", parameterName, length)));
1654+ }
1655 }
1656 }
1657
1658=== modified file 'src/UbuntuOne.sln'
1659--- src/UbuntuOne.sln 2010-09-08 11:23:58 +0000
1660+++ src/UbuntuOne.sln 2010-09-17 16:23:44 +0000
1661@@ -25,6 +25,8 @@
1662 EndProject
1663 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Canonical.Ubuntu.SSO", "Canonical.Ubuntu.SSO\Canonical.Ubuntu.SSO.csproj", "{9460A771-2589-45DA-9618-9FE8BB7D16E8}"
1664 EndProject
1665+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Canonical.Ubuntu.SSO.Tests", "Canonical.Ubuntu.SSO.Tests\Canonical.Ubuntu.SSO.Tests.csproj", "{17BBBEC2-0F4F-48CE-A585-07AA33B6B2B3}"
1666+EndProject
1667 Global
1668 GlobalSection(SolutionConfigurationPlatforms) = preSolution
1669 Debug|Any CPU = Debug|Any CPU
1670@@ -155,6 +157,16 @@
1671 {9460A771-2589-45DA-9618-9FE8BB7D16E8}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
1672 {9460A771-2589-45DA-9618-9FE8BB7D16E8}.Release|Mixed Platforms.Build.0 = Release|Any CPU
1673 {9460A771-2589-45DA-9618-9FE8BB7D16E8}.Release|x86.ActiveCfg = Release|Any CPU
1674+ {17BBBEC2-0F4F-48CE-A585-07AA33B6B2B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1675+ {17BBBEC2-0F4F-48CE-A585-07AA33B6B2B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
1676+ {17BBBEC2-0F4F-48CE-A585-07AA33B6B2B3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
1677+ {17BBBEC2-0F4F-48CE-A585-07AA33B6B2B3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
1678+ {17BBBEC2-0F4F-48CE-A585-07AA33B6B2B3}.Debug|x86.ActiveCfg = Debug|Any CPU
1679+ {17BBBEC2-0F4F-48CE-A585-07AA33B6B2B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
1680+ {17BBBEC2-0F4F-48CE-A585-07AA33B6B2B3}.Release|Any CPU.Build.0 = Release|Any CPU
1681+ {17BBBEC2-0F4F-48CE-A585-07AA33B6B2B3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
1682+ {17BBBEC2-0F4F-48CE-A585-07AA33B6B2B3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
1683+ {17BBBEC2-0F4F-48CE-A585-07AA33B6B2B3}.Release|x86.ActiveCfg = Release|Any CPU
1684 EndGlobalSection
1685 GlobalSection(SolutionProperties) = preSolution
1686 HideSolutionNode = FALSE

Subscribers

People subscribed via source and target branches

to all changes: