Proper way to hide API keys in Git - c#

In my C# project have APIKeys.cs file which have const strings with API keys.
I want those strings to be empty in Git server but have actual API keys in my local computer.
So peoples who pull project can compile it without problem and still my local computer gonna have API keys in same file.
If I try to upload APIKeys.cs file with empty strings then I can't have local file with API keys because when I try to push it, it will overwrite empty APIKeys.cs file. Also I can't ignore this file too because it will remove empty APIKeys.cs file from Git server.
So what is best automated approach for this problem which will allow class file with empty strings in server, so project will be compileable when people pull it and have real class file in local computer?

I figured another solution now which is not perfect but still good enough for me, example:
APIKeys.cs file:
public static partial class APIKeys
{
public static readonly string ImgurClientID = "";
public static readonly string ImgurClientSecret = "";
public static readonly string GoogleClientID = "";
public static readonly string GoogleClientSecret = "";
public static readonly string PastebinKey = "";
...
}
APIKeysLocal.cs file:
public static partial class APIKeys
{
static APIKeys()
{
ImgurClientID = "1234567890";
ImgurClientSecret = "1234567890";
GoogleClientID = "1234567890";
GoogleClientSecret = "1234567890";
PastebinKey = "1234567890";
...
}
}
Ignore APIKeysLocal.cs file in Git and people who don't have this file can still be able to compile project if they remove this file from solution explorer.
I also automatically create empty APIKeysLocal.cs file if it is not already exists using project pre build event:
cd $(ProjectDir)APIKeys\
if not exist APIKeysLocal.cs (
type nul > APIKeysLocal.cs
)
That way user don't need to do anything to be able to compile project.

Accept that you cannot hide unencrypted private keys in a public space.
What you can do is move the keys to a private space, and then reference that private space from code.
Your private space might be environment variables or the Windows registry, it should be something outside the source code of your app.
Another approach is to create a new config file (e.g. keys.config) specifically for storing private keys, and then exclude this file from source control.
This means you don't share your private keys, but it also means that you need to document (perhaps in readme.md) that users will need to recreate their own keys.config. Even better (thanks #Joey) is to include a sample config file (keys.sample.config) in the solution, illustrating what's needed.
Here is an example

You've got two options:
Tell Git to ignore changes to APIKeys.cs on your local machine:
git update-index --skip-worktree APIKeys.cs
This will cause local changes not to get committed. If you ever do want to commit changes to the file, you'll have to undo this with the --no-skip-worktree flag.
Rename the APIKeys.cs file to something like APIKeys.template.cs, containing the blank strings that you want to share. Keep this file in your repository. Copy that file to APIKeys.cs. Add APIKeys.cs to your .gitignore. Add instructions to copy the template file and modify with local settings.
git mv APIKeys.cs APIKeys.template.cs
$EDITOR APIKeys.template.cs
git commit
cat APIKeys.cs >> .gitignore
cp APIKeys.template.cs APIKeys.cs

Isn't this very similar to the problem of injecting a build number into your component? The way I do that is to have a pre-build step that generates a file called AssemblyVersionInfo.cs based on some environment variable. You could do the same thing with your API Keys.
In the pre-build step for the component that compiles in the API keys put something like this:-
if not defined API_KEY set API_KEY=DEFAULT_KEY
echo public class ApiKeys>"$(SolutionDir)src\ApiKeys.cs"
echo {>>"$(SolutionDir)src\ApiKeys.cs"
echo public const string Key="%API_KEY%";>>"$(SolutionDir)src\ApiKeys.cs"
echo }>>"$(SolutionDir)src\ApiKeys.cs"
Then you set either a user or system environment variable on your local machine with the real key in it.
setx API_KEY THE_REAL_KEY
To avoid Git wanting to commit the file just add it to the .gitignore.

Related

Specflow - Create Pre-defined data to be shared between all scenarios in test execution with parallel execution

