Пример #1
0
        /// <summary>Get latest swagger definitions from cloud.</summary>
        /// <param name="config">Contains config info, including profile to use</param>
        /// <returns>Success or failure</returns>
        public bool GetSwagger(CentrifyCliConfig config)
        {
            try
            {
                Ccli.ConditionalWrite($"Fetching latest swagger definitions from cloud.", config.Silent);

                // Doesn't require auth
                InitializeClient(config.Profile.URL);
                var runTask = PlaceCall("/vfslow/lib/api/swagger.json", "");
                runTask.Wait();
                Tuple <Runner.ResultCode, string> callResult = runTask.Result;

                if (callResult.Item1 == Runner.ResultCode.Success)
                {
                    // Write item2 to swagger.json file.  There's no JSON to process.
                    using (StreamWriter file = File.CreateText(config.SwaggerDirectory))
                    {
                        file.Write(callResult.Item2);
                    }
                    return(true);
                }
                else
                {
                    Ccli.WriteErrorText($"Error fetching swagger definitions from cloud: {callResult}");
                }
            }
            catch (Exception e)
            {
                Ccli.WriteErrorText($"Exception fetching swagger definitions from cloud: {e.Message}");
            }

            return(false);
        }
Пример #2
0
        /// <summary>Centrify-specific OAuth2 Token request</summary>
        /// <param name="config">Contains config info, including profile to use</param>
        /// <param name="timeout">Timeout is milliserconds for REST calls</param>
        /// <returns>Tuple of success and either token or error message.</returns>
        public async Task <Tuple <ResultCode, string> > OAuth2_GenerateTokenRequest(CentrifyCliConfig config, int timeout)
        {
            ServerProfile profile       = config.Profile;
            string        TokenEndpoint = "/oauth2/token/" + profile.OAuthAppId;

            if (profile.Password == null && !config.Silent)
            {
                Console.Write($"Enter password for {profile.UserName}: ");
                profile.Password = ReadMaskedPassword(false);
            }

            string queryParams = $"grant_type=client_credentials&response_type=code&state={RandomString(15)}&scope=ccli&client_id={profile.UserName}&client_secret={profile.Password}";

            try
            {
                Ccli.ConditionalWrite($"Requesting token for {profile.UserName}.", config.Silent);
                InitializeClient(profile.URL);
                Task <HttpResponseMessage> response = m_restUpdater.NewRequestAsync(profile.URL + TokenEndpoint, queryParams);
                if (!response.Wait(timeout))
                {
                    return(new Tuple <ResultCode, string>(ResultCode.Timeout, "Request for token timed out."));
                }

                HttpStatusCode statusCode = response.Result.StatusCode;
                string         contents   = await response.Result.Content.ReadAsStringAsync();

                JObject resultSet = ParseContentsToJObject(contents);

                if (response.Result.StatusCode != HttpStatusCode.OK)
                {
                    return(new Tuple <ResultCode, string>(ResultCode.Failed, $"Token request failed/denied: {statusCode}, {ResultToString(resultSet)}"));
                }

                if (!TryGetJObjectValue(resultSet, "access_token", out string accessToken))
                {
                    throw new ArgumentException($"Token response is missing 'access_token': {ResultToString(resultSet)}", "access_token");
                }
                return(new Tuple <ResultCode, string>(ResultCode.Success, accessToken));
            }
            catch (Exception ex)
            {
                return(new Tuple <ResultCode, string>(ResultCode.Exception, ex.Message));
            }
        }