I am trying to Re-create my BeforeTestRun step to run my setup only once per whole execution not per thread.
I had a look a Custom Deployment steps I have implemented some already but For my setup i need to bring in some values from the app.config file I am trying something like this
my Default.srprofile file contains:
<DeploymentTransformation>
<GlobalSteps>
<Custom type="Test.CustomDeploymentStep, Test"></Custom>
</GlobalSteps>
</DeploymentTransformation>
and my CustomDeploymentStep.cs:
public class CustomDeploymentStep : IDeploymentTransformationStep
{
public static string baseUrl;
public void Apply(IDeploymentContext deploymentContext)
{
baseUrl = ConfigurationManager.AppSettings["URL"];
}
public void Restore(IDeploymentContext deploymentContext)
{
DoSomething();
}
}
My app config contains the following:
<add key="URL" value="http://google.com" />
But That does not work, The ConfigurationManager.AppSettings only returns one key and one value
"key" : "TestProjectRetargetTo35Allowed" "value":"true"
How can I load my configuration from app.config into the Apply() method in CustomDeploymentStep?
Also If there is a better/more efficient way of generating pre-defined data in specflow with thread safe execution, please do let me know
I ran into the same problem once I needed to use custom deployment steps in more than one project in a large solution. This appears to be a bug within the TechTalk.SpecRun.Framework. The error is likely "Error applying global deployment step. Global steps cannot contain test assembly specific settings." and if you look inside the TestAssembly while debugging you will see the TestAssemblyConfigFilePath is null and/or swallowing another exception.
It doesn't register a project specific configuration file. My workaround was to save the config file into debug and access what I need like so:
string appConfigFilePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\App.config";
ExeConfigurationFileMap configMap = new ExeConfigurationFileMap();
configMap.ExeConfigFilename = appConfigFilePath;
var config = ConfigurationManager.OpenMappedExeConfiguration(configMap, ConfigurationUserLevel.None);
var baseUrl = config.AppSettings.Settings["URL"].Value;

How to output a file from a roslyn code analyzer?

I'm using the roslyn API to write a DiagnosticAnalyzer and CodeFix.
After I have collected all strings and string-interpolations, I want to write all of them to a file but I am not sure how to do this the best way.
Of course I can always simply do a File.WriteAllText(...) but I'd like to expose more control to the user.
I'm also not sure about how to best trigger the generation of this file, so my questions are:
I do not want to hard-code the filename, what would be the best way to expose this setting to the user of the code-analyzer? A config file? If so, how would I access that? ie: How do I know the directory?
If one string is missing from the file, I'd like to to suggest a code fix like "Project contains changed or new strings, regenerate string file". Is this the best way to do this? Or is it possible to add a button or something to visual studio?
I'm calling the devenv.com executable from the commandline to trigger builds, is there a way to force my code-fix to run either while building, or before/after? Or would I have to "manually" load the solution with roslyn and execute my codefix?
I've just completed a project on this. There are a few things that you will need to do / know.
You will probably need to switch you're portable class library to a class library. otherwise you will have trouble calling the File.WriteAllText()
You can see how to Convert a portable class library to a regular here
This will potentially not appropriately work for when trying to apply all changes to document/project/solution. When Calling from a document/project/solution, the changes are precalcuated and applied in a preview window. If you cancel, an undo action is triggered to undo all changes, if you write to a file during this time, and do not register an undo action you will not undo the changes to the file.
I've opened a bug with roslyn but you can handle instances by override the preview you can see how to do so here
And one more final thing you may need to know is how to access the Solution from the analyzer which, Currently there is a hack I've written to do so here
As Tamas said you can use additional files you can see how to do so here
You can use additional files, but I know on the version I'm using resource files, are not marked as additional files by default they are embeddedResources.
So, for my users to not have to manually mark the resource as additonalFiles I wrote a function to get out the Designer.cs files associated with resource files from the csproj file using xDoc you can use it as an example if you choose to parse the csproj file:
protected List<string> GetEmbeddedResourceResxDocumentPaths(Project project)
{
XDocument xmldoc = XDocument.Load(project.FilePath);
XNamespace msbuild = "http://schemas.microsoft.com/developer/msbuild/2003";
var resxFiles = new List<string>();
foreach (var resource in xmldoc.Descendants(msbuild + "EmbeddedResource"))
{
string includePath = resource.Attribute("Include").Value;
var includeExtension = Path.GetExtension(includePath);
if (0 == string.Compare(includeExtension, RESX_FILE_EXTENSION, StringComparison.OrdinalIgnoreCase))
{
var outputTag = resource.Elements(msbuild + LAST_GENERATED_TAG).FirstOrDefault();
if (null != outputTag)
{
resxFiles.Add(outputTag.Value);
}
}
}
return resxFiles;
}
For config files you can use the AdditionalFiles msbuild property, which is passed to the analyzers through the context. See here.