Пример #3
0
        /// <summary>Loads the swagger.json file from, typically, depot2\Cloud\Lib\Api\swagger.json
        /// Builds API Resource from it.</summary>
        /// <param name="config">Contains config info, including profile to use</param>
        /// <returns>Success or failure</returns>
        public bool LoadSwagger(CentrifyCliConfig config)
        {
            string swaggerPath = config.SwaggerDirectory;

            if (String.IsNullOrEmpty(swaggerPath))
            {
                Ccli.WriteErrorText("No swagger path defined in config.");
                return(false);
            }

            Ccli.ConditionalWrite($"Loading swagger definitions from {swaggerPath}", config.Silent);
            bool exists = File.Exists(swaggerPath);

            if ((!exists) || (File.GetCreationTimeUtc(swaggerPath) < (DateTime.UtcNow - SWAGGER_REFRESH)))
            {
                // Fetch from cloud if no swagger or swagger is 'old'
                if (!GetSwagger(config))
                {
                    if (exists)
                    {
                        Ccli.ConditionalWrite($"Using existing swagger defintiions.", config.Silent);
                    }
                    else
                    {
                        Ccli.WriteErrorText($"No swagger definitions available from cloud.");
                        return(false);
                    }
                }
            }

            JObject swagSet = null;

            try
            {
                using (StreamReader file = File.OpenText(swaggerPath))
                    using (JsonTextReader reader = new JsonTextReader(file))
                    {
                        swagSet = (JObject)JToken.ReadFrom(reader);
                    }
            }
            catch (Exception e)
            {
                Ccli.WriteErrorText($"Error loading swagger definitions from {swaggerPath}: {e.Message}");
                return(false);
            }

            JArray calls = new JArray();

            foreach (JProperty path in swagSet["paths"])
            {
                JProperty restPath   = new JProperty("path", (string)path.Name);
                JProperty group      = new JProperty("group", (string)path.First["post"]["tags"].First);
                string[]  pathBits   = ((string)path.Name).Split(new char[] { '/' });
                JProperty desc       = new JProperty("api", pathBits[pathBits.Length - 1]);
                JProperty reference  = new JProperty("reference", (string)path.First["post"]["summary"]);
                string    parameters = "{";
                int       paramCount = 0;
                JToken    pathParams = path.First["post"]["parameters"].First;
                if (pathParams != null)
                {
                    try
                    {
                        foreach (JProperty prop in pathParams["schema"]["properties"])
                        {
                            if (paramCount++ > 0)
                            {
                                parameters += ",\n";
                            }
                            parameters += "   \"" + (string)prop.Name + "\": \"\"";
                        }
                    }
                    catch
                    {
                        try
                        {
                            foreach (JToken tok in pathParams.First)
                            {
                                if (tok is JProperty prop)
                                {
                                    if (paramCount++ > 0)
                                    {
                                        parameters += ",\n";
                                    }
                                    parameters += "   \"" + (string)prop + "\": \"\"";
                                }
                            }
                        }
                        catch (Exception e)
                        {
                            Ccli.WriteErrorText($"Error parsing swagger properties from {swaggerPath}: {e.Message}");
                            return(false);
                        };
                    }
                }
                if (paramCount > 0)
                {
                    parameters += "\n";
                }
                parameters += "}";
                JProperty sample   = new JProperty("sample", parameters);
                JObject   thisCall = new JObject
                {
                    restPath,    //  path == REST endpoint
                    sample,      //  parameters
                    group,       //  Grouping of calls
                    reference,   //  Reference (not really API, misnamed)
                    desc         //  Name of call
                };
                calls.Add(thisCall);
            }
            m_apiCalls = new JObject();
            JProperty callWrapper = new JProperty("apis", calls);

            m_apiCalls.Add(callWrapper);
            return(true);
        }