C# Puma.Net The library dibapi.dll could not be found

I've got some issues running Puma.Net. I've got all the functions looking fine in the code but when it comes this point:
Value = pumaPage.RecognizeToString();
It then gives an error saying the library dibapi.dll can't be found. But I just can't even add it as a reference it says something like
Can't add reference Make sure the file is accessible and that it is a assembly or Com-Component.
So I gave it all the rights it needs to be read, write & executed. I even gave it full controll on all the users but it just won't work.
Maybe I made a mistake somewhere so here is the full code of the programm.
static void Main()
{
string Image = "V:/Test_images/value.PNG";
Console.WriteLine("Running the Program!");
var pumaPage = new PumaPage(Image);
string Value;
using (pumaPage)
{
pumaPage.FileFormat = PumaFileFormat.RtfAnsi;
pumaPage.EnableSpeller = false;
pumaPage.Language = PumaLanguage.Digits;
Value = pumaPage.RecognizeToString();
}
Console.WriteLine("The Value is" + Value);
Console.ReadLine();
}
I've added the Puma.Net dll and "using Puma.Net;" so it should work. Does someone got any idea what could be wrong?
Here is also the errormessage that appears all the time.
The Error Message which appears
If you need a translation just tell me.
Btw it is a Console Application and I would love to keep it that way. If it is not possible then I can also try to use turn it into a Form Application but that's a whole new part for me so it could take a while to get into it.
You need to copy dibapi.dll to the output folder as described in the documentation:
Steps to add Puma.NET to your project:
1. Add reference to Puma.Net.dll;
2. Make sure that after project building the output folder (i.e. bin\Debug or bin\Release)
contains files Puma.Net.dll and puma.interop.dll. If the last is not present (IDE didn’t
copy it) copy it to the folder manually;
3. Copy dibapi.dll to the output folder;

C# how to call files relative address rather absolute address

I have found myself having to rely on having to use some local folders. No problem normally, but the main issue is that if transfer the project to another machine, even during development the structure reports back an error.
The error is that is page can't be displayed on a local website and an IO error when I go to read from the file.
The directory is as follows
C:\Users\Keith\OneDrive\Documents\Project 2016\Lingerie\Lingerie\Corset\Corset
The folders in question are
Master Audio
Images
User Audio
The actual folder as they're used
private String folderPlay = #"C:\Users\Keith\OneDrive\Documents\Project 2016\Lingerie\Lingerie\Corset\Corset\Master Audio\";
private String folderRecord = #"C:\Users\Keith\OneDrive\Documents\Project 2016\Lingerie\Lingerie\Corset\Corset\User Audio\";
private String folderImages = #"C:\Users\Keith\OneDrive\Documents\Project 2016\Lingerie\Lingerie\Corset\Corset\Images\";
What I would like to do is be able to call those file relative to the code? Also if it's not too much trouble could a small note be attached so I can understand/
Use only the path in your project rather than whole physical path
private String folderPlay = #"~/Master Audio";
private String folderRecord = #"~/User Audio";
private String folderImages = #"~/Images";
and my suggestion is use path like Master_Audio, User_Audio. Don't put space between word
What I found useful in similar situation is to actually inject content root dir to the class that needs to know the location of particular resources:
E.g.
class Foo{
private string readonly _contentRoot;
public Foo (string contentRoot)
{
_contentRoot = contentRoot
}
public void DoSomethingWithImage()
{
string imgRelativePath = "/img/img1.png";
string imgPath = Path.Combine(_contentRoot, imgRelativePath);
// do something with imgPath
}
}
The benefit of this is that you can now write tests for your methods (which might be difficult otherwise). Sometimes unit testing framework copies libs to a temp location, but you still want to run the tests.
You can resolve root directory from several locations, depending how you run the code:
For unit tests, use Assembly.GetExecutingAssembly().GetName().CodeBase
For Web app HttpRuntime or HttpContext
For Windows app AppDomain.BaseDirectory or similar
Rule of thumb:
You should not have any hardcoded absolute paths (pain with testing, running on another machine, deployment)
Instead, use relative paths (usually relative to project root or root/content)

How to get Current Project Directory path using C#

okay, here is the question. I have two projects one is C# Console and other is Class library.
I am accessing/calling Class library method from the console app.
There is a folder called Files within the class library project.
I need to get the path of the Class library's files folder but whenever I use
System.IO.Directory.GetCurrentDirectory();
and
Environment.CurrentDirectory;
it is giving me path of the Console project which I am using to call the method.
Above methods are giving me path like
C:\\ConsolePro\\bin\\Debug
but I need the path of Class library project
C:\\ClassLibPro\\bin\\Debug
Please advise
Once the code is compiled and running, 'Project Path' has no meaning. All you can determine are the file locations of the compiled assemblies. And you can only do what you are asking if your Console project references the built 'class library' DLL directly, rather than via a Project Reference.
Then, you can make use of Reflection to get Assembly paths like;
string path = Assembly.GetAssembly(typeof (SomeClassInOtherProject)).Location;
You should be able to use Directory.GetParent(Directory.GetCurrentDirectory()) a few times to get higher level directories and then add the path of the lib directory to the end of that.
I believe the problem is:
Since the Console project has the DLL file reference it is using DLL to call any methods.
At this time it is returning the class library projct's DLL location which is located in console project's bin directory and it doesn't know about the physical location of class library project.
so essentially it is returning the same project path. I will have to move both projects in same directory in order to solve this issue.
If you loading the class library from another assembly.
string Path = System.Reflection.Assembly.GetAssembly(typeof({LibraryClassName})).Location;
string PathToClassLibPro = Path.GetDirectoryName( Path);
Replace {LibraryClassName} with the class name of your library.
I hope I understand u corretly:
Path.GetDirectoryName(typeof(Foo.MyFooClass).Assembly.Location);
I would recommend one of two options.
If the files are small include them in the class library and stream them to a temp location when needed
Other option is to copy the files during the build to the output directory and use them that way. In cases of multiple shared projects it is best to have a common bin folder that you copy assemblies to and run from that location.
Despite i cant find a good solution i use this trick :
as long as you want to come back to your ideal path u should add Directory.GetParent() instead of ...
Directory.GetParent(...(Directory.GetParent(Directory.GetCurrentDirectory()).ToString()...).ToString()
I use the following approach to get the current project path at runtime:
public static class ProjectInfo {
public static string appDirectory = AppDomain.CurrentDomain.BaseDirectory;
public static string projectPath = appDirectory.Substring(0, appDirectory.IndexOf("\\bin"));
}
I had this exact issue as well where I couldn't access the file in my namespace's bin/debug folder. My solution was to manipulate the string using Split() then construct a new string which is the absolute path to the json file I have in my namespace.
private static string GetFilePath()
{
const char Escape = '\\'; //can't have '\' by itself, it'll throw the "Newline in constant" error
string directory = Environment.CurrentDirectory;
string[] pathOccurences = directory.Split(Escape);
string pathToReturn = pathOccurences[0] + Escape; //prevents index out of bounds in upcoming loop
for(int i = 1; i < pathOccurences.Length; i++)
{
if (pathOccurences[i] != pathOccurences[i - 1]) //the project file name and the namespace file name are the same
pathToReturn += pathOccurences[i] + Escape;
else
pathToReturn += typeof(thisClass).Namespace + Escape; //In the one occurrence of the duplicate substring, I replace it with my class Namespace name
}
return pathToReturn + "yourFile.json";
}
I personally don't like this solution, but it was the only answer I could think of.

Categories