Пример #4
0
        /// <summary>Authenticate the user via username/password/etc.<para/>
        /// OAuth token is the preferred method of authenication; this exists for ease of use</summary>
        /// <param name="config">Contains config info, including profile to use</param>
        /// <param name="timeout">Timeout is milliserconds for REST calls</param>
        /// <returns>Tuple of success or failure reason and message or results.</returns>
        public Tuple <ResultCode, string> Authenticate(CentrifyCliConfig config, int timeout)
        {
            ServerProfile profile = config.Profile;

            try
            {
                if (string.IsNullOrWhiteSpace(profile.UserName))
                {
                    return(new Tuple <ResultCode, string>(ResultCode.NotFound, $"No user in config to authenticate."));
                }

                InitializeClient(profile.URL);

                // Do StartAuthentication
                string endpoint = "/Security/StartAuthentication";
                Dictionary <string, string> args = new Dictionary <string, string>()
                {
                    { "User", profile.UserName },
                    { "Version", "1.0" }
                };

                var authTask = PlaceCall(endpoint, JsonConvert.SerializeObject(args));
                if (!authTask.Wait(timeout))
                {
                    return(new Tuple <ResultCode, string>(ResultCode.Timeout, $"Request timed out ({endpoint})."));
                }

                Tuple <Runner.ResultCode, string> callResult = authTask.Result;
                if (callResult.Item1 != ResultCode.Success)
                {
                    return(new Tuple <ResultCode, string>(ResultCode.Failed, MakeFailResult(callResult.Item2, $"Authentication request failed ({endpoint}): {callResult.Item1}")));
                }

                // Remember session and tenant
                JObject resultSet = ParseContentsToJObject(callResult.Item2);
                if (!TryGetJObjectValue(resultSet, "Result", out JObject results))
                {
                    throw new ArgumentException($"Authentication results have no 'result' property ({endpoint}):\n{ResultToString(resultSet)}", "Result");
                }

                if (TryGetJObjectValue(results, "Redirect", out string redirect))
                {
                    Ccli.ConditionalWrite($"Authentication is redirected, use the prefered URL: {redirect}", config.Silent);
                    return(new Tuple <ResultCode, string>(ResultCode.Redirected, redirect));
                }

                if (!TryGetJObjectValue(results, "SessionId", out string sessionId))
                {
                    throw new ArgumentException($"Authentication results are missing 'SessionId' ({endpoint}): {ResultToString(results)}", "SessionId");
                }

                if (!TryGetJObjectValue(results, "TenantId", out string tenantId))
                {
                    throw new ArgumentException($"Authentication results are missing 'TenantId' ({endpoint}): {ResultToString(results)}", "TenantId");
                }

                if (!TryGetJObjectValue(results, "Challenges", out JToken challenges))
                {
                    throw new ArgumentException($"Authentication results are missing 'Challenges' ({endpoint}): {ResultToString(results)}", "Challenges");
                }

                // If pw was supplied, and is one of the first mechs, use what was supplied automagically
                int passwordMechIdx = -1;
                if (profile.Password != null)
                {
                    // Present the option(s) to the user
                    for (int mechIdx = 0; mechIdx < challenges[0]["Mechanisms"].Children().Count() && passwordMechIdx == -1; mechIdx++)
                    {
                        if (challenges[0]["Mechanisms"][mechIdx]["Name"].Value <string>() == "UP")
                        {
                            passwordMechIdx = mechIdx;
                        }
                    }
                }

                // Need to satisfy at least 1 challenge in each collection:
                for (int x = 0; x < challenges.Children().Count(); x++)
                {
                    int optionSelect = -1;

                    // If passwordMechIdx is set, we should auto-fill password first, do it
                    if (passwordMechIdx == -1)
                    {
                        // Present the option(s) to the user
                        for (int mechIdx = 0; mechIdx < challenges[x]["Mechanisms"].Children().Count(); mechIdx++)
                        {
                            Console.WriteLine("Option {0}: {1}", mechIdx, MechToDescription(challenges[x]["Mechanisms"][mechIdx]));
                        }

                        if (challenges[x]["Mechanisms"].Children().Count() == 1)
                        {
                            optionSelect = 0;
                        }
                        else
                        {
                            while (optionSelect < 0 || optionSelect > challenges[x]["Mechanisms"].Children().Count())
                            {
                                Console.Write("Select option and press enter: ");
                                string optEntered = Console.ReadLine();
                                int.TryParse(optEntered, out optionSelect);
                            }
                        }
                    }
                    else
                    {
                        optionSelect    = passwordMechIdx;
                        passwordMechIdx = -1;
                    }

                    try
                    {
                        var newChallenges = AdvanceForMech(timeout, tenantId, sessionId, challenges[x]["Mechanisms"][optionSelect], profile);
                        if (newChallenges != null)
                        {
                            challenges = newChallenges;
                            x          = -1;
                        }
                    }
                    catch (Exception ex)
                    {
                        return(new Tuple <ResultCode, string>(ResultCode.Failed, ex.Message));
                    }
                }
            }
            catch (Exception e)
            {
                Exception i           = e;
                string    allMessages = "";
                // De-dup; sometimes they double up.
                while (i != null)
                {
                    if (!allMessages.Contains(i.Message))
                    {
                        allMessages += i.Message + "  " + Environment.NewLine;
                    }
                    i = i.InnerException;
                }
                return(new Tuple <ResultCode, string>(ResultCode.Exception, allMessages));
            }

            m_url = profile.URL;
            return(new Tuple <ResultCode, string>(ResultCode.Success, ""));
        }